diff --git a/components/ForecastView.tsx b/components/ForecastView.tsx
index 974c0a2..79cfc0d 100644
--- a/components/ForecastView.tsx
+++ b/components/ForecastView.tsx
@@ -48,69 +48,94 @@ const ForecastRow: React.FC<{
}
}
- const monthlySales = activeMonths.map(m => (item.monthlyData && item.monthlyData[m]?.units) || 0);
- const avgMonthly = monthlySales.length > 0 ? monthlySales.reduce((a, b) => a + b, 0) / activeMonths.length : 0;
- const peakMonthly = monthlySales.length > 0 ? Math.max(...monthlySales) : 0;
+ // Calculations for new columns
+ const forecastPeriod = item.forecastUnits || 0;
+ const actualUnits = item.actualUnits || 0;
+ const annualForecast = item.annualForecast || 0;
+
+ // FC 26 Achievement: Actual / Annual Forecast
+ const fcAchievement = annualForecast > 0 ? (actualUnits / annualForecast) * 100 : 0;
+
+ // YTD Achievement: Actual / Period Forecast
+ // If activeMonths is basically "Year To Date" (which it usually is in this view unless filtered), this logic holds.
+ // If user selects specific months, it becomes "Period Achievement".
+ const ytdAchievement = forecastPeriod > 0 ? (actualUnits / forecastPeriod) * 100 : 0;
+
+ let achievementColor = 'bg-slate-700';
+ if (ytdAchievement >= 100) achievementColor = 'bg-emerald-500';
+ else if (ytdAchievement >= 80) achievementColor = 'bg-emerald-400';
+ else if (ytdAchievement >= 50) achievementColor = 'bg-amber-400';
+ else achievementColor = 'bg-rose-500';
return (
-
+
+ {/* Product Info */}
-
-
+
+
{ranks.map((r, i) => (
))}
{item.sku}
-
- {item.title}
+
+ {item.title}
+
- {item.line}
- {stockMap && (
-
- )}
+
+ {item.line}
+
+ {/* WOC Indicator preserved */}
- |
-
- {(item.actualUnits || 0).toLocaleString('de-DE')}
- |
-
- {(item.forecastUnits || 0).toLocaleString('de-DE')}
- |
-
-
- {(Math.round(avgMonthly) || 0).toLocaleString('de-DE')}
- Peak: {(Math.round(peakMonthly) || 0).toLocaleString('de-DE')}
-
- |
-
-
-
- ({ name: m, units: (item.monthlyData && item.monthlyData[m]?.units) || 0 }))}>
-
-
-
-
- |
-
-
- = 80 ? 'text-emerald-400' : item.accuracy >= 50 ? 'text-amber-400' : 'text-rose-400'}`}>
- {item.accuracy}%
+ {/* Forecast (Period) */}
+
+
+ {forecastPeriod.toLocaleString('de-DE')}
-
- = 80 ? 'bg-emerald-500' : item.accuracy >= 50 ? 'bg-amber-500' : 'bg-rose-500'}`}
- style={{ width: `${item.accuracy}%` }}
- />
+ |
+
+ {/* Actual Units Sales 2026 */}
+
+
+ {actualUnits.toLocaleString('de-DE')}
+
+ |
+
+ {/* FC 26 Achievement (Annual %) */}
+
+
+
+ {fcAchievement.toFixed(1)}%
+
+
+ OF ANNUAL {Math.round(annualForecast / 1000)}K
+
-
- |
+ |
+
+ {/* YTD Achievement (Period %) */}
+
+
+
+ = 100 ? 'text-emerald-400' : 'text-slate-300'}`}>
+ {ytdAchievement.toFixed(1)}%
+
+
+
+
+ |
);
});
@@ -257,40 +282,43 @@ const ForecastView: React.FC = ({
'In Stock (>20)',
'Low Stock (<10)',
]}
- />
-
-
-
- Actuals |
- Forecast |
- Avg/Mo |
- Trend |
- Accuracy |
-
-
-
- {finalFilteredData.slice(0, displayCount).map(p => (
-
- ))}
-
-
- {displayCount < finalFilteredData.length && (
-
-
-
- )}
-
-
-
- );
+
+
+
+
+
+ | Product Info |
+ Forecast (Period) |
+ Actual Units Sales 2026 |
+ FC 26 Achievement |
+ YTD Achievement |
+
+
+
+ {data.map(item => (
+ 0 ? filters.month : MONTH_ORDER}
+ top50Ranking={top50Ranking}
+ top50Mode={top50Mode}
+ stockMap={stockMap}
+ vendorStockMap={vendorStockMap}
+ />
+ ))}
+
+
+
+ {data.length === 0 && (
+
+
No forecast data found for current filters
+
+ )}
+
+
+ {/* Filter Panel (In-Column Stock Filter is handled inside components if needed, or we can add a toolbar later) */}
+
+ );
};
-export default ForecastView;
+ export default ForecastView;