feat: display available stock balance on products page based on n8n inventory updates
This commit is contained in:
@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
|
||||
import { Outlet, Link, useLocation } from 'react-router-dom';
|
||||
import { LayoutDashboard, Users, BarChart3, ChevronLeft, ChevronRight, Package, Loader2, LogOut } from 'lucide-react';
|
||||
import type { DateRange, OrderData } from '../types';
|
||||
import { fetchData, logout } from '../dataService';
|
||||
import { fetchData, fetchStock, logout } from '../dataService';
|
||||
|
||||
const Layout = () => {
|
||||
const location = useLocation();
|
||||
@@ -25,6 +25,7 @@ const Layout = () => {
|
||||
});
|
||||
|
||||
const [ordersData, setOrdersData] = useState<OrderData[]>([]);
|
||||
const [stockData, setStockData] = useState<any[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [refreshInterval, setRefreshInterval] = useState<number>(() => {
|
||||
const saved = localStorage.getItem('nexstar_refresh_interval');
|
||||
@@ -33,8 +34,9 @@ const Layout = () => {
|
||||
|
||||
const loadData = async (showLoading = false) => {
|
||||
if (showLoading) setIsLoading(true);
|
||||
const data = await fetchData();
|
||||
const [data, stock] = await Promise.all([fetchData(), fetchStock()]);
|
||||
setOrdersData(data);
|
||||
setStockData(stock);
|
||||
if (showLoading) setIsLoading(false);
|
||||
};
|
||||
|
||||
@@ -146,7 +148,7 @@ const Layout = () => {
|
||||
<Loader2 className="w-8 h-8 text-brand-primary animate-spin" />
|
||||
</div>
|
||||
) : (
|
||||
<Outlet context={{ dateRange, setDateRange, ordersData, refreshInterval, setRefreshInterval, loadData }} />
|
||||
<Outlet context={{ dateRange, setDateRange, ordersData, stockData, refreshInterval, setRefreshInterval, loadData }} />
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -33,6 +33,25 @@ export const isAuthenticated = (): boolean => {
|
||||
return !!localStorage.getItem('auth_token');
|
||||
};
|
||||
|
||||
export const fetchStock = async (): Promise<any[]> => {
|
||||
try {
|
||||
const token = localStorage.getItem('auth_token');
|
||||
const response = await fetch(`${API_URL}/stock`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
logout();
|
||||
return [];
|
||||
}
|
||||
if (!response.ok) return [];
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchData = async (): Promise<OrderData[]> => {
|
||||
try {
|
||||
const token = localStorage.getItem('auth_token');
|
||||
|
||||
@@ -6,10 +6,11 @@ import type { OrderData, DateRange } from '../types';
|
||||
import { parseOrderDate, exportToCSV } from '../dataService';
|
||||
|
||||
const Products = () => {
|
||||
const { dateRange, setDateRange, ordersData } = useOutletContext<{
|
||||
const { dateRange, setDateRange, ordersData, stockData } = useOutletContext<{
|
||||
dateRange: DateRange,
|
||||
setDateRange: (range: DateRange) => void,
|
||||
ordersData: OrderData[],
|
||||
stockData: any[],
|
||||
refreshInterval: number,
|
||||
setRefreshInterval: (interval: number) => void,
|
||||
loadData: (showLoading?: boolean) => void
|
||||
@@ -27,7 +28,21 @@ const Products = () => {
|
||||
|
||||
const productsData = useMemo(() => {
|
||||
const orders = ordersData;
|
||||
const productMap: Record<string, { id: string, name: string, totalSold: number, revenue: number, lastPrice: number }> = {};
|
||||
const productMap: Record<string, { id: string, name: string, totalSold: number, revenue: number, lastPrice: number, stock: number }> = {};
|
||||
|
||||
// Initialize with stock data
|
||||
if (stockData && Array.isArray(stockData)) {
|
||||
stockData.forEach(item => {
|
||||
productMap[item.produto_id] = {
|
||||
id: item.produto_id,
|
||||
name: item.nome,
|
||||
totalSold: 0,
|
||||
revenue: 0,
|
||||
lastPrice: 0,
|
||||
stock: item.saldo || 0
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
orders.forEach(order => {
|
||||
const orderDate = parseOrderDate(order.Data_Pedido);
|
||||
@@ -39,10 +54,16 @@ const Products = () => {
|
||||
name: order.Descricao_Produto.split(' TAMANHO')[0],
|
||||
totalSold: 0,
|
||||
revenue: 0,
|
||||
lastPrice: order.Valor_Unitario
|
||||
lastPrice: order.Valor_Unitario,
|
||||
stock: 0
|
||||
};
|
||||
}
|
||||
|
||||
// Update name if we didn't get it from stock
|
||||
if (productMap[order.ID_Produto].name === 'Unknown' || !productMap[order.ID_Produto].name) {
|
||||
productMap[order.ID_Produto].name = order.Descricao_Produto.split(' TAMANHO')[0];
|
||||
}
|
||||
|
||||
productMap[order.ID_Produto].totalSold += order.Quantidade;
|
||||
productMap[order.ID_Produto].revenue += (order.Quantidade * order.Valor_Unitario);
|
||||
productMap[order.ID_Produto].lastPrice = order.Valor_Unitario;
|
||||
@@ -118,6 +139,7 @@ const Products = () => {
|
||||
<th className="px-6 py-4 font-bold uppercase tracking-wider text-[10px]">ID Produto</th>
|
||||
<th className="px-6 py-4 font-bold uppercase tracking-wider text-[10px]">Descrição</th>
|
||||
<th className="px-6 py-4 font-bold uppercase tracking-wider text-[10px]">Total Vendido</th>
|
||||
<th className="px-6 py-4 font-bold uppercase tracking-wider text-[10px]">Estoque</th>
|
||||
<th className="px-6 py-4 font-bold uppercase tracking-wider text-[10px]">Receita Gerada</th>
|
||||
<th className="px-6 py-4 font-bold uppercase tracking-wider text-[10px] text-right">Ações</th>
|
||||
</tr>
|
||||
@@ -136,6 +158,11 @@ const Products = () => {
|
||||
<span className="font-bold text-zinc-900 dark:text-dark-text">{product.totalSold} un.</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-2.5">
|
||||
<span className={`font-bold ${product.stock > 10 ? 'text-emerald-500' : product.stock > 0 ? 'text-amber-500' : 'text-red-500'}`}>
|
||||
{product.stock} un.
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-2.5 text-brand-primary font-bold">{formatCurrency(product.revenue)}</td>
|
||||
<td className="px-6 py-2.5 text-right">
|
||||
<Link
|
||||
|
||||
Reference in New Issue
Block a user