mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 13:45:23 +02:00
Fix syntax error in ForecastView.tsx: Close td tag and remove nested structures
This commit is contained in:
+72
-140
@@ -94,48 +94,49 @@ const ForecastRow: React.FC<{
|
||||
<VendorStockBadge asin={item.asin} vendorStockMap={vendorStockMap} mode={top50Mode} avgWeeklySales={item.avgWeeklySales} />
|
||||
</div>
|
||||
</div>
|
||||
{/* Forecast (Period) */}
|
||||
<td className="px-6 py-4 text-center">
|
||||
<span className="text-sm font-bold text-slate-400">
|
||||
{forecastPeriod.toLocaleString('de-DE')}
|
||||
</span>
|
||||
</td>
|
||||
</td>
|
||||
{/* Forecast (Period) */}
|
||||
<td className="px-6 py-4 text-center">
|
||||
<span className="text-sm font-bold text-slate-400">
|
||||
{forecastPeriod.toLocaleString('de-DE')}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
{/* Actual Units Sales 2026 */}
|
||||
<td className="px-6 py-4 text-center">
|
||||
<span className="text-base font-black text-emerald-400">
|
||||
{actualUnits.toLocaleString('de-DE')}
|
||||
</span>
|
||||
</td>
|
||||
{/* Actual Units Sales 2026 */}
|
||||
<td className="px-6 py-4 text-center">
|
||||
<span className="text-base font-black text-emerald-400">
|
||||
{actualUnits.toLocaleString('de-DE')}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
{/* FC 26 Achievement (Annual %) */}
|
||||
<td className="px-6 py-4 text-center">
|
||||
<div className="flex flex-col items-center justify-center gap-1">
|
||||
<div className="px-3 py-1 rounded bg-slate-800 border border-slate-700 text-xs font-bold text-white shadow-sm">
|
||||
{fcAchievement.toFixed(1)}%
|
||||
</div>
|
||||
<span className="text-[9px] font-bold text-slate-500 uppercase tracking-tight">
|
||||
OF ANNUAL {Math.round(annualForecast / 1000)}K
|
||||
{/* FC 26 Achievement (Annual %) */}
|
||||
<td className="px-6 py-4 text-center">
|
||||
<div className="flex flex-col items-center justify-center gap-1">
|
||||
<div className="px-3 py-1 rounded bg-slate-800 border border-slate-700 text-xs font-bold text-white shadow-sm">
|
||||
{fcAchievement.toFixed(1)}%
|
||||
</div>
|
||||
<span className="text-[9px] font-bold text-slate-500 uppercase tracking-tight">
|
||||
OF ANNUAL {Math.round(annualForecast / 1000)}K
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{/* YTD Achievement (Period %) */}
|
||||
<td className="px-6 py-4 align-middle">
|
||||
<div className="flex flex-col gap-1 w-full max-w-[200px] mx-auto">
|
||||
<div className="flex justify-between items-end mb-1">
|
||||
<span className={`text-xs font-black ${ytdAchievement >= 100 ? 'text-emerald-400' : 'text-slate-300'}`}>
|
||||
{ytdAchievement.toFixed(1)}%
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{/* YTD Achievement (Period %) */}
|
||||
<td className="px-6 py-4 align-middle">
|
||||
<div className="flex flex-col gap-1 w-full max-w-[200px] mx-auto">
|
||||
<div className="flex justify-between items-end mb-1">
|
||||
<span className={`text-xs font-black ${ytdAchievement >= 100 ? 'text-emerald-400' : 'text-slate-300'}`}>
|
||||
{ytdAchievement.toFixed(1)}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-2 w-full bg-slate-800 rounded-full overflow-hidden">
|
||||
<div
|
||||
className={`h-full rounded-full transition-all duration-500 ${achievementColor}`}
|
||||
style={{ width: `${Math.min(100, ytdAchievement)}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="h-2 w-full bg-slate-800 rounded-full overflow-hidden">
|
||||
<div
|
||||
className={`h-full rounded-full transition-all duration-500 ${achievementColor}`}
|
||||
style={{ width: `${Math.min(100, ytdAchievement)}%` }}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
@@ -215,110 +216,41 @@ const ForecastView: React.FC<ForecastViewProps> = ({
|
||||
}, [finalFilteredData]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6 animate-fade-in">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div className="bg-slate-900/50 backdrop-blur-xl border border-white/10 p-5 rounded-2xl shadow-xl">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="p-2 bg-indigo-500/20 rounded-lg"><ChartIcon /></div>
|
||||
<span className="text-[10px] font-black text-indigo-400 uppercase tracking-widest">Global Actuals</span>
|
||||
</div>
|
||||
<div className="text-3xl font-black text-white tabular-nums">{(globalSummary.actualUnits || 0).toLocaleString('de-DE')}</div>
|
||||
</div>
|
||||
<div className="bg-slate-900/50 backdrop-blur-xl border border-white/10 p-5 rounded-2xl shadow-xl">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="p-2 bg-emerald-500/20 rounded-lg"><TrendingIcon /></div>
|
||||
<span className="text-[10px] font-black text-emerald-400 uppercase tracking-widest">Global Forecast</span>
|
||||
</div>
|
||||
<div className="text-3xl font-black text-emerald-400 tabular-nums">{(globalSummary.forecastUnits || 0).toLocaleString('de-DE')}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-slate-900 border border-white/10 rounded-2xl overflow-hidden shadow-2xl flex-1 flex flex-col">
|
||||
<div className="p-6 border-b border-white/5 flex flex-col md:flex-row justify-between gap-4 bg-slate-800/20">
|
||||
<h3 className="text-lg font-bold text-white uppercase tracking-tight">Product Performance Comparison</h3>
|
||||
<div className="flex items-center gap-4">
|
||||
{top50Ranking && (top50Ranking.eu.size > 0 || top50Ranking.uk.size > 0) && (
|
||||
<div className="flex bg-slate-950/50 p-1 rounded-xl border border-white/10 shadow-sm">
|
||||
<button
|
||||
onClick={() => setShowOnlyTop50(!showOnlyTop50)}
|
||||
className={`flex items-center gap-2 px-3 py-1.5 rounded-lg text-xs font-bold transition-all ${showOnlyTop50 ? 'bg-gradient-to-r from-amber-500 to-orange-500 text-white shadow-lg' : 'text-slate-400 hover:text-amber-400'}`}
|
||||
>
|
||||
<span className="text-sm">🏆</span>
|
||||
Top 50 ({top50Mode.toUpperCase()})
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="bg-slate-950 border border-white/10 rounded-xl px-4 py-2 text-sm text-white focus:outline-none w-64"
|
||||
/>
|
||||
<button onClick={handleExportExcel} className="p-2 bg-slate-800 hover:bg-slate-700 text-white rounded-xl border border-white/5 transition-all"><DownloadIcon /></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto custom-scrollbar">
|
||||
<table className="w-full text-left border-collapse">
|
||||
<thead className="bg-slate-800/40">
|
||||
<tr>
|
||||
<th className="px-6 py-4 font-black text-slate-400 uppercase tracking-widest text-[10px] min-w-[300px]">
|
||||
<div className="flex items-center gap-2">
|
||||
<span>Product Details</span>
|
||||
<div className="flex items-center gap-1 scale-75 origin-left">
|
||||
<InColumnStockFilter
|
||||
currentFilters={stockFilter}
|
||||
onFilterChange={onStockFilterChange}
|
||||
title="Warehouse Stock"
|
||||
/>
|
||||
<InColumnStockFilter
|
||||
currentFilters={vendorStockFilter}
|
||||
onFilterChange={onVendorStockFilterChange}
|
||||
title="Vendor Stock"
|
||||
options={[
|
||||
'Out of Stock (0)',
|
||||
'In Stock (>0)',
|
||||
'In Stock (>20)',
|
||||
'Low Stock (<10)',
|
||||
]}
|
||||
<div className="flex-1 overflow-hidden flex flex-col min-h-0 bg-slate-950/50">
|
||||
<div className="flex-1 overflow-auto min-h-0 custom-scrollbar relative">
|
||||
<table className="w-full text-left border-collapse relative">
|
||||
<thead className="sticky top-0 z-20 bg-slate-950 shadow-sm text-xs font-bold text-slate-400 uppercase tracking-wider">
|
||||
<tr>
|
||||
<th className="px-6 py-4 font-black text-white w-[35%]">Product Info</th>
|
||||
<th className="px-6 py-4 text-center w-[12%]">Forecast (Period)</th>
|
||||
<th className="px-6 py-4 text-center w-[12%]">Actual Units Sales 2026</th>
|
||||
<th className="px-6 py-4 text-center w-[15%]">FC 26 Achievement</th>
|
||||
<th className="px-6 py-4 text-center w-[26%]">YTD Achievement</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-800/50">
|
||||
{data.map(item => (
|
||||
<ForecastRow
|
||||
key={item.asin}
|
||||
item={item}
|
||||
activeMonths={filters.month.length > 0 ? filters.month : MONTH_ORDER}
|
||||
top50Ranking={top50Ranking}
|
||||
top50Mode={top50Mode}
|
||||
stockMap={stockMap}
|
||||
vendorStockMap={vendorStockMap}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="flex-1 overflow-auto min-h-0 custom-scrollbar relative">
|
||||
<table className="w-full text-left border-collapse relative">
|
||||
<thead className="sticky top-0 z-20 bg-slate-950 shadow-sm text-xs font-bold text-slate-400 uppercase tracking-wider">
|
||||
<tr>
|
||||
<th className="px-6 py-4 font-black text-white w-[35%]">Product Info</th>
|
||||
<th className="px-6 py-4 text-center w-[12%]">Forecast (Period)</th>
|
||||
<th className="px-6 py-4 text-center w-[12%]">Actual Units Sales 2026</th>
|
||||
<th className="px-6 py-4 text-center w-[15%]">FC 26 Achievement</th>
|
||||
<th className="px-6 py-4 text-center w-[26%]">YTD Achievement</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-800/50">
|
||||
{finalFilteredData.map(item => (
|
||||
<ForecastRow
|
||||
key={item.asin}
|
||||
item={item}
|
||||
activeMonths={filters.month.length > 0 ? filters.month : MONTH_ORDER}
|
||||
top50Ranking={top50Ranking}
|
||||
top50Mode={top50Mode}
|
||||
stockMap={stockMap}
|
||||
vendorStockMap={vendorStockMap}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{data.length === 0 && (
|
||||
<div className="flex flex-col items-center justify-center py-20 text-slate-500">
|
||||
<p className="text-lg">No forecast data found for current filters</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Filter Panel (In-Column Stock Filter is handled inside components if needed, or we can add a toolbar later) */}
|
||||
</div>
|
||||
);
|
||||
{finalFilteredData.length === 0 && (
|
||||
<div className="flex flex-col items-center justify-center py-20 text-slate-500">
|
||||
<p className="text-lg">No forecast data found for current filters</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ForecastView;
|
||||
export default ForecastView;
|
||||
|
||||
Reference in New Issue
Block a user