feat(inventory): daily control - admin project filter and calendar navigation layout

This commit is contained in:
ali_mohaghegh
2026-08-27 15:04:25 +03:30
parent 0774f08823
commit 5e690a7fc8
@@ -3,7 +3,8 @@ import { useLocation, useNavigate } from "react-router-dom";
import { Header, PageLayout } from "../../components/layout/Header"; import { Header, PageLayout } from "../../components/layout/Header";
import { useAuthStore } from "../../store/auth"; import { useAuthStore } from "../../store/auth";
import { PanelMenu } from "../../components/PanelMenu"; import { PanelMenu } from "../../components/PanelMenu";
import { panelMenuItems, useLogout } from "./panelMenu"; import { accountMenuItems, useLogout } from "./panelMenu";
import { InventoryTopNav } from "./InventoryTopNav";
import { useInventoryData, unitName, whName } from "./useInventoryData"; import { useInventoryData, unitName, whName } from "./useInventoryData";
import { faNum } from "../../lib/formatters"; import { faNum } from "../../lib/formatters";
import { gregorianToJalali, MONTHS, toFa } from "../../lib/jalali"; import { gregorianToJalali, MONTHS, toFa } from "../../lib/jalali";
@@ -29,10 +30,17 @@ export function DailyControlPage() {
const [anchor, setAnchor] = useState<Date>(new Date()); const [anchor, setAnchor] = useState<Date>(new Date());
const [sel, setSel] = useState<string>(todayKey()); const [sel, setSel] = useState<string>(todayKey());
const [actRec, setActRec] = useState<string | null>(null); const [actRec, setActRec] = useState<string | null>(null);
// فیلتر پروژه — فقط برای مدیر تدارکات/ادمین (مدیر پروژه به پروژه خودش محدود است)
const isAdmin = user?.role === "admin" || user?.role === "procurement_manager";
const [projFilter, setProjFilter] = useState("");
const txByDay = useMemo(() => { const txByDay = useMemo(() => {
const map = new Map<string, InventoryTransaction[]>(); const map = new Map<string, InventoryTransaction[]>();
const projWhIds = projFilter
? new Set(data.warehouses.filter((w) => String(w.project_id) === String(projFilter)).map((w) => w.id))
: null;
data.transactions.forEach((t) => { data.transactions.forEach((t) => {
if (projWhIds && !projWhIds.has(t.warehouse_id)) return;
const k = gregKey(new Date(t.transaction_date)); const k = gregKey(new Date(t.transaction_date));
const arr = map.get(k) || []; const arr = map.get(k) || [];
arr.push(t); arr.push(t);
@@ -40,7 +48,7 @@ export function DailyControlPage() {
}); });
map.forEach((arr) => arr.sort((a, b) => new Date(a.transaction_date).getTime() - new Date(b.transaction_date).getTime())); map.forEach((arr) => arr.sort((a, b) => new Date(a.transaction_date).getTime() - new Date(b.transaction_date).getTime()));
return map; return map;
}, [data.transactions]); }, [data.transactions, data.warehouses, projFilter]);
// cells: 42 روز از شنبه قبلِ اولین روز ماه جلالی // cells: 42 روز از شنبه قبلِ اولین روز ماه جلالی
const cells = useMemo(() => { const cells = useMemo(() => {
@@ -99,18 +107,39 @@ export function DailyControlPage() {
<Header <Header
title="کنترل روزانه" title="کنترل روزانه"
sub={user?.project_name || ""} sub={user?.project_name || ""}
menu={<PanelMenu title="پنل مدیریت انبار" items={panelMenuItems(location.pathname, user?.role, logout)} />} menu={isAdmin ? <PanelMenu title="پنل مدیریت انبار" items={accountMenuItems(logout)} /> : undefined}
actions={isAdmin || isPM ? <InventoryTopNav pathname={location.pathname} onLogout={logout} variant={isAdmin ? "admin" : "pm"} /> : undefined}
/> />
<PageLayout className="flex flex-col gap-3"> <PageLayout className="flex flex-col gap-3">
{isAdmin && (
<div className="flex flex-wrap items-center gap-2 rounded-xl border border-line bg-panel px-3 py-2 text-[12.5px]">
<label className="font-bold">🏢 پروژه:</label>
<select
className="h-8 rounded-lg border border-line bg-panel px-2 text-[12.5px]"
value={projFilter}
onChange={(e) => setProjFilter(e.target.value)}
>
<option value="">همه پروژه‌ها</option>
{data.lookup.projects.map((p) => (
<option key={p.id} value={p.id}>{p.name}</option>
))}
</select>
<span className="text-[11.5px] text-muted">
{projFilter
? "نمایش کنترل روزانه فقط برای پروژه انتخابی"
: "نمایش کنترل روزانه همه پروژه‌ها"}
</span>
</div>
)}
<div className="grid gap-4 lg:grid-cols-2"> <div className="grid gap-4 lg:grid-cols-2">
{/* تقویم */} {/* تقویم */}
<div className="rounded-xl border border-line bg-panel p-4"> <div className="rounded-xl border border-line bg-panel p-4">
<div className="mb-2 flex items-center justify-between"> <div className="mb-2 flex items-center justify-between">
<div className="text-[14px] font-bold">📅 {monthTitle}</div> <div className="text-[14px] font-bold">📅 {monthTitle}</div>
<div className="flex gap-1.5"> <div className="flex gap-1.5">
<button className="rounded-lg border border-line px-3 py-1 text-[12px]" onClick={() => setAnchor(new Date(anchor.getTime() + 33 * 86400000))}>◀ ماه بعد</button>
<button className="rounded-lg border border-line px-3 py-1 text-[12px]" onClick={() => { setAnchor(new Date()); setSel(todayKey()); setActRec(null); }}>امروز</button>
<button className="rounded-lg border border-line px-3 py-1 text-[12px]" onClick={() => setAnchor(new Date(anchor.getTime() - 33 * 86400000))}>▶ ماه قبل</button> <button className="rounded-lg border border-line px-3 py-1 text-[12px]" onClick={() => setAnchor(new Date(anchor.getTime() - 33 * 86400000))}>▶ ماه قبل</button>
<button className="rounded-lg border border-line px-3 py-1 text-[12px]" onClick={() => { setAnchor(new Date()); setSel(todayKey()); setActRec(null); }}>امروز</button>
<button className="rounded-lg border border-line px-3 py-1 text-[12px]" onClick={() => setAnchor(new Date(anchor.getTime() + 33 * 86400000))}>◀ ماه بعد</button>
</div> </div>
</div> </div>
<div className="grid grid-cols-7 gap-1"> <div className="grid grid-cols-7 gap-1">