mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-08 11:37:25 +00:00
Added scenario comparison ui - still missing cost
This commit is contained in:
parent
dd927da618
commit
c5b2c80970
6 changed files with 315 additions and 130 deletions
|
|
@ -0,0 +1,121 @@
|
|||
import { db } from "@/app/db/db";
|
||||
import { sql } from "drizzle-orm";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
type PlanRow = {
|
||||
id: bigint;
|
||||
post_sap_points: number | null;
|
||||
post_co2_emissions: number | null;
|
||||
post_energy_bill: number | null;
|
||||
post_energy_consumption: number | null;
|
||||
valuation_post_retrofit: number | null;
|
||||
valuation_increase: number | null;
|
||||
co2_savings: number | null;
|
||||
energy_bill_savings: number | null;
|
||||
energy_consumption_savings: number | null;
|
||||
};
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
props: { params: Promise<{ portfolioId: string; scenarioId: string }> }
|
||||
) {
|
||||
console.log("In the request ");
|
||||
const { portfolioId, scenarioId } = await props.params;
|
||||
|
||||
const pid = BigInt(portfolioId);
|
||||
const sid = BigInt(scenarioId);
|
||||
|
||||
//
|
||||
// --------------------------------------------------------------
|
||||
// 1️⃣ Fetch all plans for this portfolio + scenario (FAST)
|
||||
// --------------------------------------------------------------
|
||||
//
|
||||
const planRows = await db.execute(sql`
|
||||
SELECT
|
||||
id,
|
||||
post_sap_points,
|
||||
post_co2_emissions,
|
||||
post_energy_bill,
|
||||
post_energy_consumption,
|
||||
valuation_post_retrofit,
|
||||
valuation_increase,
|
||||
co2_savings,
|
||||
energy_bill_savings,
|
||||
energy_consumption_savings
|
||||
FROM plan
|
||||
WHERE portfolio_id = ${pid}
|
||||
AND scenario_id = ${sid};
|
||||
`);
|
||||
|
||||
const plans = planRows.rows as PlanRow[];
|
||||
|
||||
if (plans.length === 0) {
|
||||
return NextResponse.json(
|
||||
{ error: "No plans found for this scenario" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
const planIds = plans.map((p) => p.id);
|
||||
|
||||
//
|
||||
// --------------------------------------------------------------
|
||||
// 2️⃣ Fetch total funding for all planIds
|
||||
// funding = SUM(project_funding + total_uplift)
|
||||
// --------------------------------------------------------------
|
||||
//
|
||||
|
||||
const planIdArray = sql`ARRAY[${sql.join(planIds, sql`, `)}]::bigint[]`;
|
||||
const fundingRows = await db.execute(sql`
|
||||
SELECT
|
||||
SUM(COALESCE(project_funding, 0) + COALESCE(total_uplift, 0))::float AS total_funding
|
||||
FROM funding_package
|
||||
WHERE plan_id = ANY(${planIdArray});
|
||||
`);
|
||||
|
||||
//
|
||||
// --------------------------------------------------------------
|
||||
// 3️⃣ Aggregate scenario metrics (SAP, carbon, bills)
|
||||
// --------------------------------------------------------------
|
||||
//
|
||||
const n = plans.length;
|
||||
|
||||
const avg_sap = plans.reduce((s, r) => s + (r.post_sap_points ?? 0), 0) / n;
|
||||
|
||||
const avg_carbon =
|
||||
plans.reduce((s, r) => s + (r.post_co2_emissions ?? 0), 0) / n;
|
||||
|
||||
const avg_bills =
|
||||
plans.reduce((s, r) => s + (r.post_energy_bill ?? 0), 0) / n;
|
||||
|
||||
const total_carbon = plans.reduce(
|
||||
(s, r) => s + (r.post_co2_emissions ?? 0),
|
||||
0
|
||||
);
|
||||
|
||||
const total_bills = plans.reduce((s, r) => s + (r.post_energy_bill ?? 0), 0);
|
||||
|
||||
//
|
||||
// --------------------------------------------------------------
|
||||
// 4️⃣ Financial Metrics (based on real funding)
|
||||
// --------------------------------------------------------------
|
||||
//
|
||||
const totalCost = 0; // you will add "plan.cost" later
|
||||
const contingency = 0; // also update when you have it
|
||||
const funding = Number(fundingRows.rows[0]?.total_funding ?? 0);
|
||||
const netCost = Number(totalCost) - funding;
|
||||
|
||||
return NextResponse.json({
|
||||
avg_sap: avg_sap.toFixed(1),
|
||||
avg_carbon,
|
||||
avg_bills,
|
||||
total_carbon,
|
||||
total_bills,
|
||||
total_cost: totalCost,
|
||||
contingency,
|
||||
total_funding: funding,
|
||||
net_cost: netCost,
|
||||
net_cost_per_unit: n > 0 ? netCost / n : 0,
|
||||
n_units: n,
|
||||
});
|
||||
}
|
||||
|
|
@ -82,7 +82,6 @@ export const planTypeEnum = pgEnum("plan_type", PlanType);
|
|||
|
||||
export const plan = pgTable("plan", {
|
||||
id: bigserial("id", { mode: "bigint" }).primaryKey(),
|
||||
|
||||
name: text("name"),
|
||||
|
||||
portfolioId: bigint("portfolio_id", { mode: "bigint" })
|
||||
|
|
@ -100,6 +99,9 @@ export const plan = pgTable("plan", {
|
|||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
isDefault: boolean("is_default").notNull(),
|
||||
|
||||
totalCost: real("total_cost"),
|
||||
contingency: real("contingency"),
|
||||
|
||||
// ─────────────────────────────────────────────────────────
|
||||
// Valuation metrics (existing)
|
||||
// ─────────────────────────────────────────────────────────
|
||||
|
|
@ -126,7 +128,7 @@ export const plan = pgTable("plan", {
|
|||
energyBillSavings: real("energy_bill_savings"),
|
||||
|
||||
// ─────────────────────────────────────────────────────────
|
||||
// NEW — Energy demand (kWh/year)
|
||||
// NEW — Energy consumption (kWh/year)
|
||||
// ─────────────────────────────────────────────────────────
|
||||
postEnergyConsumption: real("post_energy_consumption"),
|
||||
energyConsumptionSavings: real("energy_consumption_savings"),
|
||||
|
|
|
|||
|
|
@ -38,6 +38,12 @@ const epcColors: Record<string, string> = {
|
|||
Unknown: "text-gray-400",
|
||||
};
|
||||
|
||||
function hasOverlay(
|
||||
overlay: ScenarioOverlayMetrics | undefined
|
||||
): overlay is ScenarioOverlayMetrics {
|
||||
return overlay !== undefined;
|
||||
}
|
||||
|
||||
export function DashboardSummaryCards({
|
||||
total,
|
||||
totals,
|
||||
|
|
@ -53,21 +59,25 @@ export function DashboardSummaryCards({
|
|||
}) {
|
||||
const missingEpcCount = estimatedCounts.estimated;
|
||||
const missingEpcPercent = total > 0 ? (missingEpcCount / total) * 100 : 0;
|
||||
const averageCurrentEpc = sapToEpc(averages.avg_sap || 0);
|
||||
const hasScenario = Boolean(scenarioOverlay);
|
||||
|
||||
// We pull the scenario data from the scenario overlay
|
||||
const averageCurrentEpc = sapToEpc(averages.avg_sap || 0);
|
||||
|
||||
const overlay = scenarioOverlay ?? undefined;
|
||||
const hasScenario = hasOverlay(overlay);
|
||||
|
||||
function deltaLabel(baseline: number, scenario: number) {
|
||||
const diff = scenario - baseline;
|
||||
if (diff === 0) return null;
|
||||
const b = Number(baseline);
|
||||
const s = Number(scenario);
|
||||
const diff = s - b;
|
||||
|
||||
if (!isFinite(diff) || diff === 0) return null;
|
||||
|
||||
const sign = diff > 0 ? "▲" : "▼";
|
||||
const color = diff > 0 ? "text-red-600" : "text-emerald-600";
|
||||
|
||||
return (
|
||||
<span className={`text-sm font-medium ${color}`}>
|
||||
{sign} {formatNumber(Math.abs(diff))}
|
||||
{sign} {Math.abs(diff).toFixed(2)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
|
@ -78,6 +88,8 @@ export function DashboardSummaryCards({
|
|||
title: "Number of Homes",
|
||||
baseline: total,
|
||||
scenario: null,
|
||||
baselineTotal: undefined,
|
||||
scenarioTotal: undefined,
|
||||
units: "",
|
||||
subtitle: "Total properties in this portfolio.",
|
||||
},
|
||||
|
|
@ -86,8 +98,10 @@ export function DashboardSummaryCards({
|
|||
title: "Average EPC Rating",
|
||||
baseline: `${averageCurrentEpc} (${Math.round(averages.avg_sap ?? 0)} pts)`,
|
||||
scenario:
|
||||
scenarioOverlay?.avgSap &&
|
||||
`${sapToEpc(scenarioOverlay.avgSap.scenario)} (${scenarioOverlay.avgSap.scenario} pts)`,
|
||||
overlay?.avgSap &&
|
||||
`${sapToEpc(overlay.avgSap.scenario)} (${overlay.avgSap.scenario} pts)`,
|
||||
baselineTotal: undefined,
|
||||
scenarioTotal: undefined,
|
||||
subtitle: "Current SAP rating across all properties.",
|
||||
isEpc: true,
|
||||
},
|
||||
|
|
@ -95,35 +109,29 @@ export function DashboardSummaryCards({
|
|||
key: "avgCarbon",
|
||||
title: "Carbon Emissions",
|
||||
baseline: formatNumber(averages.avg_carbon ?? 0),
|
||||
scenario:
|
||||
scenarioOverlay?.avgCarbon &&
|
||||
formatNumber(scenarioOverlay.avgCarbon.scenario),
|
||||
units: "tCO₂e per home",
|
||||
scenario: overlay?.avgCarbon && formatNumber(overlay.avgCarbon.scenario),
|
||||
units: "tCO₂e /home",
|
||||
baselineTotal: totals.total_carbon ?? 0,
|
||||
scenarioTotal: overlay?.avgCarbon?.scenarioTotal,
|
||||
subtitle: "Average annual CO₂ output per home.",
|
||||
totalValue: `Total: ${formatNumber(totals.total_carbon ?? 0)} tCO₂e`,
|
||||
delta:
|
||||
scenarioOverlay?.avgCarbon &&
|
||||
deltaLabel(
|
||||
scenarioOverlay.avgCarbon.baseline,
|
||||
scenarioOverlay.avgCarbon.scenario
|
||||
),
|
||||
hasScenario && overlay?.avgCarbon
|
||||
? deltaLabel(overlay.avgCarbon.baseline, overlay.avgCarbon.scenario)
|
||||
: null,
|
||||
},
|
||||
{
|
||||
key: "avgBills",
|
||||
title: "Energy Bills",
|
||||
baseline: formatNumber(averages.avg_bills ?? 0),
|
||||
scenario:
|
||||
scenarioOverlay?.avgBills &&
|
||||
formatNumber(scenarioOverlay.avgBills.scenario),
|
||||
units: "per home",
|
||||
scenario: overlay?.avgBills && formatNumber(overlay.avgBills.scenario),
|
||||
units: "/ home",
|
||||
baselineTotal: totals.total_bills ?? 0,
|
||||
scenarioTotal: overlay?.avgBills?.scenarioTotal,
|
||||
subtitle: "Estimated annual energy bills.",
|
||||
totalValue: `Total: £${formatNumber(totals.total_bills ?? 0)}`,
|
||||
delta:
|
||||
scenarioOverlay?.avgBills &&
|
||||
deltaLabel(
|
||||
scenarioOverlay.avgBills.baseline,
|
||||
scenarioOverlay.avgBills.scenario
|
||||
),
|
||||
hasScenario && overlay?.avgBills
|
||||
? deltaLabel(overlay.avgBills.baseline, overlay.avgBills.scenario)
|
||||
: null,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -133,8 +141,6 @@ export function DashboardSummaryCards({
|
|||
const Icon = cardStyles[c.key as MetricKey].icon;
|
||||
const color = cardStyles[c.key as MetricKey].color;
|
||||
|
||||
console.log("Card data:", c);
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={c.key}
|
||||
|
|
@ -156,55 +162,78 @@ export function DashboardSummaryCards({
|
|||
hasScenario ? "justify-between" : "justify-start"
|
||||
} items-start`}
|
||||
>
|
||||
{/* BASELINE */}
|
||||
{/* BASELINE COLUMN */}
|
||||
<div className="flex flex-col">
|
||||
<span className="text-xs text-gray-500">Baseline</span>
|
||||
<span
|
||||
className={
|
||||
c.isEpc
|
||||
? `text-3xl font-semibold ${epcColors[averageCurrentEpc || "Unknown"]}`
|
||||
: "text-3xl font-semibold bg-clip-text text-transparent bg-gradient-to-r from-brandblue to-midblue"
|
||||
}
|
||||
>
|
||||
{c.key === "avgBills" ? `£${c.baseline}` : c.baseline}
|
||||
</span>
|
||||
{c.units && (
|
||||
<span className="text-sm text-gray-500">{c.units}</span>
|
||||
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span
|
||||
className={
|
||||
c.isEpc
|
||||
? `text-3xl font-semibold ${epcColors[averageCurrentEpc || "Unknown"]}`
|
||||
: "text-3xl font-semibold bg-clip-text text-transparent bg-gradient-to-r from-brandblue to-midblue"
|
||||
}
|
||||
>
|
||||
{c.key === "avgBills" ? `£${c.baseline}` : c.baseline}
|
||||
</span>
|
||||
|
||||
{/* units next to baseline average */}
|
||||
{c.units && (
|
||||
<span className="text-sm text-gray-500">{c.units}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Baseline total */}
|
||||
{c.baselineTotal !== undefined && (
|
||||
<span className="text-md text-gray-600">
|
||||
Total:{" "}
|
||||
{c.key === "avgBills"
|
||||
? `£${formatNumber(c.baselineTotal)}`
|
||||
: `${formatNumber(c.baselineTotal)} tCO₂e`}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* SCENARIO */}
|
||||
{/* SCENARIO COLUMN */}
|
||||
{hasScenario && c.scenario && (
|
||||
<div className="flex flex-col text-right">
|
||||
<span className="text-xs text-gray-500">Scenario</span>
|
||||
|
||||
<span
|
||||
className={
|
||||
c.isEpc
|
||||
? `text-2xl font-semibold ${
|
||||
epcColors[
|
||||
sapToEpc(
|
||||
scenarioOverlay?.avgSap?.scenario ??
|
||||
(averages.avg_sap || 0)
|
||||
) || "Unknown"
|
||||
]
|
||||
}`
|
||||
: "text-2xl font-semibold text-brandblue"
|
||||
}
|
||||
>
|
||||
{c.key === "avgBills" ? `£${c.scenario}` : c.scenario}
|
||||
</span>
|
||||
{/* average + delta + units row */}
|
||||
<div className="flex items-baseline justify-end gap-2">
|
||||
<span
|
||||
className={
|
||||
c.isEpc
|
||||
? `text-2xl font-semibold ${
|
||||
epcColors[
|
||||
sapToEpc(
|
||||
overlay?.avgSap?.scenario ??
|
||||
(averages.avg_sap || 0)
|
||||
) || "Unknown"
|
||||
]
|
||||
}`
|
||||
: "text-2xl font-semibold text-brandblue"
|
||||
}
|
||||
>
|
||||
{c.key === "avgBills" ? `£${c.scenario}` : c.scenario}
|
||||
</span>
|
||||
|
||||
{c.delta && <div>{c.delta}</div>}
|
||||
{c.delta && <span>{c.delta}</span>}
|
||||
</div>
|
||||
|
||||
{/* Scenario total */}
|
||||
{c.scenarioTotal !== undefined && (
|
||||
<span className="text-md text-gray-600">
|
||||
Total:{" "}
|
||||
{c.key === "avgBills"
|
||||
? `£${formatNumber(c.scenarioTotal)}`
|
||||
: `${formatNumber(c.scenarioTotal)} tCO₂e`}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* TOTAlS */}
|
||||
{c.totalValue && (
|
||||
<div className="text-sm text-gray-700">{c.totalValue}</div>
|
||||
)}
|
||||
|
||||
{/* Missing EPC bar */}
|
||||
{c.key === "missingEpc" && (
|
||||
<div className="w-full bg-gray-200 rounded-full h-2 mt-2">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { ScenarioSelectorWrapper } from "./scenarioSelectorWrapper";
|
||||
import { DashboardSummaryCards } from "./DashboardSummaryCards";
|
||||
import { BreakdownChart } from "./BreakdownChart";
|
||||
|
|
@ -27,6 +28,26 @@ interface ReportingClientAreaProps {
|
|||
portfolioId: number;
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
// Fetcher for scenario API route
|
||||
// ----------------------------------------
|
||||
async function fetchScenarioReport({
|
||||
portfolioId,
|
||||
scenarioId,
|
||||
}: {
|
||||
portfolioId: number;
|
||||
scenarioId: number;
|
||||
}) {
|
||||
const res = await fetch(
|
||||
`/api/portfolio/${portfolioId}/scenario/${scenarioId}/metrics`
|
||||
);
|
||||
if (!res.ok) {
|
||||
console.error("Failed to fetch scenario report:", await res.text());
|
||||
throw new Error("Failed to load scenario report");
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export function ReportingClientArea({
|
||||
baseline,
|
||||
propertyTypes,
|
||||
|
|
@ -37,74 +58,78 @@ export function ReportingClientArea({
|
|||
null
|
||||
);
|
||||
|
||||
const [scenarioMetrics, setScenarioMetrics] = useState<any>(null);
|
||||
const drawerOpen = Boolean(selectedScenarioId);
|
||||
|
||||
// 🔥 Hardcoded scenario metrics (replace later with real fetch)
|
||||
useEffect(() => {
|
||||
if (!selectedScenarioId) {
|
||||
setScenarioMetrics(null);
|
||||
return;
|
||||
}
|
||||
// ----------------------------------------
|
||||
// React Query: fetch scenario metrics
|
||||
// ----------------------------------------
|
||||
const {
|
||||
data: scenarioData,
|
||||
isLoading,
|
||||
isError,
|
||||
} = useQuery({
|
||||
queryKey: ["scenario-report", portfolioId, selectedScenarioId],
|
||||
queryFn: () =>
|
||||
fetchScenarioReport({
|
||||
portfolioId,
|
||||
scenarioId: selectedScenarioId!,
|
||||
}),
|
||||
enabled: !!selectedScenarioId, // only run when scenario selected
|
||||
});
|
||||
|
||||
const mocked = {
|
||||
averages: {
|
||||
avg_sap: 82,
|
||||
avg_carbon: 1.7,
|
||||
avg_bills: 1300,
|
||||
},
|
||||
totals: {
|
||||
total_carbon: 1.7 * 120,
|
||||
total_bills: 1300 * 120,
|
||||
},
|
||||
valuation: {
|
||||
baseline: 130_000_000,
|
||||
scenario: 136_000_000,
|
||||
},
|
||||
};
|
||||
console.log("Setting mocked scenario metrics");
|
||||
setScenarioMetrics(mocked);
|
||||
}, [selectedScenarioId]);
|
||||
|
||||
// 👇 Active metrics switch to scenario if present
|
||||
// Baseline always stays baseline
|
||||
const activeMetrics = baseline;
|
||||
|
||||
// Scenario overlay stays separate
|
||||
const scenarioOverlay = scenarioMetrics
|
||||
// ----------------------------------------
|
||||
// Build overlay for Dashboard Summary cards
|
||||
// ----------------------------------------
|
||||
const scenarioOverlay = scenarioData
|
||||
? {
|
||||
avgSap: {
|
||||
baseline: baseline.averages.avg_sap || 0,
|
||||
scenario: scenarioMetrics.averages.avg_sap,
|
||||
baseline: baseline.averages.avg_sap ?? 0,
|
||||
scenario: Number(scenarioData.avg_sap),
|
||||
},
|
||||
avgCarbon: {
|
||||
baseline: baseline.averages.avg_carbon || 0,
|
||||
scenario: scenarioMetrics.averages.avg_carbon,
|
||||
baseline: Number(baseline.averages.avg_carbon ?? 0),
|
||||
scenario: Number(scenarioData.avg_carbon),
|
||||
|
||||
baselineTotal: Number(baseline.totals.total_carbon ?? 0),
|
||||
scenarioTotal: Number(scenarioData.total_carbon ?? 0),
|
||||
},
|
||||
avgBills: {
|
||||
baseline: baseline.averages.avg_bills || 0,
|
||||
scenario: scenarioMetrics.averages.avg_bills,
|
||||
baseline: baseline.averages.avg_bills ?? 0,
|
||||
scenario: scenarioData.avg_bills,
|
||||
baselineTotal: baseline.totals.total_bills ?? 0,
|
||||
scenarioTotal: scenarioData.total_bills,
|
||||
},
|
||||
valuation: scenarioMetrics.valuation,
|
||||
valuation: { baseline: null, scenario: null },
|
||||
}
|
||||
: null;
|
||||
|
||||
// --------------------
|
||||
// Scenario Financial Metrics (mocked)
|
||||
// --------------------
|
||||
const scenarioFinancial = scenarioMetrics
|
||||
console.log("Scenario Data:", scenarioData);
|
||||
|
||||
// ----------------------------------------
|
||||
// Financial drawer values (from API)
|
||||
// ----------------------------------------
|
||||
const scenarioFinancial = scenarioData
|
||||
? {
|
||||
totalCost: 5_400_000,
|
||||
contingency: 540_000,
|
||||
funding: 2_100_000,
|
||||
costPerSap: 3200,
|
||||
costPerCo2: 180,
|
||||
netCost: 5_400_000 - 2_100_000,
|
||||
netCostPerUnit: (5_400_000 - 2_100_000) / 120,
|
||||
nUnits: 120,
|
||||
totalCost: scenarioData.total_cost,
|
||||
contingency: scenarioData.contingency,
|
||||
funding: scenarioData.total_funding,
|
||||
costPerSap:
|
||||
scenarioData.total_cost > 0
|
||||
? scenarioData.total_cost / scenarioData.avg_sap
|
||||
: 0,
|
||||
costPerCo2:
|
||||
scenarioData.total_cost > 0
|
||||
? scenarioData.total_cost / scenarioData.total_carbon
|
||||
: 0,
|
||||
netCost: scenarioData.net_cost,
|
||||
netCostPerUnit: scenarioData.net_cost_per_unit,
|
||||
nUnits: scenarioData.n_units,
|
||||
}
|
||||
: null;
|
||||
|
||||
// Baseline stays baseline
|
||||
const activeMetrics = baseline;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ScenarioSelectorWrapper
|
||||
|
|
@ -114,6 +139,16 @@ export function ReportingClientArea({
|
|||
setSelectedScenarioId={setSelectedScenarioId}
|
||||
/>
|
||||
|
||||
{/* LOADING + ERROR STATES */}
|
||||
{isLoading && selectedScenarioId && (
|
||||
<div className="text-sm text-gray-500 mt-2">Loading scenario…</div>
|
||||
)}
|
||||
{isError && (
|
||||
<div className="text-sm text-red-500 mt-2">
|
||||
Couldn't load scenario data.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* --- RETROFIT SECTION --- */}
|
||||
<SectionDivider
|
||||
title="Retrofit Summary"
|
||||
|
|
|
|||
|
|
@ -3,15 +3,6 @@ import {
|
|||
getCountByPropertyType,
|
||||
getScenarios,
|
||||
} from "@/app/portfolio/[slug]/(portfolio)/reporting/databaseFunctions";
|
||||
import { DashboardSummaryCards } from "@/app/portfolio/[slug]/(portfolio)/reporting/DashboardSummaryCards";
|
||||
import { BreakdownChart } from "@/app/portfolio/[slug]/(portfolio)/reporting/BreakdownChart";
|
||||
import { EpcQualityCards } from "@/app/portfolio/[slug]/(portfolio)/reporting/EpcQualityCards";
|
||||
import { SectionDivider } from "@/app/portfolio/[slug]/(portfolio)/reporting/SectionDivider";
|
||||
import {
|
||||
PlaceholderMetricCards,
|
||||
CONDITION_PLACEHOLDERS,
|
||||
FINANCIAL_PLACEHOLDERS,
|
||||
} from "@/app/portfolio/[slug]/(portfolio)/reporting/PlaceholderMetricCards";
|
||||
import { ReportingClientArea } from "./ReportingClientArea";
|
||||
|
||||
export default async function ReportingPage(props: {
|
||||
|
|
|
|||
|
|
@ -59,16 +59,23 @@ export interface ScenarioOverlayMetrics {
|
|||
baseline: number;
|
||||
scenario: number;
|
||||
};
|
||||
|
||||
avgCarbon?: {
|
||||
baseline: number;
|
||||
scenario: number;
|
||||
baselineTotal: number;
|
||||
scenarioTotal: number;
|
||||
};
|
||||
|
||||
avgBills?: {
|
||||
baseline: number;
|
||||
scenario: number;
|
||||
baselineTotal: number;
|
||||
scenarioTotal: number;
|
||||
};
|
||||
|
||||
valuation?: {
|
||||
baseline: number;
|
||||
scenario: number;
|
||||
baseline: number | null;
|
||||
scenario: number | null;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue