mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 12:25:22 +02:00
Integrate stock availability data and StockBadge component across all views
This commit is contained in:
@@ -2,6 +2,7 @@ import React, { useMemo, useState, useCallback } from 'react';
|
||||
import * as XLSX from 'xlsx';
|
||||
import { CombinedKPIs, FilterState } from '../types';
|
||||
import { DownloadIcon } from './Icons';
|
||||
import { StockBadge } from './StockBadge';
|
||||
|
||||
interface AdsPerformanceProps {
|
||||
data: CombinedKPIs[];
|
||||
@@ -10,6 +11,7 @@ interface AdsPerformanceProps {
|
||||
eu: Map<string, number>;
|
||||
uk: Map<string, number>;
|
||||
};
|
||||
stockMap?: Map<string, number>;
|
||||
}
|
||||
|
||||
type SortKey = keyof CombinedKPIs | 'acos' | 'roas' | 'tacos' | 'ctr' | 'cpc' | 'cvrUnits';
|
||||
@@ -34,7 +36,7 @@ const Top50Badge: React.FC<{ rank: number; label?: string; theme?: 'amber' | 'bl
|
||||
);
|
||||
};
|
||||
|
||||
const AdsPerformance: React.FC<AdsPerformanceProps> = ({ data, filters, top50Ranking }) => {
|
||||
const AdsPerformance: React.FC<AdsPerformanceProps> = ({ data, filters, top50Ranking, stockMap }) => {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [showOnlyTop50, setShowOnlyTop50] = useState(false);
|
||||
const [top50Mode, setTop50Mode] = useState<'eu' | 'uk'>('eu');
|
||||
@@ -404,6 +406,9 @@ const AdsPerformance: React.FC<AdsPerformanceProps> = ({ data, filters, top50Ran
|
||||
<Top50Badge key={i} rank={r.rank} label={r.label} theme={r.theme} />
|
||||
))}
|
||||
<span className="text-white font-bold text-sm tracking-tight">{row.asin}</span>
|
||||
{stockMap && (
|
||||
<StockBadge stock={stockMap.get(row.sku?.replace(/(DE|EN)$/i, ''))} />
|
||||
)}
|
||||
</div>
|
||||
<span className="text-[10px] uppercase font-black text-indigo-400 tracking-tighter">SKU: {row.sku}</span>
|
||||
<span className="text-[10px] text-slate-400 truncate max-w-[220px] leading-tight" title={row.title}>{row.title}</span>
|
||||
|
||||
+11
-2
@@ -5,11 +5,13 @@ import {
|
||||
import { SalesRecord, PivotRow, AdsRecord, CombinedKPIs } from '../types';
|
||||
import { pivotSalesData, generateXLSX, aggregateForTimeSeries, aggregateForComparisonTimeSeries, applyPanEUGrouping } from '../services/dataProcessor';
|
||||
import { DownloadIcon, FunnelIcon, CloseIcon, ChartIcon, TrendingIcon } from './Icons';
|
||||
import { StockBadge } from './StockBadge';
|
||||
|
||||
interface DataGridProps {
|
||||
data: SalesRecord[] | CombinedKPIs[];
|
||||
hasCustomerFilter: boolean;
|
||||
adsData?: AdsRecord[];
|
||||
stockMap?: Map<string, number>;
|
||||
}
|
||||
|
||||
type SortConfig = {
|
||||
@@ -226,7 +228,7 @@ const ExpandableChartCard: React.FC<{ title: string; children: React.ReactNode;
|
||||
};
|
||||
|
||||
|
||||
const DataGrid: React.FC<DataGridProps> = ({ data, hasCustomerFilter, adsData = [] }) => {
|
||||
const DataGrid: React.FC<DataGridProps> = ({ data, hasCustomerFilter, adsData, stockMap }) => {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [sortConfig, setSortConfig] = useState<SortConfig>({ key: null, direction: 'desc' });
|
||||
const [showChart, setShowChart] = useState(true);
|
||||
@@ -1081,7 +1083,14 @@ const DataGrid: React.FC<DataGridProps> = ({ data, hasCustomerFilter, adsData =
|
||||
<td key={dim} className="px-4 py-3 font-medium text-slate-200 break-words max-w-xs">
|
||||
{dim === 'title'
|
||||
? <div className="line-clamp-2" title={row.title}>{row.title || '-'}</div>
|
||||
: (row[dim as keyof PivotRow] as string) || '-'
|
||||
: (dim === 'sku' || dim === 'asin')
|
||||
? <div className="flex items-center gap-2">
|
||||
<span>{(row[dim as keyof PivotRow] as string) || '-'}</span>
|
||||
{stockMap && dim === 'sku' && (
|
||||
<StockBadge stock={stockMap.get((row[dim] as string)?.replace(/(DE|EN)$/i, ''))} />
|
||||
)}
|
||||
</div>
|
||||
: (row[dim as keyof PivotRow] as string) || '-'
|
||||
}
|
||||
</td>
|
||||
))}
|
||||
|
||||
@@ -2,6 +2,8 @@ import React, { useMemo, useState, useEffect, useCallback } from 'react';
|
||||
import * as XLSX from 'xlsx';
|
||||
import { ProductForecastData, FilterState } from '../types';
|
||||
import { DownloadIcon } from './Icons';
|
||||
import { StockBadge } from './StockBadge';
|
||||
import { StockBadge } from './StockBadge';
|
||||
import {
|
||||
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer,
|
||||
LineChart, Line, Legend, ComposedChart, Area
|
||||
@@ -13,7 +15,9 @@ interface ForecastViewProps {
|
||||
top50Ranking?: {
|
||||
eu: Map<string, number>;
|
||||
uk: Map<string, number>;
|
||||
stockMap?: Map<string, number>;
|
||||
};
|
||||
stockMap?: Map<string, number>;
|
||||
}
|
||||
|
||||
const MONTH_ORDER = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
@@ -29,7 +33,7 @@ const Top50Badge: React.FC<{ rank: number; label: string; theme?: 'amber' | 'ind
|
||||
);
|
||||
};
|
||||
|
||||
const ForecastView: React.FC<ForecastViewProps> = ({ data, filters, top50Ranking }) => {
|
||||
const ForecastView: React.FC<ForecastViewProps> = ({ data, filters, top50Ranking, stockMap, stockMap }) => {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [showOnlyTop50, setShowOnlyTop50] = useState(false);
|
||||
const [top50Mode, setTop50Mode] = useState<'eu' | 'uk'>('eu');
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
interface StockBadgeProps {
|
||||
stock: number | undefined;
|
||||
}
|
||||
|
||||
export const StockBadge: React.FC<StockBadgeProps> = ({ stock }) => {
|
||||
if (stock === undefined) return null;
|
||||
|
||||
const isLow = stock < 50;
|
||||
const isOut = stock <= 0;
|
||||
|
||||
let colorClass = "bg-emerald-500/20 text-emerald-400 border-emerald-500/30";
|
||||
let icon = "📦";
|
||||
|
||||
if (isOut) {
|
||||
colorClass = "bg-rose-500/20 text-rose-400 border-rose-500/30";
|
||||
icon = "❌";
|
||||
} else if (isLow) {
|
||||
colorClass = "bg-amber-500/20 text-amber-400 border-amber-500/30";
|
||||
icon = "⚠️";
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[10px] font-black border uppercase tracking-tighter shadow-sm ${colorClass}`}
|
||||
title={`Total Stock (GMBH): ${stock}`}
|
||||
>
|
||||
<span className="text-xs">{icon}</span>
|
||||
<span>{stock.toLocaleString('de-DE')}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import React, { useMemo, useState, useEffect, useCallback } from 'react';
|
||||
import * as XLSX from 'xlsx';
|
||||
import { CombinedKPIs } from '../types';
|
||||
import { pivotWeeklySalesData, WeeklyPivotRow } from '../services/dataProcessor';
|
||||
import { StockBadge } from './StockBadge';
|
||||
|
||||
interface WeeklyGridProps {
|
||||
data: CombinedKPIs[];
|
||||
@@ -10,6 +11,7 @@ interface WeeklyGridProps {
|
||||
uk: Map<string, number>;
|
||||
};
|
||||
onDrillDown?: (sku: string) => void;
|
||||
stockMap?: Map<string, number>;
|
||||
}
|
||||
|
||||
type SortConfig = {
|
||||
@@ -51,7 +53,7 @@ const Top50Badge: React.FC<{ rank: number; label?: string; theme?: 'amber' | 'bl
|
||||
);
|
||||
};
|
||||
|
||||
const WeeklyGrid: React.FC<WeeklyGridProps> = ({ data, top50Ranking, onDrillDown }) => {
|
||||
const WeeklyGrid: React.FC<WeeklyGridProps> = ({ data, top50Ranking, onDrillDown, stockMap }) => {
|
||||
// Pivot data - memoized
|
||||
const { rows, weeks: allWeeks } = useMemo(() => pivotWeeklySalesData(data), [data]);
|
||||
|
||||
@@ -515,6 +517,9 @@ const WeeklyGrid: React.FC<WeeklyGridProps> = ({ data, top50Ranking, onDrillDown
|
||||
{row.sku || '-'}
|
||||
</span>
|
||||
<span className="text-[10px] font-bold text-slate-500 bg-slate-800 px-1.5 py-0.5 rounded border border-white/5">{row.asin}</span>
|
||||
{stockMap && (
|
||||
<StockBadge stock={stockMap.get(row.sku?.replace(/(DE|EN)$/i, ''))} />
|
||||
)}
|
||||
</div>
|
||||
<span className="text-[11px] text-white/70 truncate w-[210px] leading-tight mb-1" title={row.title}>{row.title}</span>
|
||||
<span className="text-[9px] text-fuchsia-400/80 font-bold uppercase tracking-widest">{row.line}</span>
|
||||
|
||||
Reference in New Issue
Block a user