diff --git a/packages/web/app/scenarios/[id]/page.tsx b/packages/web/app/scenarios/[id]/page.tsx index 8dfe083..2787a3a 100644 --- a/packages/web/app/scenarios/[id]/page.tsx +++ b/packages/web/app/scenarios/[id]/page.tsx @@ -229,6 +229,14 @@ export default function ScenarioPage() { kpis={kpis} debtScheduleJson={scenario?.debt_schedule_json ?? null} activeSheet={activeSheet} + codYear={(() => { + try { + const inputs = scenario?.inputs_json ? JSON.parse(scenario.inputs_json) : {}; + return inputs?.project?.cod_year || inputs?.project?.cod_date + ? new Date(inputs.project.cod_date || inputs.project.cod_year + '-04-01').getFullYear() + : new Date().getFullYear(); + } catch { return new Date().getFullYear(); } + })()} /> ) : scenario?.status === "failed" ? (
diff --git a/packages/web/components/WorkbookView.tsx b/packages/web/components/WorkbookView.tsx index dceb32e..dcc578e 100644 --- a/packages/web/components/WorkbookView.tsx +++ b/packages/web/components/WorkbookView.tsx @@ -533,12 +533,25 @@ interface HourlyData { wind_hourly?: number[]; } -function HourlyGenerationSheet({ hourly }: { hourly: HourlyData }) { +function HourlyGenerationSheet({ hourly, codYear }: { hourly: HourlyData; codYear?: number }) { const { solar_hourly, wind_hourly } = hourly; const hasSolar = solar_hourly && solar_hourly.length > 0; const hasWind = wind_hourly && wind_hourly.length > 0; const hasData = hasSolar || hasWind; + // FY labels: if COD is April 2026, FY 2026-27 = Year 1 + const getFyLabel = (yearIndex: number) => { + const startYear = codYear || new Date().getFullYear(); + const fyStart = startYear + yearIndex; + const fyEnd = fyStart + 1; + return `FY ${String(fyStart).slice(-2)}-${String(fyEnd).slice(-2)}`; + }; + + const getMonthLabel = (month: number, yearIndex: number) => { + const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + return monthNames[month - 1]; + }; + // Expand state: which years/months/days are expanded const [expandedYears, setExpandedYears] = useState>(new Set()); const [expandedMonths, setExpandedMonths] = useState>(new Set()); @@ -623,6 +636,7 @@ function HourlyGenerationSheet({ hourly }: { hourly: HourlyData }) { const isYearExpanded = expandedYears.has(year); const solarYr = hasSolar ? computeYearTotal(year, true) : 0; const windYr = hasWind ? computeYearTotal(year, false) : 0; + const fyLabel = getFyLabel(i); return (
@@ -631,7 +645,7 @@ function HourlyGenerationSheet({ hourly }: { hourly: HourlyData }) { className="w-full flex items-center gap-2 py-1.5 hover:bg-accent/40 rounded px-2 text-left font-medium" > {isYearExpanded ? "▼" : "▶"} - Y{year} + {fyLabel} {hasSolar && {Math.round(solarYr).toLocaleString()} MWh} {hasWind && {Math.round(windYr).toLocaleString()} MWh} @@ -644,6 +658,7 @@ function HourlyGenerationSheet({ hourly }: { hourly: HourlyData }) { const solarMo = hasSolar ? computeMonthTotal(year, month, true) : 0; const windMo = hasWind ? computeMonthTotal(year, month, false) : 0; const daysInMonth = MONTH_DAYS[month - 1]; + const monthLabel = getMonthLabel(month, i); return (
@@ -652,7 +667,7 @@ function HourlyGenerationSheet({ hourly }: { hourly: HourlyData }) { className="w-full flex items-center gap-2 py-1 hover:bg-accent/30 rounded px-2 text-left" > {isMonthExpanded ? "▼" : "▶"} - M{month} + {monthLabel} {hasSolar && {Math.round(solarMo).toLocaleString()} MWh} {hasWind && {Math.round(windMo).toLocaleString()} MWh} @@ -672,7 +687,7 @@ function HourlyGenerationSheet({ hourly }: { hourly: HourlyData }) { className="w-full flex items-center gap-2 py-0.5 hover:bg-accent/20 rounded px-2 text-left" > {isDayExpanded ? "▼" : "▶"} - D{day} + {monthLabel} {day} {hasSolar && {Math.round(solarDy).toLocaleString()}} {hasWind && {Math.round(windDy).toLocaleString()}} @@ -797,9 +812,10 @@ interface Props { kpis: KpiSummary; debtScheduleJson: string | null; activeSheet: string; + codYear?: number; } -export function WorkbookView({ scenarioId, kpis, debtScheduleJson, activeSheet }: Props) { +export function WorkbookView({ scenarioId, kpis, debtScheduleJson, activeSheet, codYear }: Props) { const { data: stmts } = useQuery({ queryKey: ["statements", scenarioId], queryFn: () => getStatements(scenarioId), @@ -847,7 +863,7 @@ export function WorkbookView({ scenarioId, kpis, debtScheduleJson, activeSheet } )} {activeSheet === "irr" && } {activeSheet === "generation" && (stmts?.solar_hourly || stmts?.wind_hourly ? ( - + ) : generation.length > 0 ? ( ) : (