// ============================================================ // Inventory data hook — loads all dash data (scoped by role/project) // ============================================================ import { useCallback, useEffect, useMemo, useState } from "react"; import { apiV26 } from "../../lib/api"; import { useAuthStore } from "../../store/auth"; import { useDataChanged } from "../../lib/crossTab"; import type { InventoryRow, InventoryTransaction, Warehouse } from "../../lib/types"; export interface LookupData { categories: { id: string; slug: string; name: string }[]; units: { id: number; code: string; name_fa: string | null }[]; brands: { id: string; name: string }[]; materials: { id: string; name: string; category: { id: string; name: string } | null; canonical_unit: { id: number; code: string; name_fa: string | null } | null }[]; projects: { id: string; name: string; pm_user_id: string | null; pm_name?: string | null }[]; } export interface InventoryData { warehouses: Warehouse[]; inventory: InventoryRow[]; transactions: InventoryTransaction[]; lookup: LookupData; loading: boolean; refresh: () => Promise; } export function useInventoryData(): InventoryData { const user = useAuthStore((s) => s.user); const isPM = user?.role === "pm"; const projectId = isPM ? user.project_id || undefined : undefined; const [warehouses, setWarehouses] = useState([]); const [inventory, setInventory] = useState([]); const [transactions, setTransactions] = useState([]); const [lookup, setLookup] = useState({ categories: [], units: [], brands: [], materials: [], projects: [], }); const [loading, setLoading] = useState(true); const load = useCallback(async () => { try { const [cats, units, brands, mats, projs, whs, inv, txs] = await Promise.all([ apiV26.lookup.categories(), apiV26.lookup.units(), apiV26.lookup.brands(), apiV26.lookup.materials(), apiV26.lookup.projects(), apiV26.warehouses.list(projectId), apiV26.inventory.list({ project_id: projectId }), apiV26.inventory.transactions({ project_id: projectId }), ]); setLookup({ categories: cats, units, brands, materials: mats, projects: projs }); setWarehouses(whs); setInventory(inv); setTransactions(txs); } catch { // پروژه‌ی فعال ممکن است همین حالا باطل شده باشد — گیتِ پروژه در سطح مسیر // (RequirePmProjects) پروژه‌ی جایگزین/صفحه‌ی خالی را نشان می‌دهد. } finally { setLoading(false); } }, [projectId]); useEffect(() => { setLoading(true); void load(); }, [load]); // تب‌های دیگر (ثبت تراکنش/رسید در تب دوم) → بازخوانی خودکار همین صفحه useDataChanged( useCallback(() => { void load(); }, [load]), ["quotes", "inventory"], ); const scopedWarehouses = useMemo( () => user?.role === "admin" || user?.role === "procurement_manager" ? warehouses : warehouses.filter((w) => String(w.project_id) === String(user?.project_id || "")), [warehouses, user?.role, user?.project_id], ); return { warehouses: scopedWarehouses, inventory, transactions, lookup, loading, refresh: load, }; } export function unitName(units: LookupData["units"], id: number | null | undefined): string { const u = units.find((x) => x.id === id); return u ? u.name_fa || u.code : "—"; } export function brandName(brands: LookupData["brands"], id: string | null | undefined): string { const b = brands.find((x) => x.id === id); return b ? b.name : "—"; } export function materialName(materials: LookupData["materials"], id: string): string { const m = materials.find((x) => x.id === id); return m ? m.name : "—"; } export function catName(categories: LookupData["categories"], id: string | null | undefined): string { const c = categories.find((x) => x.id === id); return c ? c.name : "—"; } export function projName(projects: LookupData["projects"], id: string | null | undefined): string { const p = projects.find((x) => x.id === id); return p ? p.name : "—"; } export function whName(warehouses: Warehouse[], id: string | null | undefined): string { const w = warehouses.find((x) => x.id === id); return w ? w.name : "—"; }