feat(frontend): add React UI — procurement office, wizard, suppliers, inventory panel (dash port), role-based login
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
// ============================================================
|
||||
// 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 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<void>;
|
||||
}
|
||||
|
||||
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<Warehouse[]>([]);
|
||||
const [inventory, setInventory] = useState<InventoryRow[]>([]);
|
||||
const [transactions, setTransactions] = useState<InventoryTransaction[]>([]);
|
||||
const [lookup, setLookup] = useState<LookupData>({
|
||||
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);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [projectId]);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
void load();
|
||||
}, [load]);
|
||||
|
||||
const scopedWarehouses = useMemo(
|
||||
() => (user?.role === "admin" ? 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 : "—";
|
||||
}
|
||||
Reference in New Issue
Block a user