mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 15:45:24 +02:00
Forecast View: Added Annual Forecast and Annual Fulfillment cards
This commit is contained in:
+51
-12
@@ -82,18 +82,33 @@ const ForecastView: React.FC<ForecastViewProps> = ({ data, filters, top50Ranking
|
|||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
const globalSummary = useMemo(() => {
|
const globalSummary = useMemo(() => {
|
||||||
let totalForecast = 0;
|
let periodForecast = 0;
|
||||||
let totalActual = 0;
|
let periodActual = 0;
|
||||||
|
let annualForecast = 0;
|
||||||
|
let annualActual = 0;
|
||||||
|
|
||||||
data.forEach(p => {
|
data.forEach(p => {
|
||||||
|
annualForecast += p.annualForecast;
|
||||||
p.monthlyData.forEach(md => {
|
p.monthlyData.forEach(md => {
|
||||||
|
annualActual += md.actualUnits;
|
||||||
if (activeMonths.includes(md.month)) {
|
if (activeMonths.includes(md.month)) {
|
||||||
totalForecast += md.forecastUnits;
|
periodForecast += md.forecastUnits;
|
||||||
totalActual += md.actualUnits;
|
periodActual += md.actualUnits;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const fulfillment = totalForecast > 0 ? (totalActual / totalForecast) * 100 : 0;
|
|
||||||
return { totalForecast, totalActual, fulfillment };
|
const periodFulfillment = periodForecast > 0 ? (periodActual / periodForecast) * 100 : 0;
|
||||||
|
const annualFulfillment = annualForecast > 0 ? (annualActual / annualForecast) * 100 : 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
periodForecast,
|
||||||
|
periodActual,
|
||||||
|
periodFulfillment,
|
||||||
|
annualForecast,
|
||||||
|
annualActual,
|
||||||
|
annualFulfillment
|
||||||
|
};
|
||||||
}, [data, activeMonths]);
|
}, [data, activeMonths]);
|
||||||
|
|
||||||
const filteredProducts = useMemo(() => {
|
const filteredProducts = useMemo(() => {
|
||||||
@@ -137,12 +152,21 @@ const ForecastView: React.FC<ForecastViewProps> = ({ data, filters, top50Ranking
|
|||||||
<div className="p-6 space-y-6 max-w-7xl mx-auto animate-fade-in pb-24">
|
<div className="p-6 space-y-6 max-w-7xl mx-auto animate-fade-in pb-24">
|
||||||
|
|
||||||
{/* Summary Cards */}
|
{/* Summary Cards */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
|
||||||
|
{/* Annual Forecast Card */}
|
||||||
|
<div className="bg-slate-900/50 border border-slate-800 rounded-2xl p-4 shadow-xl relative overflow-hidden group">
|
||||||
|
<div className="absolute top-0 right-0 w-32 h-32 bg-slate-500/5 rounded-full -mr-16 -mt-16 blur-3xl"></div>
|
||||||
|
<h3 className="text-[10px] font-black text-slate-500 uppercase tracking-widest mb-1">Annual Forecast Total</h3>
|
||||||
|
<div className="text-2xl font-black text-slate-300">
|
||||||
|
{globalSummary.annualForecast.toLocaleString('de-DE')} <span className="text-xs font-medium text-slate-600">Units</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="bg-slate-900 border border-border rounded-2xl p-4 shadow-xl relative overflow-hidden group">
|
<div className="bg-slate-900 border border-border rounded-2xl p-4 shadow-xl relative overflow-hidden group">
|
||||||
<div className="absolute top-0 right-0 w-32 h-32 bg-indigo-500/10 rounded-full -mr-16 -mt-16 blur-3xl group-hover:bg-indigo-500/20 transition-all"></div>
|
<div className="absolute top-0 right-0 w-32 h-32 bg-indigo-500/10 rounded-full -mr-16 -mt-16 blur-3xl group-hover:bg-indigo-500/20 transition-all"></div>
|
||||||
<h3 className="text-[10px] font-black text-slate-500 uppercase tracking-widest mb-1">Total Forecast (Period)</h3>
|
<h3 className="text-[10px] font-black text-slate-500 uppercase tracking-widest mb-1">Total Forecast (Period)</h3>
|
||||||
<div className="text-2xl font-black text-white">
|
<div className="text-2xl font-black text-white">
|
||||||
{globalSummary.totalForecast.toLocaleString('de-DE')} <span className="text-xs font-medium text-slate-500">Units</span>
|
{globalSummary.periodForecast.toLocaleString('de-DE')} <span className="text-xs font-medium text-slate-500">Units</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -150,7 +174,7 @@ const ForecastView: React.FC<ForecastViewProps> = ({ data, filters, top50Ranking
|
|||||||
<div className="absolute top-0 right-0 w-32 h-32 bg-emerald-500/10 rounded-full -mr-16 -mt-16 blur-3xl group-hover:bg-emerald-500/20 transition-all"></div>
|
<div className="absolute top-0 right-0 w-32 h-32 bg-emerald-500/10 rounded-full -mr-16 -mt-16 blur-3xl group-hover:bg-emerald-500/20 transition-all"></div>
|
||||||
<h3 className="text-[10px] font-black text-slate-500 uppercase tracking-widest mb-1">Total Actual Sales (Period)</h3>
|
<h3 className="text-[10px] font-black text-slate-500 uppercase tracking-widest mb-1">Total Actual Sales (Period)</h3>
|
||||||
<div className="text-2xl font-black text-emerald-400">
|
<div className="text-2xl font-black text-emerald-400">
|
||||||
{globalSummary.totalActual.toLocaleString('de-DE')} <span className="text-xs font-medium text-slate-500">Units</span>
|
{globalSummary.periodActual.toLocaleString('de-DE')} <span className="text-xs font-medium text-slate-500">Units</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -158,15 +182,30 @@ const ForecastView: React.FC<ForecastViewProps> = ({ data, filters, top50Ranking
|
|||||||
<div className="absolute top-0 right-0 w-32 h-32 bg-indigo-500/20 rounded-full -mr-16 -mt-16 blur-3xl"></div>
|
<div className="absolute top-0 right-0 w-32 h-32 bg-indigo-500/20 rounded-full -mr-16 -mt-16 blur-3xl"></div>
|
||||||
<h3 className="text-[10px] font-black text-indigo-400 uppercase tracking-widest mb-1">Fulfillment (Period)</h3>
|
<h3 className="text-[10px] font-black text-indigo-400 uppercase tracking-widest mb-1">Fulfillment (Period)</h3>
|
||||||
<div className="flex items-baseline gap-2">
|
<div className="flex items-baseline gap-2">
|
||||||
<div className={`text-2xl font-black ${globalSummary.fulfillment >= 100 ? 'text-emerald-400' : 'text-indigo-400'}`}>
|
<div className={`text-2xl font-black ${globalSummary.periodFulfillment >= 100 ? 'text-emerald-400' : 'text-indigo-400'}`}>
|
||||||
{globalSummary.fulfillment.toFixed(1)}%
|
{globalSummary.periodFulfillment.toFixed(1)}%
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Fulfillment Progress Bar */}
|
{/* Fulfillment Progress Bar */}
|
||||||
<div className="mt-2 w-full h-1.5 bg-slate-800 rounded-full overflow-hidden">
|
<div className="mt-2 w-full h-1.5 bg-slate-800 rounded-full overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className="h-full bg-gradient-to-r from-indigo-500 to-fuchsia-500 shadow-[0_0_8px_rgba(99,102,241,0.5)] transition-all duration-1000"
|
className="h-full bg-gradient-to-r from-indigo-500 to-fuchsia-500 shadow-[0_0_8px_rgba(99,102,241,0.5)] transition-all duration-1000"
|
||||||
style={{ width: `${Math.min(100, globalSummary.fulfillment)}%` }}
|
style={{ width: `${Math.min(100, globalSummary.periodFulfillment)}%` }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Annual Fulfillment Card */}
|
||||||
|
<div className="bg-slate-900/50 border border-slate-800 rounded-2xl p-4 shadow-xl relative overflow-hidden group">
|
||||||
|
<div className="absolute top-0 right-0 w-32 h-32 bg-slate-500/5 rounded-full -mr-16 -mt-16 blur-3xl"></div>
|
||||||
|
<h3 className="text-[10px] font-black text-slate-500 uppercase tracking-widest mb-1">Annual Fulfillment %</h3>
|
||||||
|
<div className="text-2xl font-black text-slate-400">
|
||||||
|
{globalSummary.annualFulfillment.toFixed(1)}%
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 w-full h-1.5 bg-slate-800/50 rounded-full overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="h-full bg-slate-700 transition-all duration-1000"
|
||||||
|
style={{ width: `${Math.min(100, globalSummary.annualFulfillment)}%` }}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user