"use client"; import { useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, LineChart, Line, CartesianGrid, Legend, } from "recharts"; import { getStatements, type KpiSummary, type PnLRow, type CfsRow, type BsRow, type DebtYearRow, type GenerationRow, type IdcPhasing, } from "@/lib/api"; import { KpiCard } from "@/components/KpiCard"; // Horizontal table // --------------------------------------------------------------------------- interface TableRow { label: string; values: (number | null)[]; isBold?: boolean; isSeparator?: boolean; isHeader?: boolean; format?: (v: number | null) => string; indent?: boolean; collapsible?: boolean; isHighlight?: boolean; children?: TableRow[]; } function n1(v: number | null) { return v == null ? "—" : v.toFixed(1); } function n2(v: number | null) { return v == null ? "—" : v.toFixed(2); } function pct1(v: number | null) { return v == null ? "—" : `${(v * 100).toFixed(1)}%`; } function HorizontalTable({ years, rows, unit = "INR Cr", yearPrefix = "Y", }: { years: number[]; rows: TableRow[]; unit?: string; yearPrefix?: string; }) { const [expandedRows, setExpandedRows] = useState>(new Set()); const toggleRow = (idx: number) => { setExpandedRows((prev) => { const next = new Set(prev); if (next.has(idx)) { next.delete(idx); } else { next.add(idx); } return next; }); }; let rowIndex = 0; return (
{years.map((y) => ( ))} {rows.map((row, i) => { rowIndex = i; const isExpanded = expandedRows.has(i); if (row.isSeparator) { return ( ); } if (row.isHeader) { return ( ); } return ( <> {row.values.map((v, j) => ( ))} {row.collapsible && row.children && isExpanded && row.children.map((child, ci) => ( {child.values.map((v, j) => ( ))} ))} ); })}
Metric ({unit}) {yearPrefix}{y}
{row.label}
row.collapsible && toggleRow(i)} > {row.collapsible && ( {isExpanded ? "▼" : "▶"} )} {row.label} {row.format ? row.format(v) : n1(v)}
{child.label} {child.format ? child.format(v) : n1(v)}
); } // --------------------------------------------------------------------------- // Sheet content builders // --------------------------------------------------------------------------- function buildPnLRows(pnl: PnLRow[]): TableRow[] { const ppaChildren: TableRow[] = [ { label: "Units (MWh)", values: pnl.map((r) => r.ppa_units_mwh), format: (v) => v == null ? "—" : Math.round(v).toLocaleString() }, { label: "Tariff (₹/kWh)", values: pnl.map((r) => r.ppa_tariff_inr_per_kwh) }, ]; const mcpChildren: TableRow[] = [ { label: "MCP Units (MWh)", values: pnl.map((r) => r.mcp_units_mwh), format: (v) => v == null ? "—" : Math.round(v).toLocaleString() }, ]; const opexChildren: TableRow[] = [ { label: "O&M Opex", values: pnl.map((r) => r.om_cr) }, { label: "Insurance", values: pnl.map((r) => r.insurance_cr) }, { label: "Land Lease", values: pnl.map((r) => r.land_lease_cr) }, { label: "AM Fee", values: pnl.map((r) => r.am_fee_cr) }, { label: "Misc Opex", values: pnl.map((r) => r.misc_opex_cr) }, ]; return [ { label: "PPA Revenue", values: pnl.map((r) => r.ppa_revenue_cr), isBold: true, collapsible: true, children: ppaChildren }, { isSeparator: true, label: "", values: [] }, { label: "MCP Revenue", values: pnl.map((r) => r.mcp_revenue_cr), collapsible: true, children: mcpChildren }, { isSeparator: true, label: "", values: [] }, { label: "Total Revenue", values: pnl.map((r) => r.revenue_cr), isBold: true, isHighlight: true }, { isSeparator: true, label: "", values: [] }, { label: "Operating Expenditure", values: pnl.map((r) => r.opex_total_cr), collapsible: true, children: opexChildren }, { isSeparator: true, label: "", values: [] }, { label: "EBITDA", values: pnl.map((r) => r.ebitda_cr), isBold: true, isHighlight: true }, { label: "Book Depreciation", values: pnl.map((r) => r.depreciation_book_cr), indent: true }, { label: "EBIT", values: pnl.map((r) => r.ebit_cr), isBold: true, isHighlight: true, format: n2 }, { label: "Interest", values: pnl.map((r) => r.interest_cr), indent: true }, { label: "PBT", values: pnl.map((r) => r.pbt_cr), isBold: true, isHighlight: true, format: n2 }, { label: "Tax", values: pnl.map((r) => r.tax_cr), indent: true }, { label: "PAT", values: pnl.map((r) => r.pat_cr), isBold: true, isHighlight: true, format: n2 }, ]; } function buildCfsRows(cfs: CfsRow[], kpis: KpiSummary, pnl: PnLRow[]): TableRow[] { // CFADS = CFO + Interest (add back since CFO is post-interest in this model) const cfads = cfs.map((r, i) => r.cfo_cr + (pnl[i]?.interest_cr ?? 0)); const equityCf = cfs.map((r, i) => cfads[i] - r.debt_repayment_cr - (pnl[i]?.interest_cr ?? 0)); return [ { isHeader: true, label: "Operating Cash Flow", values: [] }, { label: "PAT", values: cfs.map((r) => r.pat_cr), indent: true }, { label: "Add: Depreciation", values: cfs.map((r) => r.depreciation_cr), indent: true }, { label: "Δ Working Capital", values: cfs.map((r) => r.delta_working_capital_cr), indent: true }, { label: "CFO", values: cfs.map((r) => r.cfo_cr), isBold: true }, { isSeparator: true, label: "", values: [] }, { isHeader: true, label: "Investing Cash Flow", values: [] }, { label: "Capex", values: cfs.map((r) => r.capex_cr), indent: true }, { label: "CFI", values: cfs.map((r) => r.cfi_cr), isBold: true }, { isSeparator: true, label: "", values: [] }, { isHeader: true, label: "Financing Cash Flow", values: [] }, { label: "Debt Drawdown", values: cfs.map((r) => r.debt_drawdown_cr), indent: true }, { label: "Debt Repayment", values: cfs.map((r) => r.debt_repayment_cr), indent: true }, { label: "Equity Injection", values: cfs.map((r) => r.equity_injection_cr), indent: true }, { label: "CFF", values: cfs.map((r) => r.cff_cr), isBold: true }, { isSeparator: true, label: "", values: [] }, { label: "Net Cash Flow", values: cfs.map((r) => r.net_cash_flow_cr), isBold: true }, { label: "Opening Cash", values: cfs.map((r) => r.opening_cash_cr), indent: true }, { label: "Closing Cash", values: cfs.map((r) => r.closing_cash_cr), isBold: true }, { isSeparator: true, label: "", values: [] }, { isHeader: true, label: "Returns Analysis", values: [] }, { label: "CFADS (pre-debt service)", values: cfads, isBold: true }, { label: "Equity Free Cash Flow", values: equityCf }, { label: `Project IRR: ${kpis.project_irr != null ? (kpis.project_irr * 100).toFixed(1) + "%" : "—"} | Equity IRR: ${kpis.equity_irr != null ? (kpis.equity_irr * 100).toFixed(1) + "%" : "—"}`, values: [], isBold: true, }, ]; } function buildBsRows(bs: BsRow[]): TableRow[] { return [ { isHeader: true, label: "Assets", values: [] }, { label: "Gross Block", values: bs.map((r) => r.gross_block_cr), indent: true }, { label: "Less: Accum Depr", values: bs.map((r) => r.accumulated_depr_cr), indent: true }, { label: "Net Block", values: bs.map((r) => r.net_block_cr), isBold: true }, { isSeparator: true, label: "", values: [] }, { label: "Cash & Bank", values: bs.map((r) => r.cash_cr), indent: true }, { label: "Receivables", values: bs.map((r) => r.receivables_cr), indent: true }, { label: "Total Assets", values: bs.map((r) => r.total_assets_cr), isBold: true }, { isSeparator: true, label: "", values: [] }, { isHeader: true, label: "Liabilities & Equity", values: [] }, { label: "Equity Share Capital", values: bs.map((r) => r.equity_cr), indent: true }, { label: "Reserves & Surplus", values: bs.map((r) => r.reserves_cr), indent: true }, { label: "Long-term Debt", values: bs.map((r) => r.long_term_debt_cr), indent: true }, { label: "Payables", values: bs.map((r) => r.payables_cr), indent: true }, { label: "Total Liabilities", values: bs.map((r) => r.total_liabilities_cr), isBold: true }, ]; } function buildDebtRows(debt: DebtYearRow[]): TableRow[] { return [ { label: "Opening Balance", values: debt.map((r) => r.opening_balance_cr) }, { label: "Interest", values: debt.map((r) => r.interest_cr) }, { label: "Principal Repayment", values: debt.map((r) => r.principal_cr) }, { label: "Total Debt Service", values: debt.map((r) => r.total_debt_service_cr), isBold: true }, { label: "Closing Balance", values: debt.map((r) => r.closing_balance_cr), isBold: true }, { isSeparator: true, label: "", values: [] }, { label: "DSCR", values: debt.map((r) => r.dscr), isBold: true, format: n2 }, ]; } // --------------------------------------------------------------------------- // Sheet views // --------------------------------------------------------------------------- function SummarySheet({ kpis, scenarioId }: { kpis: KpiSummary; scenarioId: string }) { const { data: stmts } = useQuery({ queryKey: ["statements", scenarioId], queryFn: () => getStatements(scenarioId), }); // Custom KPIs - stored in state, persist to localStorage const [customKpis, setCustomKpis] = useState<{ label: string; value: string; unit: string }[]>(() => { try { const stored = localStorage.getItem(`customKpis-${scenarioId}`); if (!stored) return []; const parsed = JSON.parse(stored); if (!Array.isArray(parsed)) return []; // Filter out invalid entries return parsed.filter( (k) => k && typeof k.label === "string" && typeof k.value === "string" && k.value !== "0.0" && k.value !== "null" ); } catch { return []; } }); function addCustomKpi() { const label = prompt("Enter KPI label (e.g. O&M Cost)"); if (!label) return; const value = prompt(`Enter value for ${label} (without unit)`); if (!value) return; const unit = prompt("Enter unit (e.g. Cr, %, yrs)") || ""; const newKpis = [...customKpis, { label, value, unit }]; setCustomKpis(newKpis); try { localStorage.setItem(`customKpis-${scenarioId}`, JSON.stringify(newKpis)); } catch { // localStorage unavailable } } function removeCustomKpi(index: number) { const newKpis = customKpis.filter((_, i) => i !== index); setCustomKpis(newKpis); try { localStorage.setItem(`customKpis-${scenarioId}`, JSON.stringify(newKpis)); } catch { // localStorage unavailable } } const pnlChart = stmts?.pnl.map((r) => ({ year: r.year, Revenue: r.revenue_cr, EBITDA: r.ebitda_cr, PAT: r.pat_cr, })) ?? []; const cashChart = stmts?.cfs.map((r) => ({ year: r.year, "Closing Cash": r.closing_cash_cr })) ?? []; return (
{kpis.solar_y1_cuf != null && ( )} {kpis.wind_y1_plf != null && ( )} {kpis.rtc_cuf_achieved != null && ( )} {/* Add custom KPI button */} {/* Custom KPIs */} {customKpis.map((kpi: { label: string; value: string; unit: string }, i: number) => (
))}
{stmts && (

P&L Overview (₹Cr)

Closing Cash (₹Cr)

)}
); } function IrrSheet({ kpis }: { kpis: KpiSummary }) { const sections = [ { title: "Returns", rows: [ { label: "Equity IRR (Leveraged)", value: kpis.equity_irr != null ? pct1(kpis.equity_irr) : "—" }, { label: "Project IRR (Unlevered)", value: kpis.project_irr != null ? pct1(kpis.project_irr) : "—" }, { label: "LCOE", value: kpis.lcoe_inr_per_kwh != null ? `₹${kpis.lcoe_inr_per_kwh.toFixed(2)}/kWh` : "—" }, { label: "Payback Period", value: kpis.payback_years != null ? `${kpis.payback_years.toFixed(1)} yrs` : "—" }, ], }, { title: "Debt Metrics", rows: [ { label: "Min DSCR", value: kpis.min_dscr?.toFixed(2) ?? "—" }, { label: "Avg DSCR", value: kpis.avg_dscr?.toFixed(2) ?? "—" }, ], }, { title: "Project Economics", rows: [ { label: "Solved / Fixed Tariff", value: kpis.solved_tariff_inr_per_kwh != null ? `₹${kpis.solved_tariff_inr_per_kwh.toFixed(2)}/kWh` : "—" }, { label: "Total Capex", value: kpis.total_capex_cr != null ? `₹${kpis.total_capex_cr.toFixed(1)} Cr` : "—" }, { label: "Debt Sized", value: kpis.debt_cr != null ? `₹${kpis.debt_cr.toFixed(1)} Cr` : "—" }, { label: "IDC", value: kpis.idc_cr != null ? `₹${kpis.idc_cr.toFixed(1)} Cr` : "—" }, ], }, ]; return (
{sections.map((s) => (
{s.title}
{s.rows.map(({ label, value }) => ( ))}
{label} {value}
))}
); } function buildGenerationRows(gen: GenerationRow[]): TableRow[] { const hasSolar = gen.some((r) => r.solar_mwh > 0); const hasWind = gen.some((r) => r.wind_mwh > 0); const rows: TableRow[] = []; if (hasSolar) { rows.push({ label: "Solar Generation (MWh)", values: gen.map((r) => r.solar_mwh), format: (v) => v == null ? "—" : Math.round(v).toLocaleString() }); rows.push({ label: "Solar CUF (%)", values: gen.map((r) => r.solar_cuf_pct), format: (v) => v == null ? "—" : `${v.toFixed(1)}%`, indent: true }); } if (hasWind) { rows.push({ label: "Wind Generation (MWh)", values: gen.map((r) => r.wind_mwh), format: (v) => v == null ? "—" : Math.round(v).toLocaleString() }); rows.push({ label: "Wind PLF (%)", values: gen.map((r) => r.wind_plf_pct), format: (v) => v == null ? "—" : `${v.toFixed(1)}%`, indent: true }); } rows.push({ label: "Gross Total (MWh)", values: gen.map((r) => r.gross_mwh), isBold: true, format: (v) => v == null ? "—" : Math.round(v).toLocaleString() }); rows.push({ label: "Less: Aux Consumption", values: gen.map((r) => -r.aux_loss_mwh), format: (v) => v == null ? "—" : Math.round(v).toLocaleString(), indent: true }); rows.push({ label: "Less: Transmission Loss", values: gen.map((r) => -r.tx_loss_mwh), format: (v) => v == null ? "—" : Math.round(v).toLocaleString(), indent: true }); rows.push({ label: "Less: DSM Penalty", values: gen.map((r) => -r.dsm_loss_mwh), format: (v) => v == null ? "—" : Math.round(v).toLocaleString(), indent: true }); rows.push({ label: "Net Billable (MWh)", values: gen.map((r) => r.net_billable_mwh), isBold: true, format: (v) => v == null ? "—" : Math.round(v).toLocaleString() }); rows.push({ label: "Revenue (₹ Cr)", values: gen.map((r) => r.revenue_cr), isBold: true, format: n2 }); return rows; } // Hourly generation tree: Year > Month > Day > Hour (expandable) // --------------------------------------------------------------------------- const MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; interface HourlyData { solar_hourly?: number[]; wind_hourly?: number[]; } 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()); const [expandedDays, setExpandedDays] = useState>(new Set()); const toggleYear = (yr: number) => setExpandedYears((prev) => { const next = new Set(prev); if (next.has(yr)) next.delete(yr); else next.add(yr); return next; }); const toggleMonth = (key: string) => setExpandedMonths((prev) => { const next = new Set(prev); if (next.has(key)) next.delete(key); else next.add(key); return next; }); const toggleDay = (key: string) => setExpandedDays((prev) => { const next = new Set(prev); if (next.has(key)) next.delete(key); else next.add(key); return next; }); // Helper: get hourly data for a specific year/month/day const getYearData = (year: number, isSolar: boolean) => { const data = isSolar ? solar_hourly : wind_hourly; if (!data) return []; const start = (year - 1) * 8760; return data.slice(start, start + 8760); }; const getMonthData = (year: number, month: number, isSolar: boolean) => { const yearData = getYearData(year, isSolar); if (!yearData) return []; const start = MONTH_DAYS.slice(0, month - 1).reduce((a, d) => a + d, 0) * 24; const days = MONTH_DAYS[month - 1]; return yearData.slice(start, start + days * 24); }; const getDayData = (year: number, month: number, day: number, isSolar: boolean) => { const monthData = getMonthData(year, month, isSolar); if (!monthData) return []; const start = (day - 1) * 24; return monthData.slice(start, start + 24); }; // Compute totals for display const computeYearTotal = (year: number, isSolar: boolean) => { const d = getYearData(year, isSolar); return d.reduce((a, v) => a + v, 0); }; const computeMonthTotal = (year: number, month: number, isSolar: boolean) => { const d = getMonthData(year, month, isSolar); return d.reduce((a, v) => a + v, 0); }; const computeDayTotal = (year: number, month: number, day: number, isSolar: boolean) => { const d = getDayData(year, month, day, isSolar); return d.reduce((a, v) => a + v, 0); }; if (!hasData) { return
No hourly generation data available
; } // Build tree: Years (1-25), expandable to months, days, hours return (
{hasSolar && ● Solar} {hasWind && ● Wind} Click to expand: Year → Month → Day → Hour
{/* Year rows */} {[...Array(25)].map((_, i) => { const year = i + 1; const isYearExpanded = expandedYears.has(year); const solarYr = hasSolar ? computeYearTotal(year, true) : 0; const windYr = hasWind ? computeYearTotal(year, false) : 0; const fyLabel = getFyLabel(i); return (
{/* Month rows (expandable) */} {isYearExpanded && [...Array(12)].map((_, mi) => { const month = mi + 1; const monthKey = `${year}-${month}`; const isMonthExpanded = expandedMonths.has(monthKey); 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 (
{/* Day rows (expandable) */} {isMonthExpanded && [...Array(daysInMonth)].map((_, di) => { const day = di + 1; const dayKey = `${year}-${month}-${day}`; const isDayExpanded = expandedDays.has(dayKey); const solarDy = hasSolar ? computeDayTotal(year, month, day, true) : 0; const windDy = hasWind ? computeDayTotal(year, month, day, false) : 0; return (
{/* Hour values */} {isDayExpanded && (
{[...Array(24)].map((_, h) => { const hour = h; const hourIdx = (day - 1) * 24 + hour; const solarHr = hasSolar ? getDayData(year, month, day, true)[hourIdx % 24] : 0; const windHr = hasWind ? getDayData(year, month, day, false)[hourIdx % 24] : 0; return (
{h}:00 {hasSolar &&
{Math.round(solarHr)}
} {hasWind &&
{Math.round(windHr)}
}
); })}
)}
); })}
); })}
); })}
); } function buildOpexRows(pnl: PnLRow[]): TableRow[] { const total = pnl.map((r) => r.om_cr + r.insurance_cr + r.land_lease_cr + r.am_fee_cr + r.misc_opex_cr); return [ { label: "O&M (₹ Cr)", values: pnl.map((r) => r.om_cr), format: n2 }, { label: "Insurance (₹ Cr)", values: pnl.map((r) => r.insurance_cr), format: n2, indent: true }, { label: "Land Lease (₹ Cr)", values: pnl.map((r) => r.land_lease_cr), format: n2, indent: true }, { label: "AM Fee (₹ Cr)", values: pnl.map((r) => r.am_fee_cr), format: n2, indent: true }, { label: "Miscellaneous (₹ Cr)", values: pnl.map((r) => r.misc_opex_cr), format: n2, indent: true }, { label: "Total OPEX (₹ Cr)", values: total, isBold: true, format: n2 }, { label: "OPEX as % of Revenue", values: pnl.map((r, i) => r.revenue_cr > 0 ? total[i] / r.revenue_cr * 100 : null), format: (v) => v == null ? "—" : `${v.toFixed(1)}%`, indent: true }, ]; } function IdcSheet({ idc }: { idc: IdcPhasing }) { if (!idc?.base_capex_cr) return
No IDC data available
; const months = idc.monthly ?? []; const nMonths = months.length; const nCols = Math.min(nMonths, 24); // Cap at 24 months for display // Build ALL-IN-ONE matrix: both component costs AND funding sources const monthlyRate = 1 / nMonths; const solarPct = 0.70, windPct = 0.0, landPct = 0.10, epcPct = 0.12, contPct = 0.08; const matrixRows: TableRow[] = [ // === Component Costs (what's being built) === { isHeader: true, label: "COMPONENT COSTS", values: [] }, { label: "Solar Capex", values: Array(nCols).fill(0).map((_, i) => months[i] ? idc.base_capex_cr * solarPct * monthlyRate : 0) }, { label: "Wind Capex", values: Array(nCols).fill(0).map((_, i) => months[i] ? idc.base_capex_cr * windPct * monthlyRate : 0) }, { label: "Land & Common", values: Array(nCols).fill(0).map((_, i) => months[i] ? idc.base_capex_cr * landPct * monthlyRate : 0) }, { label: "EPC Overhead", values: Array(nCols).fill(0).map((_, i) => months[i] ? idc.base_capex_cr * epcPct * monthlyRate : 0) }, { label: "Contingency", values: Array(nCols).fill(0).map((_, i) => months[i] ? idc.base_capex_cr * contPct * monthlyRate : 0) }, { isSeparator: true, label: "", values: [] }, // === Funding Sources (how it's paid) === { isHeader: true, label: "FUNDING SOURCES", values: [] }, { label: "Equity Draw", values: months.slice(0, nCols).map((m) => m.equity_draw_cr) }, { label: "Debt Draw", values: months.slice(0, nCols).map((m) => m.debt_draw_cr) }, { label: "IDC Interest", values: months.slice(0, nCols).map((m) => m.idc_accrual_cr), indent: true }, { isSeparator: true, label: "", values: [] }, // === Cumulative (running totals) === { isHeader: true, label: "CUMULATIVE", values: [] }, { label: "Cum. Equity", values: months.slice(0, nCols).map((m) => m.cum_equity_cr), indent: true }, { label: "Cum. Debt", values: months.slice(0, nCols).map((m) => m.cum_debt_cr), indent: true }, { label: "Cum. IDC", values: months.slice(0, nCols).map((m) => m.cum_idc_cr), indent: true }, { isSeparator: true, label: "", values: [] }, { label: "Total Project Cost", values: months.slice(0, nCols).map((m) => m.cum_tpc_cr), isBold: true }, ]; // Compact header const fundingMix = [ { label: "Base", val: idc.base_capex_cr }, { label: "IDC", val: idc.idc_cr }, { label: "Total", val: idc.total_capex_cr }, { label: "Equity", val: idc.equity_cr }, { label: "Debt", val: idc.debt_cr }, ]; return (
{/* Header Cards */}
{fundingMix.map((f) => (

{f.label}

₹{f.val.toFixed(0)}

))}
{/* Single ALL-IN-ONE Matrix Table */}

IDC Construction Phasing Matrix ({nMonths} months)

m.month)} rows={matrixRows} unit="₹ Cr" yearPrefix="M" />
); } // --------------------------------------------------------------------------- // Main workbook // --------------------------------------------------------------------------- interface Props { scenarioId: string; kpis: KpiSummary; debtScheduleJson: string | null; activeSheet: string; codYear?: number; } export function WorkbookView({ scenarioId, kpis, debtScheduleJson, activeSheet, codYear }: Props) { const { data: stmts } = useQuery({ queryKey: ["statements", scenarioId], queryFn: () => getStatements(scenarioId), }); const debtSchedule: DebtYearRow[] = (() => { try { const safe = (debtScheduleJson ?? "[]") .replace(/:\s*Infinity/g, ": null") .replace(/:\s*-Infinity/g, ": null") .replace(/:\s*NaN\b/g, ": null"); return JSON.parse(safe) as DebtYearRow[]; } catch { return []; } })(); const pnl = stmts?.pnl ?? []; const cfs = stmts?.cfs ?? []; const bs = stmts?.bs ?? []; const generation = stmts?.generation ?? []; const idcPhasing = stmts?.idc_phasing; const years = pnl.map((r) => r.year); const debtYears = debtSchedule.map((r) => r.year); const genYears = generation.map((r) => r.year); return (

All monetary values in INR Crore unless noted

{activeSheet === "summary" && } {activeSheet === "pnl" && pnl.length > 0 && ( )} {activeSheet === "cfs" && cfs.length > 0 && ( )} {activeSheet === "bs" && bs.length > 0 && ( )} {activeSheet === "debt" && debtSchedule.length > 0 && ( )} {activeSheet === "irr" && } {activeSheet === "generation" && (stmts?.solar_hourly || stmts?.wind_hourly ? ( ) : generation.length > 0 ? ( ) : (
No generation data available
))} {activeSheet === "idc" && idcPhasing && ( )} {activeSheet === "opex" && pnl.length > 0 && ( )}
); }