Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e8a2368ee | ||
|
|
17c7ae63c8 |
@@ -13,6 +13,7 @@ export function StatsBar({ total, offered, accepted, avgLeadTime }: StatsBarProp
|
||||
label: string;
|
||||
shortLabel: string;
|
||||
value: string;
|
||||
unit?: string;
|
||||
color: string;
|
||||
icon: LucideIcon;
|
||||
}[] = [
|
||||
@@ -38,9 +39,10 @@ export function StatsBar({ total, offered, accepted, avgLeadTime }: StatsBarProp
|
||||
icon: CheckCircle2,
|
||||
},
|
||||
{
|
||||
label: "میانگین lead time (روز)",
|
||||
shortLabel: "زمان تحویل",
|
||||
label: "میانگین زمان تحویل",
|
||||
shortLabel: "میانگین تحویل",
|
||||
value: avgLeadTime == null ? "—" : fmtNum(avgLeadTime),
|
||||
unit: avgLeadTime != null ? "روز" : undefined,
|
||||
color: "text-green",
|
||||
icon: Timer,
|
||||
},
|
||||
@@ -60,17 +62,22 @@ export function StatsBar({ total, offered, accepted, avgLeadTime }: StatsBarProp
|
||||
<span className="hidden sm:inline">{s.label}</span>
|
||||
</span>
|
||||
|
||||
{/* آیکون در موبایل حذف شده تا فضا برای متن باز بماند */}
|
||||
{/* آیکون در دسکتاپ */}
|
||||
<span className="hidden h-6 w-6 shrink-0 items-center justify-center rounded-md bg-slate-bg text-muted sm:flex">
|
||||
<s.icon className="h-3.5 w-3.5" strokeWidth={2} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* مقدار عددی */}
|
||||
{/* مقدار عددی به همراه واحد روز */}
|
||||
<div
|
||||
className={`mt-0.5 text-base font-extrabold leading-5 tabular-nums sm:mt-1 sm:text-[22px] sm:leading-8 ${s.color}`}
|
||||
className={`mt-0.5 flex items-baseline justify-center sm:justify-start gap-1 text-base font-extrabold leading-5 tabular-nums sm:mt-1 sm:text-[22px] sm:leading-8 ${s.color}`}
|
||||
>
|
||||
{s.value}
|
||||
<span>{s.value}</span>
|
||||
{s.unit && (
|
||||
<span className="text-[10px] font-medium text-muted sm:text-xs">
|
||||
{s.unit}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -48,7 +48,7 @@ const ADMIN_INV_TABS: TabDef[] = [
|
||||
function TabBar({ tabs }: { tabs: TabDef[] }) {
|
||||
return (
|
||||
<nav
|
||||
className="fixed inset-x-0 bottom-0 z-40 border-t border-line bg-panel md:hidden"
|
||||
className="fixed inset-x-0 bottom-0 z-40 border-t border-line bg-panel/95 backdrop-blur-md md:hidden"
|
||||
style={{ paddingBottom: "env(safe-area-inset-bottom)" }}
|
||||
>
|
||||
<div className="grid grid-cols-4">
|
||||
@@ -59,13 +59,13 @@ function TabBar({ tabs }: { tabs: TabDef[] }) {
|
||||
end={t.end}
|
||||
className={({ isActive }) =>
|
||||
cn(
|
||||
"flex min-h-[56px] flex-col items-center justify-center gap-0.5 whitespace-nowrap px-1 text-[10.5px] font-medium transition-colors",
|
||||
isActive ? "text-accent" : "text-muted",
|
||||
"flex min-h-[66px] flex-col items-center justify-center gap-1.5 whitespace-nowrap px-1 py-1 transition-all active:scale-95",
|
||||
isActive ? "text-accent font-bold" : "text-muted hover:text-ink font-medium",
|
||||
)
|
||||
}
|
||||
>
|
||||
<t.icon className="h-5 w-5" strokeWidth={1.8} />
|
||||
<span>{t.label}</span>
|
||||
<t.icon className="h-6.5 w-6.5" strokeWidth={2.2} />
|
||||
<span className="text-[12px] leading-tight tracking-tight">{t.label}</span>
|
||||
</NavLink>
|
||||
))}
|
||||
</div>
|
||||
@@ -97,4 +97,4 @@ export function MobileTabBar() {
|
||||
pathname.startsWith("/suppliers");
|
||||
if (onOfficePage && isOffice) return <TabBar tabs={PROC_TABS} />;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import { Plus, Warehouse as WarehouseIcon, SlidersHorizontal, ArrowUpDown, X } f
|
||||
import { Header, PageLayout } from "../../components/layout/Header";
|
||||
import { EmptyState } from "../../components/EmptyState";
|
||||
import { Badge } from "../../components/ui/badge";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { useAuthStore } from "../../store/auth";
|
||||
import { AppSwitcherMenu } from "../../components/AppSwitcherMenu";
|
||||
import { inventorySubheader } from "./panelMenu";
|
||||
@@ -149,26 +148,16 @@ export function InventoryPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen pb-20 md:pb-6">
|
||||
<div className="min-h-screen pb-24 md:pb-6">
|
||||
<Header
|
||||
title="سامانه مدیریت انبار"
|
||||
sub={inventorySubheader(user)}
|
||||
icon={<WarehouseIcon className="h-4.5 w-4.5" strokeWidth={2} />}
|
||||
menu={<AppSwitcherMenu activeModule="warehouse" canSwitchToProcurement={isAdmin} />}
|
||||
actions={
|
||||
<div className="flex items-center gap-2">
|
||||
{/* تبهای تکراری در موبایل مخفی و فقط در دسکتاپ نمایش داده میشوند */}
|
||||
<div className="hidden md:flex items-center gap-2">
|
||||
<InventoryTopNav pathname={location.pathname} variant={isAdmin ? "admin" : "pm"} />
|
||||
</div>
|
||||
{isPM && (
|
||||
<Button asChild variant="primary" className="h-8 px-2.5 text-xs">
|
||||
<Link to="/inventory/change" className="no-underline">
|
||||
<Plus className="h-3.5 w-3.5" />
|
||||
<span>تغییر موجودی</span>
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
/* تبهای ناوبری فقط در دسکتاپ — دکمه بالایی به طور کامل حذف شد */
|
||||
<div className="hidden md:flex items-center gap-2">
|
||||
<InventoryTopNav pathname={location.pathname} variant={isAdmin ? "admin" : "pm"} />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
@@ -227,7 +216,7 @@ export function InventoryPage() {
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
{/* گزینههای فیلتر پیشرفته: در موبایل با دکمه باز میشود و در دسکتاپ همیشه باز است */}
|
||||
{/* گزینههای فیلتر پیشرفته */}
|
||||
<div
|
||||
className={cn(
|
||||
"mt-3 grid grid-cols-1 gap-2.5 border-t border-line/60 pt-3 sm:grid-cols-2 md:flex md:flex-wrap md:items-end",
|
||||
@@ -366,6 +355,18 @@ export function InventoryPage() {
|
||||
۱۰ نوع تراکنش استاندارد • سبز زمردی = ورود به انبار · رز = خروج/کاهش · آبی = انتقال و شمارش • کلیک روی هر سطر: خط زمانی با «مانده» لحظهبهلحظه و تفکیک برندها
|
||||
</div>
|
||||
</PageLayout>
|
||||
|
||||
{/* دکمه شناور تغییر موجودی (FAB دایرهای آبی با علامت جمع) */}
|
||||
{isPM && (
|
||||
<Link
|
||||
to="/inventory/change"
|
||||
className="fixed bottom-20 left-5 z-40 flex h-14 w-14 items-center justify-center rounded-full bg-blue-600 text-white shadow-xl shadow-blue-600/40 transition-all duration-200 hover:bg-blue-700 hover:scale-105 active:scale-90 md:bottom-8 md:left-8"
|
||||
title="تغییر موجودی"
|
||||
aria-label="تغییر موجودی"
|
||||
>
|
||||
<Plus className="h-7 w-7" strokeWidth={2.5} />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,18 +55,22 @@ export function Step5Review({
|
||||
تاریخ، یادداشت و بازبینی نهایی
|
||||
</h3>
|
||||
|
||||
<div className="grid max-w-2xl grid-cols-2 gap-3 max-md:grid-cols-1">
|
||||
<div className="field">
|
||||
<div className="grid max-w-2xl grid-cols-12 gap-3">
|
||||
{/* تاریخ پیشنهاد: فضای بزرگتر (۷ ستون از ۱۲) به همراه ساعت */}
|
||||
<div className="field col-span-7 [&>div]:w-full [&_input]:w-full">
|
||||
<label>تاریخ پیشنهاد</label>
|
||||
<JalaliDatePicker value={quoteDate} onChange={onQuoteDate} placeholder="۱۴۰۳/۰۵/۱۲" withTime />
|
||||
<div className="hint">پیشفرض امروز</div>
|
||||
</div>
|
||||
<div className="field">
|
||||
|
||||
{/* تاریخ انقضای پیشنهاد: فضای کوچکتر (۵ ستون از ۱۲) */}
|
||||
<div className="field col-span-5 [&>div]:w-full [&_input]:w-full">
|
||||
<label>تاریخ انقضای پیشنهاد</label>
|
||||
<JalaliDatePicker value={expiryDate} onChange={onExpiryDate} placeholder="۱۴۰۳/۰۶/۱۲" />
|
||||
<div className="hint">تا چه زمانی این قیمت معتبر است؟</div>
|
||||
</div>
|
||||
<div className="field col-span-2">
|
||||
|
||||
<div className="field col-span-12">
|
||||
<label>یادداشت (اختیاری)</label>
|
||||
<textarea
|
||||
rows={2}
|
||||
@@ -76,8 +80,9 @@ export function Step5Review({
|
||||
onChange={(e) => onNotes(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="field col-span-2">
|
||||
<label className="mb-1.5 block text-xs font-medium text-gray-700">پیشفاکتور (اختیاری)</label>
|
||||
|
||||
<div className="field col-span-12">
|
||||
<label>پیشفاکتور (اختیاری)</label>
|
||||
{proforma ? (
|
||||
/* وضعیت فایل انتخابشده با دکمه حذف تمیز */
|
||||
<div className="flex items-center justify-between rounded-xl border border-gray-200 bg-gray-50/70 px-3 py-2 text-xs">
|
||||
|
||||
Reference in New Issue
Block a user