diff --git a/src/pages/Supplies.tsx b/src/pages/Supplies.tsx index 95586d4..7a0c987 100644 --- a/src/pages/Supplies.tsx +++ b/src/pages/Supplies.tsx @@ -28,7 +28,7 @@ import DateRangePicker from '../components/DateRangePicker'; import PaginationControls from '../components/PaginationControls'; import ProductTypeBadge from '../components/ProductTypeBadge'; import { classifyCutFamily } from '../analytics/cutting'; -import { adjustSupplyLotInventory, approveSupplyReceipt, consumeSupplyLotForProduction, createSupplyFabricPlan, createSupplyReceipt, deleteSupplyFabricPlan, deleteSupplyReceipt, downloadDatabaseDiagnostic, fetchCuttingSettings, fetchProductAnalytics, fetchStock, fetchSupplySummary, isSuperAdmin } from '../dataService'; +import { adjustSupplyLotInventory, approveSupplyReceipt, consumeSupplyLotForProduction, createSupplyFabricPlan, createSupplyReceipt, deleteSupplyFabricPlan, deleteSupplyReceipt, downloadDatabaseDiagnostic, fetchCuttingSettings, fetchProductAnalytics, fetchProductionOrders, fetchStock, fetchSupplySummary, isSuperAdmin } from '../dataService'; import { parseProductName } from '../productParsing'; import { resolveProductType, type ProductTypeKey } from '../productClassification'; import { getPlanningStock } from '../planningStock'; @@ -42,6 +42,10 @@ const panelClassName = 'rounded-2xl border border-dark-border bg-dark-card shado const buttonClassName = 'inline-flex h-10 items-center justify-center gap-2 rounded-lg border border-dark-border bg-dark-input px-3 text-sm font-bold text-dark-text transition-colors hover:border-brand-primary cursor-pointer'; const inputClassName = 'h-10 w-full rounded-lg border border-dark-border bg-dark-input px-3 text-sm font-semibold text-dark-text outline-none placeholder:text-dark-muted focus:border-brand-primary'; const emptyStateClassName = 'flex min-h-[190px] flex-col items-center justify-center gap-2 px-4 py-10 text-center'; +const allProductionOrdersRange = { start: new Date(2000, 0, 1), end: new Date(2100, 11, 31) }; +const isServicePurchaseNeed = (need: SupplyPurchaseNeed) => ( + /\b(?:SERVI[CÇ]O|FRETE|TINTURARIA|TECELAGEM|COSTURA|ESTAMPARIA|LAVANDERIA)\b/i.test(need.material) +); const emptySupplySummary: SupplySummary = { receipts: [], @@ -170,15 +174,19 @@ const ModuleCard = ({ description, icon: Icon, to, + meta, + priority = false, }: { title: string; description: string; icon: typeof Package; to: string; + meta?: string; + priority?: boolean; }) => { return ( - - + + @@ -187,6 +195,7 @@ const ModuleCard = ({ {description} + {meta && {meta}} ); @@ -196,6 +205,29 @@ const SuppliesHub = () => { const [isExportingDiagnostic, setIsExportingDiagnostic] = useState(false); const [diagnosticError, setDiagnosticError] = useState(''); const canExportDiagnostic = isSuperAdmin(); + const [summary, setSummary] = useState(emptySupplySummary); + const [productionCounts, setProductionCounts] = useState({ open: 0, inProgress: 0 }); + + useEffect(() => { + let isMounted = true; + const loadHub = async () => { + const [supplySummary, productionSummary] = await Promise.all([ + fetchSupplySummary(), + fetchProductionOrders(allProductionOrdersRange), + ]); + if (!isMounted) return; + setSummary(supplySummary); + setProductionCounts({ open: productionSummary.counts.open || 0, inProgress: productionSummary.counts.in_progress || 0 }); + }; + void loadHub(); + return () => { isMounted = false; }; + }, []); + + const materialPurchaseNeeds = summary.purchaseNeeds.filter(need => !need.missingReference && !isServicePurchaseNeed(need) && need.purchaseKg > 0); + const mappingSkuCount = new Set(summary.purchaseNeeds + .filter(need => need.missingReference) + .flatMap(need => (need.products || []).map(product => product.productId)) + .filter(Boolean)).size; const handleExportDiagnostic = async () => { setIsExportingDiagnostic(true); @@ -230,14 +262,30 @@ const SuppliesHub = () => { {diagnosticError} )} - - - - - - - - + + + + Ações de hoje + Siga o fluxo: comprar materiais, definir o corte e acompanhar a produção. + + + + + + + + + + + Gestão de materiais + Cadastros e controles que sustentam o planejamento operacional. + + + + + + + ); }; @@ -1792,10 +1840,6 @@ const getNeedPendingLabel = (need: SupplyPurchaseNeed) => { type PurchaseNeedTab = 'materials' | 'services' | 'mapping'; -const isServicePurchaseNeed = (need: SupplyPurchaseNeed) => ( - /\b(?:SERVI[CÇ]O|FRETE|TINTURARIA|TECELAGEM|COSTURA|ESTAMPARIA|LAVANDERIA)\b/i.test(need.material) -); - const sumNeedProducts = ( need: SupplyPurchaseNeed, field: 'suggestedQuantity' | 'quantitySold' | 'stockQuantity'
{description}
{meta}
Siga o fluxo: comprar materiais, definir o corte e acompanhar a produção.
Cadastros e controles que sustentam o planejamento operacional.