feat: use FY labels in generation sheet based on COD date
- Show FY 2026-27 instead of Y1 based on cod_year - Show month names (Apr, May, etc.) instead of M1, M2 - Show actual dates (Apr 1, etc.) instead of D1, D2 - Pass codYear from scenario inputs to WorkbookView
This commit is contained in:
parent
0ada193bb0
commit
7062688397
2 changed files with 30 additions and 6 deletions
|
|
@ -229,6 +229,14 @@ export default function ScenarioPage() {
|
||||||
kpis={kpis}
|
kpis={kpis}
|
||||||
debtScheduleJson={scenario?.debt_schedule_json ?? null}
|
debtScheduleJson={scenario?.debt_schedule_json ?? null}
|
||||||
activeSheet={activeSheet}
|
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" ? (
|
) : scenario?.status === "failed" ? (
|
||||||
<div className="border border-red-200 rounded-lg p-6 text-sm max-w-lg">
|
<div className="border border-red-200 rounded-lg p-6 text-sm max-w-lg">
|
||||||
|
|
|
||||||
|
|
@ -533,12 +533,25 @@ interface HourlyData {
|
||||||
wind_hourly?: number[];
|
wind_hourly?: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function HourlyGenerationSheet({ hourly }: { hourly: HourlyData }) {
|
function HourlyGenerationSheet({ hourly, codYear }: { hourly: HourlyData; codYear?: number }) {
|
||||||
const { solar_hourly, wind_hourly } = hourly;
|
const { solar_hourly, wind_hourly } = hourly;
|
||||||
const hasSolar = solar_hourly && solar_hourly.length > 0;
|
const hasSolar = solar_hourly && solar_hourly.length > 0;
|
||||||
const hasWind = wind_hourly && wind_hourly.length > 0;
|
const hasWind = wind_hourly && wind_hourly.length > 0;
|
||||||
const hasData = hasSolar || hasWind;
|
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
|
// Expand state: which years/months/days are expanded
|
||||||
const [expandedYears, setExpandedYears] = useState<Set<number>>(new Set());
|
const [expandedYears, setExpandedYears] = useState<Set<number>>(new Set());
|
||||||
const [expandedMonths, setExpandedMonths] = useState<Set<string>>(new Set());
|
const [expandedMonths, setExpandedMonths] = useState<Set<string>>(new Set());
|
||||||
|
|
@ -623,6 +636,7 @@ function HourlyGenerationSheet({ hourly }: { hourly: HourlyData }) {
|
||||||
const isYearExpanded = expandedYears.has(year);
|
const isYearExpanded = expandedYears.has(year);
|
||||||
const solarYr = hasSolar ? computeYearTotal(year, true) : 0;
|
const solarYr = hasSolar ? computeYearTotal(year, true) : 0;
|
||||||
const windYr = hasWind ? computeYearTotal(year, false) : 0;
|
const windYr = hasWind ? computeYearTotal(year, false) : 0;
|
||||||
|
const fyLabel = getFyLabel(i);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={year}>
|
<div key={year}>
|
||||||
|
|
@ -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"
|
className="w-full flex items-center gap-2 py-1.5 hover:bg-accent/40 rounded px-2 text-left font-medium"
|
||||||
>
|
>
|
||||||
<span className="text-[10px] w-4">{isYearExpanded ? "▼" : "▶"}</span>
|
<span className="text-[10px] w-4">{isYearExpanded ? "▼" : "▶"}</span>
|
||||||
<span className="w-12">Y{year}</span>
|
<span className="w-20 font-medium">{fyLabel}</span>
|
||||||
{hasSolar && <span className="text-orange-700 w-24">{Math.round(solarYr).toLocaleString()} MWh</span>}
|
{hasSolar && <span className="text-orange-700 w-24">{Math.round(solarYr).toLocaleString()} MWh</span>}
|
||||||
{hasWind && <span className="text-blue-700 w-24">{Math.round(windYr).toLocaleString()} MWh</span>}
|
{hasWind && <span className="text-blue-700 w-24">{Math.round(windYr).toLocaleString()} MWh</span>}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -644,6 +658,7 @@ function HourlyGenerationSheet({ hourly }: { hourly: HourlyData }) {
|
||||||
const solarMo = hasSolar ? computeMonthTotal(year, month, true) : 0;
|
const solarMo = hasSolar ? computeMonthTotal(year, month, true) : 0;
|
||||||
const windMo = hasWind ? computeMonthTotal(year, month, false) : 0;
|
const windMo = hasWind ? computeMonthTotal(year, month, false) : 0;
|
||||||
const daysInMonth = MONTH_DAYS[month - 1];
|
const daysInMonth = MONTH_DAYS[month - 1];
|
||||||
|
const monthLabel = getMonthLabel(month, i);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={monthKey} className="ml-4">
|
<div key={monthKey} className="ml-4">
|
||||||
|
|
@ -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"
|
className="w-full flex items-center gap-2 py-1 hover:bg-accent/30 rounded px-2 text-left"
|
||||||
>
|
>
|
||||||
<span className="text-[10px] w-4">{isMonthExpanded ? "▼" : "▶"}</span>
|
<span className="text-[10px] w-4">{isMonthExpanded ? "▼" : "▶"}</span>
|
||||||
<span className="w-12 text-muted-foreground">M{month}</span>
|
<span className="w-12 text-muted-foreground">{monthLabel}</span>
|
||||||
{hasSolar && <span className="text-orange-700/80 w-24">{Math.round(solarMo).toLocaleString()} MWh</span>}
|
{hasSolar && <span className="text-orange-700/80 w-24">{Math.round(solarMo).toLocaleString()} MWh</span>}
|
||||||
{hasWind && <span className="text-blue-700/80 w-24">{Math.round(windMo).toLocaleString()} MWh</span>}
|
{hasWind && <span className="text-blue-700/80 w-24">{Math.round(windMo).toLocaleString()} MWh</span>}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -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"
|
className="w-full flex items-center gap-2 py-0.5 hover:bg-accent/20 rounded px-2 text-left"
|
||||||
>
|
>
|
||||||
<span className="text-[10px] w-4">{isDayExpanded ? "▼" : "▶"}</span>
|
<span className="text-[10px] w-4">{isDayExpanded ? "▼" : "▶"}</span>
|
||||||
<span className="w-12 text-muted-foreground/80">D{day}</span>
|
<span className="w-12 text-muted-foreground/80">{monthLabel} {day}</span>
|
||||||
{hasSolar && <span className="text-orange-600/70 w-20">{Math.round(solarDy).toLocaleString()}</span>}
|
{hasSolar && <span className="text-orange-600/70 w-20">{Math.round(solarDy).toLocaleString()}</span>}
|
||||||
{hasWind && <span className="text-blue-600/70 w-20">{Math.round(windDy).toLocaleString()}</span>}
|
{hasWind && <span className="text-blue-600/70 w-20">{Math.round(windDy).toLocaleString()}</span>}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -797,9 +812,10 @@ interface Props {
|
||||||
kpis: KpiSummary;
|
kpis: KpiSummary;
|
||||||
debtScheduleJson: string | null;
|
debtScheduleJson: string | null;
|
||||||
activeSheet: string;
|
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({
|
const { data: stmts } = useQuery({
|
||||||
queryKey: ["statements", scenarioId],
|
queryKey: ["statements", scenarioId],
|
||||||
queryFn: () => getStatements(scenarioId),
|
queryFn: () => getStatements(scenarioId),
|
||||||
|
|
@ -847,7 +863,7 @@ export function WorkbookView({ scenarioId, kpis, debtScheduleJson, activeSheet }
|
||||||
)}
|
)}
|
||||||
{activeSheet === "irr" && <IrrSheet kpis={kpis} />}
|
{activeSheet === "irr" && <IrrSheet kpis={kpis} />}
|
||||||
{activeSheet === "generation" && (stmts?.solar_hourly || stmts?.wind_hourly ? (
|
{activeSheet === "generation" && (stmts?.solar_hourly || stmts?.wind_hourly ? (
|
||||||
<HourlyGenerationSheet hourly={{ solar_hourly: stmts?.solar_hourly, wind_hourly: stmts?.wind_hourly }} />
|
<HourlyGenerationSheet hourly={{ solar_hourly: stmts?.solar_hourly, wind_hourly: stmts?.wind_hourly }} codYear={codYear} />
|
||||||
) : generation.length > 0 ? (
|
) : generation.length > 0 ? (
|
||||||
<HorizontalTable years={genYears} rows={buildGenerationRows(generation)} unit="MWh / ₹Cr" />
|
<HorizontalTable years={genYears} rows={buildGenerationRows(generation)} unit="MWh / ₹Cr" />
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue