Use stock status labels for products
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 45s

This commit is contained in:
Cauê Faleiros
2026-07-06 14:01:05 -03:00
parent 578835d9da
commit 76de11f4de

View File

@@ -12,7 +12,7 @@ type StockStatusFilter = 'all' | StockRisk;
type StockQuantityFilter = 'all' | 'zero' | 'positive' | 'low' | 'high'; type StockQuantityFilter = 'all' | 'zero' | 'positive' | 'low' | 'high';
type SalesFilter = 'all' | 'sold' | 'not_sold'; type SalesFilter = 'all' | 'sold' | 'not_sold';
type CoverageFilter = 'all' | 'up_to_7' | 'up_to_14' | 'up_to_30' | 'over_30' | 'none'; type CoverageFilter = 'all' | 'up_to_7' | 'up_to_14' | 'up_to_30' | 'over_30' | 'none';
type ProductSortOption = 'sold_desc' | 'sold_asc' | 'revenue_desc' | 'revenue_asc' | 'stock_asc' | 'stock_desc' | 'coverage_asc' | 'coverage_desc' | 'name_asc'; type ProductSortOption = 'sold_desc' | 'sold_asc' | 'stock_priority' | 'revenue_desc' | 'revenue_asc' | 'stock_asc' | 'stock_desc' | 'coverage_asc' | 'coverage_desc' | 'name_asc';
type ProductRow = ProductAnalyticsItem & { type ProductRow = ProductAnalyticsItem & {
dailySales: number; dailySales: number;
@@ -28,22 +28,22 @@ const riskStyles: Record<StockRisk, { label: string; className: string; dotClass
dotClass: 'bg-zinc-400' dotClass: 'bg-zinc-400'
}, },
critical: { critical: {
label: '0-7 dias', label: 'Repor agora',
className: 'border-red-400/35 bg-red-400/10 text-red-300', className: 'border-red-400/35 bg-red-400/10 text-red-300',
dotClass: 'bg-red-400' dotClass: 'bg-red-400'
}, },
attention: { attention: {
label: '8-14 dias', label: 'Atenção',
className: 'border-amber-400/35 bg-amber-400/10 text-amber-300', className: 'border-amber-400/35 bg-amber-400/10 text-amber-300',
dotClass: 'bg-amber-400' dotClass: 'bg-amber-400'
}, },
monitor: { monitor: {
label: '15-30 dias', label: 'Monitorar',
className: 'border-sky-400/35 bg-sky-400/10 text-sky-300', className: 'border-sky-400/35 bg-sky-400/10 text-sky-300',
dotClass: 'bg-sky-400' dotClass: 'bg-sky-400'
}, },
healthy: { healthy: {
label: '>30 dias', label: 'Saudável',
className: 'border-emerald-400/35 bg-emerald-400/10 text-emerald-300', className: 'border-emerald-400/35 bg-emerald-400/10 text-emerald-300',
dotClass: 'bg-emerald-400' dotClass: 'bg-emerald-400'
}, },
@@ -57,13 +57,22 @@ const riskStyles: Record<StockRisk, { label: string; className: string; dotClass
const stockStatusOptions: Array<{ value: StockStatusFilter; label: string }> = [ const stockStatusOptions: Array<{ value: StockStatusFilter; label: string }> = [
{ value: 'all', label: 'Todos' }, { value: 'all', label: 'Todos' },
{ value: 'rupture', label: 'Sem estoque' }, { value: 'rupture', label: 'Sem estoque' },
{ value: 'critical', label: '0-7 dias' }, { value: 'critical', label: 'Repor agora' },
{ value: 'attention', label: '8-14 dias' }, { value: 'attention', label: 'Atenção' },
{ value: 'monitor', label: '15-30 dias' }, { value: 'monitor', label: 'Monitorar' },
{ value: 'healthy', label: '>30 dias' }, { value: 'healthy', label: 'Saudável' },
{ value: 'no_sales', label: 'Sem venda' } { value: 'no_sales', label: 'Sem venda' }
]; ];
const stockPriority: Record<StockRisk, number> = {
rupture: 0,
critical: 1,
attention: 2,
monitor: 3,
healthy: 4,
no_sales: 5
};
const dateFilterPresets = [ const dateFilterPresets = [
{ value: 'today', label: 'Hoje', getRange: () => rangeForDay(new Date()) }, { value: 'today', label: 'Hoje', getRange: () => rangeForDay(new Date()) },
{ value: 'yesterday', label: 'Ontem', getRange: () => rangeForPreviousDay() }, { value: 'yesterday', label: 'Ontem', getRange: () => rangeForPreviousDay() },
@@ -258,8 +267,8 @@ const Products = () => {
coverageFilter === 'all' || coverageFilter === 'all' ||
(coverageFilter === 'none' && product.daysOfCover === null) || (coverageFilter === 'none' && product.daysOfCover === null) ||
(coverageFilter === 'up_to_7' && product.daysOfCover !== null && product.daysOfCover <= 7) || (coverageFilter === 'up_to_7' && product.daysOfCover !== null && product.daysOfCover <= 7) ||
(coverageFilter === 'up_to_14' && product.daysOfCover !== null && product.daysOfCover <= 14) || (coverageFilter === 'up_to_14' && product.daysOfCover !== null && product.daysOfCover > 7 && product.daysOfCover <= 14) ||
(coverageFilter === 'up_to_30' && product.daysOfCover !== null && product.daysOfCover <= 30) || (coverageFilter === 'up_to_30' && product.daysOfCover !== null && product.daysOfCover > 14 && product.daysOfCover <= 30) ||
(coverageFilter === 'over_30' && product.daysOfCover !== null && product.daysOfCover > 30); (coverageFilter === 'over_30' && product.daysOfCover !== null && product.daysOfCover > 30);
return matchesStatus && matchesStockQuantity && matchesSales && matchesCoverage; return matchesStatus && matchesStockQuantity && matchesSales && matchesCoverage;
@@ -268,6 +277,11 @@ const Products = () => {
return detailedFilteredProducts.sort((a, b) => { return detailedFilteredProducts.sort((a, b) => {
switch (sortBy) { switch (sortBy) {
case 'sold_asc': return a.quantitySold - b.quantitySold; case 'sold_asc': return a.quantitySold - b.quantitySold;
case 'stock_priority': {
const priorityDiff = stockPriority[a.risk] - stockPriority[b.risk];
if (priorityDiff !== 0) return priorityDiff;
return b.quantitySold - a.quantitySold;
}
case 'revenue_desc': return b.revenue - a.revenue; case 'revenue_desc': return b.revenue - a.revenue;
case 'revenue_asc': return a.revenue - b.revenue; case 'revenue_asc': return a.revenue - b.revenue;
case 'stock_asc': return a.stock - b.stock; case 'stock_asc': return a.stock - b.stock;
@@ -433,6 +447,7 @@ const Products = () => {
> >
<option value="sold_desc">Mais vendidos</option> <option value="sold_desc">Mais vendidos</option>
<option value="sold_asc">Menos vendidos</option> <option value="sold_asc">Menos vendidos</option>
<option value="stock_priority">Prioridade de estoque</option>
<option value="revenue_desc">Maior receita</option> <option value="revenue_desc">Maior receita</option>
<option value="revenue_asc">Menor receita</option> <option value="revenue_asc">Menor receita</option>
<option value="stock_asc">Menor estoque</option> <option value="stock_asc">Menor estoque</option>
@@ -464,7 +479,7 @@ const Products = () => {
</label> </label>
<label className="block text-xs font-bold uppercase tracking-wider text-dark-muted"> <label className="block text-xs font-bold uppercase tracking-wider text-dark-muted">
Faixa de cobertura Status
<select <select
value={stockStatusFilter} value={stockStatusFilter}
onChange={(event) => { onChange={(event) => {
@@ -559,7 +574,7 @@ const Products = () => {
const exportData = productsData.map(product => ({ const exportData = productsData.map(product => ({
'ID Produto': product.id, 'ID Produto': product.id,
'Descrição': product.name, 'Descrição': product.name,
'Faixa de Cobertura': product.riskLabel, 'Status': product.riskLabel,
'Preço Atual (R$)': product.lastPrice.toFixed(2).replace('.', ','), 'Preço Atual (R$)': product.lastPrice.toFixed(2).replace('.', ','),
'Total Vendido (un.)': product.quantitySold, 'Total Vendido (un.)': product.quantitySold,
'Estoque': product.stock, 'Estoque': product.stock,
@@ -601,7 +616,7 @@ const Products = () => {
<tr> <tr>
<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]">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]">Descrição</th>
<th className="px-6 py-4 font-bold uppercase tracking-wider text-[10px]">Faixa de cobertura</th> <th className="px-6 py-4 font-bold uppercase tracking-wider text-[10px]">Status</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]">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]">Estoque</th>
<th className="px-6 py-4 font-bold uppercase tracking-wider text-[10px]">Média Diária</th> <th className="px-6 py-4 font-bold uppercase tracking-wider text-[10px]">Média Diária</th>