From 0cd4f8f361a10593475ec45d22697738fbf0063e Mon Sep 17 00:00:00 2001 From: ali_mohaghegh Date: Thu, 3 Sep 2026 15:01:07 +0330 Subject: [PATCH] feat(ui): AppSwitcherMenu parity + hub-only warehouse headers + calendar badge removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - shared AppSwitcherMenu ('پنل‌ها' title, active filled item with ✓ فعلی, «مدیریت انبار»/«مدیریت تدارکات» switcher, divider + خروج از حساب) used by both the procurement dashboard and the warehouse hub (PM variant keeps logout-only); replaced the old raw «بازگشت به دفتر» link - warehouse header mirrors the procurement pattern: icon + hamburger only on the hub dashboard; sub-views (تغییر موجودی، کنترل روزانه، انبارها، کالاهای در راه، سازمان‌دهی) render a minimal header with just the title and «بازگشت به داشبورد» - removed the self-referencing «داشبورد انبار» tab from the hub strip - removed the red per-day transaction-count badges from the daily-control calendar (backend untouched — purely visual) - removed the test subtitle «داده‌ها در پایگاه داده ذخیره می‌شوند» from the procurement dashboard header --- frontend/src/components/AppSwitcherMenu.tsx | 29 +++++++++++++++++++ frontend/src/pages/Admin/AdminPanel.tsx | 12 ++++---- .../src/pages/Dashboard/DashboardPage.tsx | 17 ++--------- .../src/pages/Inventory/DailyControlPage.tsx | 14 ++------- .../src/pages/Inventory/InventoryPage.tsx | 14 +++------ .../src/pages/Inventory/InventoryTopNav.tsx | 14 ++++----- .../src/pages/Inventory/WarehousesPage.tsx | 14 +++------ 7 files changed, 53 insertions(+), 61 deletions(-) create mode 100644 frontend/src/components/AppSwitcherMenu.tsx diff --git a/frontend/src/components/AppSwitcherMenu.tsx b/frontend/src/components/AppSwitcherMenu.tsx new file mode 100644 index 0000000..395935c --- /dev/null +++ b/frontend/src/components/AppSwitcherMenu.tsx @@ -0,0 +1,29 @@ +import { PanelMenu } from "./PanelMenu"; +import { useLogout } from "../pages/Inventory/panelMenu"; + +// منوی همبرگری مشترک بین دو ماژول (پنل انبار / دفتر تدارکات) — +// دقیقاً مثل منوی دفتر: عنوان «پنل‌ها»، ماژول فعلی با استایل فعال (پر) +// + «✓ فعلی»، ماژول دیگر به‌عنوان سوییچر، و در پایان جداکننده + خروج. +export function AppSwitcherMenu({ + activeModule, + canSwitchToProcurement = true, +}: { + activeModule: "warehouse" | "procurement"; + canSwitchToProcurement?: boolean; // مدیر پروژه به دفتر تدارکات دسترسی ندارد +}) { + const logout = useLogout(); + const items = []; + + if (activeModule === "procurement") { + items.push({ label: "مدیریت انبار", to: "/inventory" }); + items.push({ label: "مدیریت تدارکات", to: "/", active: true }); + } else { + items.push({ label: "مدیریت انبار", to: "/inventory", active: true }); + if (canSwitchToProcurement) { + items.push({ label: "مدیریت تدارکات", to: "/" }); + } + } + items.push({ divider: true }); + items.push({ label: "خروج از حساب", onClick: logout }); + return ; +} diff --git a/frontend/src/pages/Admin/AdminPanel.tsx b/frontend/src/pages/Admin/AdminPanel.tsx index 68158dd..ff37a2d 100644 --- a/frontend/src/pages/Admin/AdminPanel.tsx +++ b/frontend/src/pages/Admin/AdminPanel.tsx @@ -5,9 +5,10 @@ import { toast } from "sonner"; import { Header, PageLayout } from "../../components/layout/Header"; import { EmptyState } from "../../components/EmptyState"; import { useAuthStore } from "../../store/auth"; -import { PanelMenu } from "../../components/PanelMenu"; -import { accountMenuItems, inventorySubheader, useLogout } from "../Inventory/panelMenu"; +import { AppSwitcherMenu } from "../../components/AppSwitcherMenu"; +import { inventorySubheader } from "../Inventory/panelMenu"; import { apiV26, type UserRow } from "../../lib/api"; +import { refreshProcurementStore } from "../../hooks"; import { faNum } from "../../lib/formatters"; const ROLE_LABELS: Record = { @@ -21,7 +22,6 @@ const ROLE_LABELS: Record = { // (مدیریت پروژه‌ها در «سازمان‌دهی» توسط مدیر تدارکات انجام می‌شود). export function AdminPanel() { const navigate = useNavigate(); - const logout = useLogout(); const user = useAuthStore((s) => s.user); const [users, setUsers] = useState([]); @@ -53,6 +53,7 @@ export function AdminPanel() { toast.success( created > 0 ? `همگام‌سازی انجام شد — ${faNum(created)} کاربر جدید از Casdoor اضافه شد ✓` : "همگام‌سازی انجام شد — کاربر جدیدی یافت نشد", ); + refreshProcurementStore(); } catch (err) { toast.error(err instanceof Error ? err.message : "خطا در همگام‌سازی با Casdoor"); } finally { @@ -71,6 +72,7 @@ export function AdminPanel() { : `نقش مدیر تدارکات از «${u.first_name} ${u.last_name}» گرفته شد`, ); await load(); + refreshProcurementStore(); } catch (err) { toast.error(err instanceof Error ? err.message : "خطا در تغییر نقش"); } finally { @@ -81,7 +83,7 @@ export function AdminPanel() { if (!isAdmin) { return (
-
} /> +
@@ -97,7 +99,7 @@ export function AdminPanel() { backTo="/inventory" backLabel="بازگشت به داشبورد انبار" icon={} - menu={} + menu={} actions={ ); })} diff --git a/frontend/src/pages/Inventory/InventoryPage.tsx b/frontend/src/pages/Inventory/InventoryPage.tsx index 6304ff2..5b62036 100644 --- a/frontend/src/pages/Inventory/InventoryPage.tsx +++ b/frontend/src/pages/Inventory/InventoryPage.tsx @@ -5,8 +5,8 @@ import { Header, PageLayout } from "../../components/layout/Header"; import { EmptyState } from "../../components/EmptyState"; import { Badge } from "../../components/ui/badge"; import { useAuthStore } from "../../store/auth"; -import { PanelMenu } from "../../components/PanelMenu"; -import { accountMenuItems, inventorySubheader, panelMenuItems, pmMenuItems, useLogout } from "./panelMenu"; +import { AppSwitcherMenu } from "../../components/AppSwitcherMenu"; +import { inventorySubheader } from "./panelMenu"; import { InventoryTopNav } from "./InventoryTopNav"; import { useInventoryData, brandName, catName, materialName, unitName, whName } from "./useInventoryData"; import type { InventoryRow, InventoryTransaction, TransactionType } from "../../lib/types"; @@ -50,7 +50,6 @@ function specsHash(specs: Record | null | undefined): string { // داشبورد انبار — پورت کامل نسخه‌ی ارسالی (گروه‌بندی + خط زمانی + فیلترها) export function InventoryPage() { const location = useLocation(); - const logout = useLogout(); const user = useAuthStore((s) => s.user); const { warehouses, inventory, transactions, lookup, loading } = useInventoryData(); const isAdmin = user?.role === "admin" || user?.role === "procurement_manager"; // مدیر تدارکات = ادمین دفتر @@ -127,7 +126,7 @@ export function InventoryPage() { if (!user || (!isAdmin && !isPM)) { return (
-
} /> +
@@ -141,12 +140,7 @@ export function InventoryPage() { title="سامانه مدیریت انبار" sub={inventorySubheader(user)} icon={} - menu={ - - } + menu={} actions={ <> diff --git a/frontend/src/pages/Inventory/InventoryTopNav.tsx b/frontend/src/pages/Inventory/InventoryTopNav.tsx index 82252f8..297d459 100644 --- a/frontend/src/pages/Inventory/InventoryTopNav.tsx +++ b/frontend/src/pages/Inventory/InventoryTopNav.tsx @@ -2,14 +2,12 @@ import { Link } from "react-router-dom"; import { cn } from "../../lib/utils"; // ناوبری بالای صفحه در پنل انبار — فقط روی «داشبورد انبار» (هاب) دیده می‌شود؛ -// زیرصفحه‌ها تب ندارند و فقط با «بازگشت به داشبورد» به هاب برمی‌گردند. +// خودِ «داشبورد انبار» دکمه ندارد (ریشه/هاب است) و زیرصفحه‌ها تب ندارند و +// فقط با «بازگشت به داشبورد» به هاب برمی‌گردند. // -// - variant="pm" (مدیر پروژه): همه بخش‌ها — بدون «تغییر موجودی» (ورود آن از -// دکمه‌ی «تغییر موجودی» بالای هاب است)، بدون «دفتر تدارکات» (مدیر پروژه -// به سیستم تدارکات دسترسی ندارد) و بدون دکمه‌ی خروج تکی (خروج از حساب -// فقط در منوی همبرگری است). -// - variant="admin" (مدیر تدارکات/ادمین): فقط بخش‌های مدیر تدارکات — -// «بازگشت به دفتر» و «خروج از حساب» در منوی همبرگری می‌مانند. +// - variant="pm" (مدیر پروژه): کنترل روزانه، انبارها، کالاهای در راه — +// ورود به «تغییر موجودی» از دکمه‌ی بالای هاب است. +// - variant="admin" (مدیر تدارکات/ادمین): کنترل روزانه، کالاهای در راه، سازمان‌دهی. export function InventoryTopNav({ pathname, variant = "pm", @@ -20,13 +18,11 @@ export function InventoryTopNav({ const items = variant === "admin" ? [ - { label: "داشبورد انبار", to: "/inventory", active: pathname === "/inventory" }, { label: "کنترل روزانه", to: "/inventory/daily", active: pathname.startsWith("/inventory/daily") }, { label: "کالاهای در راه", to: "/inventory/arrivals", active: pathname.startsWith("/inventory/arrivals") }, { label: "سازمان‌دهی", to: "/inventory/org", active: pathname.startsWith("/inventory/org") }, ] : [ - { label: "داشبورد انبار", to: "/inventory", active: pathname === "/inventory" }, { label: "کنترل روزانه", to: "/inventory/daily", active: pathname.startsWith("/inventory/daily") }, { label: "انبارها", to: "/inventory/warehouses", active: pathname.startsWith("/inventory/warehouses") }, { label: "کالاهای در راه", to: "/inventory/arrivals", active: pathname.startsWith("/inventory/arrivals") }, diff --git a/frontend/src/pages/Inventory/WarehousesPage.tsx b/frontend/src/pages/Inventory/WarehousesPage.tsx index 7bada64..aa83e1b 100644 --- a/frontend/src/pages/Inventory/WarehousesPage.tsx +++ b/frontend/src/pages/Inventory/WarehousesPage.tsx @@ -1,11 +1,9 @@ import { useState } from "react"; -import { useLocation, useNavigate } from "react-router-dom"; -import { Warehouse as WarehouseIcon } from "lucide-react"; +import { useNavigate } from "react-router-dom"; import { toast } from "sonner"; import { Header, PageLayout } from "../../components/layout/Header"; import { useAuthStore } from "../../store/auth"; -import { PanelMenu } from "../../components/PanelMenu"; -import { inventorySubheader, panelMenuItems, pmMenuItems, useLogout } from "./panelMenu"; +import { inventorySubheader } from "./panelMenu"; import { apiV26 } from "../../lib/api"; import { useInventoryData, projName } from "./useInventoryData"; import { faNum } from "../../lib/formatters"; @@ -13,9 +11,7 @@ import { isoToJalaliStr } from "../../lib/jalali"; // انبارها — مدیر پروژه: مشاهده انبارهای پروژه خودش + افزودن/ویرایش/حذف (ادمین دسترسی ندارد) export function WarehousesPage() { - const location = useLocation(); const navigate = useNavigate(); - const logout = useLogout(); const user = useAuthStore((s) => s.user); const data = useInventoryData(); const isAdmin = user?.role === "admin" || user?.role === "procurement_manager"; @@ -31,7 +27,7 @@ export function WarehousesPage() { if (isAdmin) { return (
-
} /> +
🔒
@@ -47,7 +43,7 @@ export function WarehousesPage() { if (!isPM) { return (
-
} /> +
این بخش فقط برای مدیر پروژه است.
@@ -101,8 +97,6 @@ export function WarehousesPage() { sub={inventorySubheader(user)} backTo="/inventory" backLabel="بازگشت به داشبورد" - icon={} - menu={} /> {isPM && (