mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 11:45:23 +02:00
Fix: consolidate stock badges and update WOC calculation to include internal warehouse stock
This commit is contained in:
@@ -82,7 +82,13 @@ const AdsRow: React.FC<{
|
|||||||
{stockMap && (
|
{stockMap && (
|
||||||
<StockBadge stock={stockMap.get(item.sku?.replace(/(DE|EN)$/i, ''))} />
|
<StockBadge stock={stockMap.get(item.sku?.replace(/(DE|EN)$/i, ''))} />
|
||||||
)}
|
)}
|
||||||
<VendorStockBadge asin={item.asin} vendorStockMap={vendorStockMap} mode={top50Mode} avgWeeklySales={velocityMap?.get(asin)} />
|
<VendorStockBadge
|
||||||
|
asin={item.asin}
|
||||||
|
vendorStockMap={vendorStockMap}
|
||||||
|
mode={top50Mode}
|
||||||
|
avgWeeklySales={velocityMap?.get(asin)}
|
||||||
|
internalStock={stockMap?.get(item.sku?.replace(/(DE|EN)$/i, ''))}
|
||||||
|
/>
|
||||||
<BuyBoxWarningBadge asin={asin} buyBoxLostMap={buyBoxLostMap} />
|
<BuyBoxWarningBadge asin={asin} buyBoxLostMap={buyBoxLostMap} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1400,7 +1400,13 @@ const DataGrid: React.FC<DataGridProps> = ({ data, filters, hasCustomerFilter, a
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
})()}
|
})()}
|
||||||
<VendorStockBadge asin={row.asin} vendorStockMap={vendorStockMap} mode={top50Mode} avgWeeklySales={velocityMap?.get(row.asin.trim().toUpperCase())} />
|
<VendorStockBadge
|
||||||
|
asin={row.asin}
|
||||||
|
vendorStockMap={vendorStockMap}
|
||||||
|
mode={top50Mode}
|
||||||
|
avgWeeklySales={velocityMap?.get(row.asin.trim().toUpperCase())}
|
||||||
|
internalStock={stockMap?.get(row.sku?.replace(/(DE|EN)$/i, ''))}
|
||||||
|
/>
|
||||||
<BuyBoxWarningBadge asin={row.asin} buyBoxLostMap={buyBoxLostMap} />
|
<BuyBoxWarningBadge asin={row.asin} buyBoxLostMap={buyBoxLostMap} />
|
||||||
</div>
|
</div>
|
||||||
: dim === 'asin' || dim === 'sku'
|
: dim === 'asin' || dim === 'sku'
|
||||||
|
|||||||
@@ -157,6 +157,8 @@ const MoversTable: React.FC<{
|
|||||||
asin={item.asin}
|
asin={item.asin}
|
||||||
vendorStockMap={vendorStockMap}
|
vendorStockMap={vendorStockMap}
|
||||||
mode={top50Mode}
|
mode={top50Mode}
|
||||||
|
avgWeeklySales={velocityMap?.get(item.asin.trim().toUpperCase())}
|
||||||
|
internalStock={stockMap?.get(item.sku?.replace(/(DE|EN)$/i, ''))}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
|||||||
@@ -502,6 +502,7 @@ const VendorDataView: React.FC<VendorDataViewProps> = ({
|
|||||||
vendorStockMap={vendorStockMap}
|
vendorStockMap={vendorStockMap}
|
||||||
mode={top50Mode}
|
mode={top50Mode}
|
||||||
avgWeeklySales={velocityMap?.get(productInfo.asin.trim().toUpperCase())}
|
avgWeeklySales={velocityMap?.get(productInfo.asin.trim().toUpperCase())}
|
||||||
|
internalStock={stockMap?.get(productInfo.sku?.replace(/(DE|EN)$/i, ''))}
|
||||||
/>
|
/>
|
||||||
<BuyBoxWarningBadge asin={productInfo.asin} buyBoxLostMap={buyBoxLostMap} />
|
<BuyBoxWarningBadge asin={productInfo.asin} buyBoxLostMap={buyBoxLostMap} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ interface VendorStockBadgeProps {
|
|||||||
vendorStockMap?: Map<string, { eu: number; uk: number }>;
|
vendorStockMap?: Map<string, { eu: number; uk: number }>;
|
||||||
mode: 'eu' | 'uk';
|
mode: 'eu' | 'uk';
|
||||||
avgWeeklySales?: number;
|
avgWeeklySales?: number;
|
||||||
|
internalStock?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VendorStockBadge: React.FC<VendorStockBadgeProps> = ({ asin, vendorStockMap, mode, avgWeeklySales }) => {
|
export const VendorStockBadge: React.FC<VendorStockBadgeProps> = ({ asin, vendorStockMap, mode, avgWeeklySales, internalStock }) => {
|
||||||
if (!asin) return null;
|
if (!asin) return null;
|
||||||
|
|
||||||
const normalizedAsin = asin.trim().toUpperCase();
|
const normalizedAsin = asin.trim().toUpperCase();
|
||||||
@@ -27,7 +28,8 @@ export const VendorStockBadge: React.FC<VendorStockBadgeProps> = ({ asin, vendor
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const stock = mode === 'uk' ? stockData.uk : stockData.eu;
|
const amazonStock = mode === 'uk' ? stockData.uk : stockData.eu;
|
||||||
|
const totalStock = amazonStock + (internalStock || 0);
|
||||||
|
|
||||||
// Calculate Weeks of Coverage (WOC)
|
// Calculate Weeks of Coverage (WOC)
|
||||||
// If sales are 0 but store has stock -> Infinite coverage (>52 weeks) -> Green
|
// If sales are 0 but store has stock -> Infinite coverage (>52 weeks) -> Green
|
||||||
@@ -37,8 +39,8 @@ export const VendorStockBadge: React.FC<VendorStockBadgeProps> = ({ asin, vendor
|
|||||||
const velocity = avgWeeklySales || 0;
|
const velocity = avgWeeklySales || 0;
|
||||||
|
|
||||||
if (velocity > 0) {
|
if (velocity > 0) {
|
||||||
woc = stock / velocity;
|
woc = totalStock / velocity;
|
||||||
} else if (stock > 0) {
|
} else if (totalStock > 0) {
|
||||||
woc = 999;
|
woc = 999;
|
||||||
} else {
|
} else {
|
||||||
woc = 0;
|
woc = 0;
|
||||||
@@ -53,17 +55,17 @@ export const VendorStockBadge: React.FC<VendorStockBadgeProps> = ({ asin, vendor
|
|||||||
<div className="flex flex-col items-center gap-0.5 justify-center min-w-[70px]">
|
<div className="flex flex-col items-center gap-0.5 justify-center min-w-[70px]">
|
||||||
<div
|
<div
|
||||||
className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded border text-[10px] font-bold transition-all hover:scale-105 active:scale-95 cursor-default ${themeClasses}`}
|
className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded border text-[10px] font-bold transition-all hover:scale-105 active:scale-95 cursor-default ${themeClasses}`}
|
||||||
title={`Stock en Amazon Warehouse (${mode.toUpperCase()}): ${stock}`}
|
title={`Stock en Amazon Warehouse (${mode.toUpperCase()}): ${amazonStock}${internalStock ? ` | Almacén GMBH: ${internalStock}` : ''} | TOTAL: ${totalStock}`}
|
||||||
>
|
>
|
||||||
<AmazonSmileIcon className="w-3.5 h-3.5" />
|
<AmazonSmileIcon className="w-3.5 h-3.5" />
|
||||||
<div className="flex flex-col leading-[0.8]">
|
<div className="flex flex-col leading-[0.8]">
|
||||||
<span className="text-[7px] font-black uppercase opacity-60">Vendor</span>
|
<span className="text-[7px] font-black uppercase opacity-60">Vendor</span>
|
||||||
<span>{stock.toLocaleString('de-DE')}</span>
|
<span>{amazonStock.toLocaleString('de-DE')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`px-1.5 py-0.5 rounded-sm bg-slate-900/40 border border-white/5 shadow-inner`}
|
className={`px-1.5 py-0.5 rounded-sm bg-slate-900/40 border border-white/5 shadow-inner`}
|
||||||
title={`Avg Sales (4wk): ${velocity.toFixed(2)} | Stock: ${stock}`}
|
title={`Avg Sales (4wk): ${velocity.toFixed(2)} | Total Stock (Amz+Gmbh): ${totalStock}`}
|
||||||
>
|
>
|
||||||
<span className={`text-[10px] font-black uppercase tracking-tighter whitespace-nowrap block ${wocColor}`}>
|
<span className={`text-[10px] font-black uppercase tracking-tighter whitespace-nowrap block ${wocColor}`}>
|
||||||
{wocText}
|
{wocText}
|
||||||
|
|||||||
@@ -692,15 +692,6 @@ const WeeklyGrid: React.FC<WeeklyGridProps & { top50Mode: 'eu' | 'uk' }> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
{/* COVERAGE Header Column */}
|
|
||||||
<th
|
|
||||||
className="p-3 text-[11px] font-black text-emerald-400 uppercase tracking-widest sticky left-[240px] z-40 bg-slate-900 border-r border-white/10 min-w-[100px]"
|
|
||||||
>
|
|
||||||
<div className="flex flex-col items-center gap-1">
|
|
||||||
<span>Coverage</span>
|
|
||||||
<span className="text-[8px] text-slate-500">(WOC)</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
{weeks.map(week => (
|
{weeks.map(week => (
|
||||||
<th
|
<th
|
||||||
key={week}
|
key={week}
|
||||||
|
|||||||
@@ -118,6 +118,13 @@ export const WeeklyRow: React.FC<WeeklyRowProps> = React.memo(({
|
|||||||
{stockMap && (
|
{stockMap && (
|
||||||
<StockBadge stock={stockMap.get(row.sku?.replace(/(DE|EN)$/i, ''))} />
|
<StockBadge stock={stockMap.get(row.sku?.replace(/(DE|EN)$/i, ''))} />
|
||||||
)}
|
)}
|
||||||
|
<VendorStockBadge
|
||||||
|
asin={asin}
|
||||||
|
vendorStockMap={vendorStockMap}
|
||||||
|
mode={top50Mode}
|
||||||
|
avgWeeklySales={velocityMap?.get(asin)}
|
||||||
|
internalStock={stockMap?.get(row.sku?.replace(/(DE|EN)$/i, ''))}
|
||||||
|
/>
|
||||||
{(() => {
|
{(() => {
|
||||||
if (asin && top50Ranking) {
|
if (asin && top50Ranking) {
|
||||||
if (top50Mode === 'eu') {
|
if (top50Mode === 'eu') {
|
||||||
@@ -141,17 +148,6 @@ export const WeeklyRow: React.FC<WeeklyRowProps> = React.memo(({
|
|||||||
<span className="text-[9px] text-fuchsia-400/80 font-bold uppercase tracking-widest">{row.line}</span>
|
<span className="text-[9px] text-fuchsia-400/80 font-bold uppercase tracking-widest">{row.line}</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
{/* Coverage Column */}
|
|
||||||
<td className="p-3 py-2 sticky left-[240px] z-10 bg-slate-900 group-hover:bg-slate-800 border-r border-white/10 min-w-[100px]">
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<VendorStockBadge
|
|
||||||
asin={asin}
|
|
||||||
vendorStockMap={vendorStockMap}
|
|
||||||
mode={top50Mode}
|
|
||||||
avgWeeklySales={velocityMap?.get(asin)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
{weeks.map((week, idx) => {
|
{weeks.map((week, idx) => {
|
||||||
const val = row.unitsByWeek[week] || 0;
|
const val = row.unitsByWeek[week] || 0;
|
||||||
const prevVal = row.unitsByWeek[weeks[idx + 1]] || 0;
|
const prevVal = row.unitsByWeek[weeks[idx + 1]] || 0;
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ export const MasterTable: React.FC<MasterTableProps> = ({
|
|||||||
vendorStockMap={vendorStockMap}
|
vendorStockMap={vendorStockMap}
|
||||||
mode={top50Mode}
|
mode={top50Mode}
|
||||||
avgWeeklySales={velocityMap?.get(product.asin.trim().toUpperCase())}
|
avgWeeklySales={velocityMap?.get(product.asin.trim().toUpperCase())}
|
||||||
|
internalStock={stockMap?.get(product.sku?.replace(/(DE|EN)$/i, ''))}
|
||||||
/>
|
/>
|
||||||
<BuyBoxWarningBadge asin={product.asin} buyBoxLostMap={buyBoxLostMap} />
|
<BuyBoxWarningBadge asin={product.asin} buyBoxLostMap={buyBoxLostMap} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user