feat(ui): AppSwitcherMenu parity + hub-only warehouse headers + calendar badge removal
- 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
This commit is contained in:
@@ -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 <PanelMenu title="پنلها" items={items} />;
|
||||
}
|
||||
@@ -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<string, string> = {
|
||||
@@ -21,7 +22,6 @@ const ROLE_LABELS: Record<string, string> = {
|
||||
// (مدیریت پروژهها در «سازماندهی» توسط مدیر تدارکات انجام میشود).
|
||||
export function AdminPanel() {
|
||||
const navigate = useNavigate();
|
||||
const logout = useLogout();
|
||||
const user = useAuthStore((s) => s.user);
|
||||
|
||||
const [users, setUsers] = useState<UserRow[]>([]);
|
||||
@@ -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 (
|
||||
<div className="min-h-screen">
|
||||
<Header title="پنل مدیریت ادمین" menu={<PanelMenu title="پنل مدیریت انبار" items={accountMenuItems(logout)} />} />
|
||||
<Header title="پنل مدیریت ادمین" backTo="/inventory" backLabel="بازگشت به داشبورد انبار" />
|
||||
<PageLayout>
|
||||
<EmptyState emoji="🔒" text="این پنل فقط برای ادمین (سوپر یوزر) است." />
|
||||
</PageLayout>
|
||||
@@ -97,7 +99,7 @@ export function AdminPanel() {
|
||||
backTo="/inventory"
|
||||
backLabel="بازگشت به داشبورد انبار"
|
||||
icon={<ShieldCheck className="h-4.5 w-4.5" strokeWidth={2} />}
|
||||
menu={<PanelMenu title="پنل مدیریت انبار" items={accountMenuItems(logout)} />}
|
||||
menu={<AppSwitcherMenu activeModule="warehouse" />}
|
||||
actions={
|
||||
<button
|
||||
onClick={() => void sync()}
|
||||
|
||||
@@ -16,8 +16,7 @@ import { Header, PageLayout } from "../../components/layout/Header";
|
||||
import { StatsBar } from "../../components/StatsBar";
|
||||
import { FilterToolbar, type Filters } from "./FilterToolbar";
|
||||
import { LedgerTable } from "./LedgerTable";
|
||||
import { PanelMenu } from "../../components/PanelMenu";
|
||||
import { useLogout } from "../Inventory/panelMenu";
|
||||
import { AppSwitcherMenu } from "../../components/AppSwitcherMenu";
|
||||
import { AcceptModal } from "./AcceptModal";
|
||||
import { EditModal } from "./EditModal";
|
||||
|
||||
@@ -26,7 +25,6 @@ const EMPTY_FILTERS: Filters = { category: "", supplier: "", status: "", search:
|
||||
// صفحهی اصلی دفتر پیشنهادهای خرید
|
||||
export function DashboardPage() {
|
||||
useStoreData();
|
||||
const logout = useLogout();
|
||||
|
||||
const quotes = useQuotes();
|
||||
const suppliers = useSuppliers();
|
||||
@@ -78,19 +76,8 @@ export function DashboardPage() {
|
||||
<div className="min-h-screen">
|
||||
<Header
|
||||
title="دفتر پیشنهادهای خرید"
|
||||
sub="دادهها در پایگاه داده ذخیره میشوند"
|
||||
brand
|
||||
menu={
|
||||
<PanelMenu
|
||||
title="پنلها"
|
||||
items={[
|
||||
{ label: "مدیریت انبار", to: "/inventory" },
|
||||
{ label: "مدیریت تدارکات", to: "/", active: true },
|
||||
{ divider: true },
|
||||
{ label: "خروج از حساب", onClick: logout },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
menu={<AppSwitcherMenu activeModule="procurement" />}
|
||||
actions={
|
||||
<>
|
||||
<Link to="/suppliers" className="btn-line-file no-underline">
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Warehouse as WarehouseIcon } from "lucide-react";
|
||||
import { Header, PageLayout } from "../../components/layout/Header";
|
||||
import { useAuthStore } from "../../store/auth";
|
||||
import { PanelMenu } from "../../components/PanelMenu";
|
||||
import { accountMenuItems, inventorySubheader, pmMenuItems, useLogout } from "./panelMenu";
|
||||
import { inventorySubheader } from "./panelMenu";
|
||||
import { useInventoryData, unitName, whName } from "./useInventoryData";
|
||||
import { faNum } from "../../lib/formatters";
|
||||
import { gregorianToJalali, jalaliToGregorian, MONTHS, toFa } from "../../lib/jalali";
|
||||
@@ -20,8 +18,7 @@ function todayKey(): string {
|
||||
|
||||
// کنترل روزانه — پورت کامل تقویم شمسی نسخهی ارسالی
|
||||
export function DailyControlPage() {
|
||||
const logout = useLogout();
|
||||
const user = useAuthStore((s) => s.user);
|
||||
const user = useAuthStore((s) => s.user);
|
||||
const data = useInventoryData();
|
||||
const navigate = useNavigate();
|
||||
const isPM = user?.role === "pm";
|
||||
@@ -124,8 +121,6 @@ export function DailyControlPage() {
|
||||
sub={inventorySubheader(user)}
|
||||
backTo="/inventory"
|
||||
backLabel="بازگشت به داشبورد"
|
||||
icon={<WarehouseIcon className="h-4.5 w-4.5" strokeWidth={2} />}
|
||||
menu={<PanelMenu title="پنل مدیریت انبار" items={isAdmin ? accountMenuItems(logout) : pmMenuItems(logout)} />}
|
||||
/>
|
||||
<PageLayout className="flex flex-col gap-3">
|
||||
{isAdmin && (
|
||||
@@ -175,11 +170,6 @@ export function DailyControlPage() {
|
||||
className={`relative rounded-lg py-2 text-[12px] ${!c.inMonth ? "text-muted/40" : ""} ${c.isToday ? "bg-accent font-bold text-white" : ""} ${sel === k ? "ring-2 ring-accent" : "hover:bg-accent/5"}`}
|
||||
>
|
||||
{toFa(jd)}
|
||||
{c.n > 0 && (
|
||||
<span className="absolute -top-0.5 -left-0.5 flex h-4 w-4 items-center justify-center rounded-full bg-red text-[9px] font-bold text-white">
|
||||
{toFa(c.n)}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -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<string, string> | 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 (
|
||||
<div className="min-h-screen">
|
||||
<Header title="داشبورد انبار" menu={<PanelMenu title="پنل مدیریت انبار" items={panelMenuItems(location.pathname, user?.role, logout)} />} />
|
||||
<Header title="داشبورد انبار" backTo="/inventory" backLabel="بازگشت به داشبورد" />
|
||||
<PageLayout>
|
||||
<EmptyState emoji="🔒" text="دسترسی به انبار فقط برای مدیر پروژه یا ادمین است." />
|
||||
</PageLayout>
|
||||
@@ -141,12 +140,7 @@ export function InventoryPage() {
|
||||
title="سامانه مدیریت انبار"
|
||||
sub={inventorySubheader(user)}
|
||||
icon={<WarehouseIcon className="h-4.5 w-4.5" strokeWidth={2} />}
|
||||
menu={
|
||||
<PanelMenu
|
||||
title="پنل مدیریت انبار"
|
||||
items={isAdmin ? accountMenuItems(logout) : pmMenuItems(logout)}
|
||||
/>
|
||||
}
|
||||
menu={<AppSwitcherMenu activeModule="warehouse" canSwitchToProcurement={isAdmin} />}
|
||||
actions={
|
||||
<>
|
||||
<InventoryTopNav pathname={location.pathname} variant={isAdmin ? "admin" : "pm"} />
|
||||
|
||||
@@ -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") },
|
||||
|
||||
@@ -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 (
|
||||
<div className="min-h-screen">
|
||||
<Header title="انبارها" menu={<PanelMenu title="پنل مدیریت انبار" items={panelMenuItems(location.pathname, user?.role, logout)} />} />
|
||||
<Header title="انبارها" backTo="/inventory" backLabel="بازگشت به داشبورد" />
|
||||
<PageLayout>
|
||||
<div className="rounded-xl border border-line bg-panel p-8 text-center">
|
||||
<div className="text-3xl">🔒</div>
|
||||
@@ -47,7 +43,7 @@ export function WarehousesPage() {
|
||||
if (!isPM) {
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<Header title="انبارها" menu={<PanelMenu title="پنل مدیریت انبار" items={panelMenuItems(location.pathname, user?.role, logout)} />} />
|
||||
<Header title="انبارها" backTo="/inventory" backLabel="بازگشت به داشبورد" />
|
||||
<PageLayout>
|
||||
<div className="text-[13px] text-muted">این بخش فقط برای مدیر پروژه است.</div>
|
||||
</PageLayout>
|
||||
@@ -101,8 +97,6 @@ export function WarehousesPage() {
|
||||
sub={inventorySubheader(user)}
|
||||
backTo="/inventory"
|
||||
backLabel="بازگشت به داشبورد"
|
||||
icon={<WarehouseIcon className="h-4.5 w-4.5" strokeWidth={2} />}
|
||||
menu={<PanelMenu title="پنل مدیریت انبار" items={pmMenuItems(logout)} />}
|
||||
/>
|
||||
<PageLayout className="flex flex-col gap-3">
|
||||
{isPM && (
|
||||
|
||||
Reference in New Issue
Block a user