From 58caee64eb8a6f92ff0a4638a255fe3b56cd6841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Tue, 4 Aug 2026 12:57:22 -0300 Subject: [PATCH] Clarify synchronized and local inventory --- src/pages/Supplies.tsx | 90 +++++++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/src/pages/Supplies.tsx b/src/pages/Supplies.tsx index edf8db6..fdc9d03 100644 --- a/src/pages/Supplies.tsx +++ b/src/pages/Supplies.tsx @@ -831,7 +831,17 @@ const StatGrid = ({ summary }: { summary: SupplySummary }) => { ); }; -const InventoryDashboard = ({ summary, onNewReceipt }: { summary: SupplySummary; onNewReceipt: () => void }) => { +const InventoryDashboard = ({ + summary, + stock, + onNewReceipt, + onOpenStock, +}: { + summary: SupplySummary; + stock: StockData[]; + onNewReceipt: () => void; + onOpenStock: () => void; +}) => { const categories = summary.lots.reduce>((acc, lot) => { const current = acc[lot.category] || { quantity: 0, unit: lot.unit, lots: 0 }; acc[lot.category] = { @@ -842,12 +852,46 @@ const InventoryDashboard = ({ summary, onNewReceipt }: { summary: SupplySummary; return acc; }, {}); const latestReceipts = summary.receipts.slice(0, 4); + const negativeStockCount = stock.filter(item => Number(item.saldo || 0) < 0).length; + const zeroStockCount = stock.filter(item => Number(item.saldo || 0) === 0).length; + const latestStockUpdate = stock + .map(item => item.updated_at ? new Date(item.updated_at).getTime() : 0) + .filter(Boolean) + .sort((a, b) => b - a)[0] || null; return (
- +
+ + + +
+

Lotes locais

+

{formatNumber(summary.stats.activeLots, 0)}

+

Controle físico registrado aqui

+
+
+
-

Por categoria de material

+
+
+

Estoque físico por categoria

+

Lotes aprovados por recebimento ou inventário local.

+
+ {formatNumber(summary.stats.rolls, 0)} rolos +
{Object.keys(categories).length ? (
{Object.entries(categories).map(([category, data]) => ( @@ -861,14 +905,25 @@ const InventoryDashboard = ({ summary, onNewReceipt }: { summary: SupplySummary; ) : (
-

Nenhuma categoria com saldo

-

Registre entradas para agrupar o estoque por tipo de material.

+

Nenhum lote físico registrado

+

O saldo do Tiny aparece na aba Estoque. Registre recebimentos para controlar lotes aqui.

)}
-

Alertas de estoque

-

Nenhum alerta no momento.

+

Conferir antes de planejar

+
+ + +

Última sincronização: {latestStockUpdate ? formatDateTime(new Date(latestStockUpdate).toISOString()) : 'sem atualização registrada'}.

+
+
@@ -1732,7 +1787,7 @@ const InventoryScreen = () => {
Carregando estoque...
) : ( <> - {activeTab === 'dashboard' && setActiveTab('receipts')} />} + {activeTab === 'dashboard' && setActiveTab('receipts')} onOpenStock={() => setActiveTab('stock')} />} {activeTab === 'stock' && } {activeTab === 'balance' && } {activeTab === 'receipts' && ( @@ -2244,10 +2299,10 @@ const PurchaseNeedsScreen = () => { const [summary, setSummary] = useState(emptySupplySummary); const [isLoading, setIsLoading] = useState(true); const [search, setSearch] = useState(''); - const initialTab = searchParams.get('tab'); - const initialFilter = searchParams.get('filter'); - const [activeTab, setActiveTab] = useState(initialTab === 'services' || initialTab === 'mapping' ? initialTab : 'materials'); - const [materialFilter, setMaterialFilter] = useState(initialFilter === 'negative_balance' || initialFilter === 'no_stock' || initialFilter === 'all' ? initialFilter : 'to_buy'); + const requestedTab = searchParams.get('tab'); + const requestedFilter = searchParams.get('filter'); + const activeTab: PurchaseNeedTab = requestedTab === 'services' || requestedTab === 'mapping' ? requestedTab : 'materials'; + const materialFilter: MaterialPurchaseFilter = requestedFilter === 'negative_balance' || requestedFilter === 'no_stock' || requestedFilter === 'all' ? requestedFilter : 'to_buy'; const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(10); @@ -2276,18 +2331,7 @@ const PurchaseNeedsScreen = () => { }; }, []); - useEffect(() => { - const tab = searchParams.get('tab'); - const filter = searchParams.get('filter'); - setActiveTab(tab === 'services' || tab === 'mapping' ? tab : 'materials'); - setMaterialFilter(filter === 'negative_balance' || filter === 'no_stock' || filter === 'all' ? filter : 'to_buy'); - setSearch(''); - setCurrentPage(1); - }, [searchParams]); - const openQueue = (tab: PurchaseNeedTab, filter: MaterialPurchaseFilter = 'to_buy') => { - setActiveTab(tab); - setMaterialFilter(filter); setSearch(''); setCurrentPage(1); setSearchParams(tab === 'materials' ? { tab, filter } : { tab });