diff --git a/frontend/src/pages/Inventory/InventoryPage.tsx b/frontend/src/pages/Inventory/InventoryPage.tsx index 6f8e21a..a2eb7f8 100644 --- a/frontend/src/pages/Inventory/InventoryPage.tsx +++ b/frontend/src/pages/Inventory/InventoryPage.tsx @@ -1,9 +1,8 @@ import { useMemo, useState } from "react"; import { Link, useLocation, useSearchParams } from "react-router-dom"; -import { Plus, Warehouse as WarehouseIcon, SlidersHorizontal, ArrowUpDown, X } from "lucide-react"; +import { Plus, Warehouse as WarehouseIcon, SlidersHorizontal, ArrowUpDown, X, ChevronDown } from "lucide-react"; import { Header, PageLayout } from "../../components/layout/Header"; import { EmptyState } from "../../components/EmptyState"; -import { Badge } from "../../components/ui/badge"; import { useAuthStore } from "../../store/auth"; import { AppSwitcherMenu } from "../../components/AppSwitcherMenu"; import { inventorySubheader } from "./panelMenu"; @@ -14,7 +13,7 @@ import { faNum } from "../../lib/formatters"; import { isoToJalaliStr } from "../../lib/jalali"; import { cn } from "../../lib/utils"; -// رنگ‌بندی معنایی: سبز زمردی = ورود/افزایش، رز = خروج/کاهش، آبی = خنثی/جابه‌جایی +// رنگ‌بندی معنایی تراکنش‌ها const TX_META: Record = { PURCHASE: { label: "خرید", color: "#10b981", icon: "🛒" }, WITHDRAWAL: { label: "برداشت", color: "#e11d48", icon: "➖" }, @@ -148,24 +147,23 @@ export function InventoryPage() { } return ( -
+
} menu={} actions={ - /* تب‌های ناوبری فقط در دسکتاپ — دکمه بالایی به طور کامل حذف شد */
} /> - - {/* نوار فیلتر و جستجوی واکنش‌گرا */} -
-
-
+ + {/* جعبه فیلترها و جستجو */} +
+
+
setShowMobileFilters((s) => !s)} className={cn( - "inline-flex h-9 items-center gap-1.5 rounded-lg border px-2.5 text-xs font-medium transition-colors md:hidden", - hasFilter ? "border-accent bg-accent/10 text-accent font-bold" : "border-line bg-panel text-ink hover:bg-bg" + "inline-flex shrink-0 h-9 items-center gap-1.5 rounded-lg border px-2.5 text-xs font-medium transition-colors", + showMobileFilters || hasFilter + ? "border-accent bg-accent/10 text-accent font-bold" + : "border-line bg-panel text-ink hover:bg-bg" )} > @@ -199,7 +199,7 @@ export function InventoryPage() { - - - {faNum(groups.length)} قلم -
- {/* گزینه‌های فیلتر پیشرفته */} -
-
- - -
- - {isAdmin && ( -
- - -
- )} - -
- - -
- - {hasFilter && ( + {/* فیلتر سریع انبارها (چیپ‌های افقی لمسی) */} + {visibleWarehouses.length > 1 && ( +
- )} - -
- نمایش {faNum(groups.length)} قلم کالا + {visibleWarehouses.map((w) => ( + + ))}
-
+ )} + + {/* فیلترهای پیشرفته — برچسب‌های لمسی بدون سلکت و بدون بیرون‌زدگی */} + {showMobileFilters && ( +
+ {/* دسته‌بندی‌ها */} +
+
دسته‌بندی متریال:
+
+ + {lookup.categories.map((c) => ( + + ))} +
+
+ + {/* پروژه‌ها (مخصوص ادمین) */} + {isAdmin && lookup.projects.length > 0 && ( +
+
پروژه:
+
+ + {lookup.projects.map((p) => ( + + ))} +
+
+ )} + + {/* دکمه پاک کردن فیلترها */} + {hasFilter && ( +
+ + + نمایش {faNum(groups.length)} قلم کالا + +
+ )} +
+ )}
- {/* جدول موجودی انبار با اسکرول افقی محافظت‌شده */} -
+ {/* ۱. نمای مخصوص موبایل: کارت‌های عمودی کاملاً ریسپانسیو */} +
+ {!groups.length ? ( +
+ +
+ ) : ( + groups.map((g) => { + const isExp = expandedKey === g.key; + const matCat = lookup.materials.find((m) => m.id === g.matId)?.category; + return ( + p.id === warehouses.find((w) => w.id === g.whId)?.project_id)?.name || ""} + unit={unitName(lookup.units, g.unitId)} + onToggle={() => { + setExpandedKey(isExp ? null : g.key); + setSelTx(null); + setExpBrand(null); + }} + timeline={ + isExp ? ( + brandName(lookup.brands, id)} + whNameFn={(id) => whName(warehouses, id)} + /> + ) : null + } + /> + ); + }) + )} +
+ + {/* ۲. نمای دسکتاپ: جدول سنتی */} +
{!groups.length ? ( ) : ( @@ -349,14 +452,9 @@ export function InventoryPage() { )}
- - {/* متن راهنما فقط در دسکتاپ نمایش داده می‌شود */} -
- ۱۰ نوع تراکنش استاندارد • سبز زمردی = ورود به انبار · رز = خروج/کاهش · آبی = انتقال و شمارش • کلیک روی هر سطر: خط زمانی با «مانده» لحظه‌به‌لحظه و تفکیک برندها -
- {/* دکمه شناور تغییر موجودی (FAB دایره‌ای آبی با علامت جمع) */} + {/* دکمه شناور تغییر موجودی */} {isPM && ( void; + timeline: React.ReactNode; +}) { + const specsText = Object.entries(g.specs || {}).map(([k, v]) => `${k}: ${v}`).join(" · "); + + return ( +
+
+
+
+

+ {matName} +

+ {matCatName !== "—" && ( + + {matCatName} + + )} +
+
+ +
+
+ + {/* کادر شاخص موجودی و انبار */} +
+
+ موجودی انبار +
+ + {faNum(g.qty)} + + {unit} +
+
+ +
+ محل نگهداری +
+ + {wh} +
+ {isAdmin && projName && ( + {projName} + )} +
+
+ + {/* مشخصات فنی و تاریخ */} +
+
+ {specsText ? ( + + {specsText} + + ) : ( + — + )} +
+ + {isoToJalaliStr(g.updatedAt)} + +
+
+ + {isExp && ( +
+ {timeline} +
+ )} +
+ ); +} + function GroupRow({ g, isExp, isAdmin, matName, matCatName, wh, projName, unit, onToggle, timeline }: { g: Group; isExp: boolean;