diff --git a/src/app/api/portfolio/[portfolioId]/scenario/[scenarioId]/metrics/route.ts b/src/app/api/portfolio/[portfolioId]/scenario/[scenarioId]/metrics/route.ts index 3573b3a..03a7c2c 100644 --- a/src/app/api/portfolio/[portfolioId]/scenario/[scenarioId]/metrics/route.ts +++ b/src/app/api/portfolio/[portfolioId]/scenario/[scenarioId]/metrics/route.ts @@ -92,10 +92,11 @@ export async function GET( const upgraded = upgradedResult.rows[0] as UpgradedAggregates; const n_units_upgraded = upgraded.n_units_upgraded; - const total_cost = upgraded.total_cost ?? 0; + const construction_cost = upgraded.total_cost ?? 0; const contingency = upgraded.contingency ?? 0; const total_funding = upgraded.total_funding ?? 0; - const net_cost = total_cost - total_funding; + const net_cost = construction_cost - total_funding; + const pc_cost = construction_cost * 0.28; // Placeholder for PC cost // // ---------------------------------------------------------- @@ -132,13 +133,16 @@ export async function GET( total_bills, n_units, scenario_epc_counts, - + pc_cost, // Upgrade metrics (only properties with work) n_units_upgraded, - total_cost, + construction_cost, contingency, total_funding, net_cost, - gross_per_unit: n_units_upgraded > 0 ? total_cost / n_units_upgraded : 0, + gross_per_unit: + n_units_upgraded > 0 + ? (construction_cost + pc_cost) / n_units_upgraded + : 0, }); } diff --git a/src/app/portfolio/[slug]/(portfolio)/page.tsx b/src/app/portfolio/[slug]/(portfolio)/page.tsx index d6722c6..4d5d754 100644 --- a/src/app/portfolio/[slug]/(portfolio)/page.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/page.tsx @@ -81,13 +81,7 @@ export default async function Page(props: { <>
-
- -
-
+
{properties.length === 0 ? ( ) : ( diff --git a/src/app/portfolio/[slug]/(portfolio)/reporting/ReportingClientArea.tsx b/src/app/portfolio/[slug]/(portfolio)/reporting/ReportingClientArea.tsx index ce72dfa..cb78fd4 100644 --- a/src/app/portfolio/[slug]/(portfolio)/reporting/ReportingClientArea.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/reporting/ReportingClientArea.tsx @@ -110,17 +110,19 @@ export function ReportingClientArea({ // ---------------------------------------- const scenarioSpecific = scenarioData ? { - totalCost: scenarioData.total_cost, + constructionCost: scenarioData.construction_cost, + pcCost: scenarioData.pc_cost, contingency: scenarioData.contingency, funding: scenarioData.total_funding, costPerSap: - scenarioData.total_cost > 0 + scenarioData.construction_cost > 0 ? scenarioData.gross_per_unit / (scenarioData.avg_sap - (baseline.averages.avg_sap ?? 0)) : 0, costPerCo2: - scenarioData.total_cost > 0 - ? scenarioData.total_cost / scenarioData.total_carbon + scenarioData.construction_cost > 0 + ? (scenarioData.construction_cost + scenarioData.pc_cost) / + scenarioData.total_carbon : 0, netCost: scenarioData.net_cost, grossPerUnit: scenarioData.gross_per_unit, diff --git a/src/app/portfolio/[slug]/(portfolio)/reporting/ScenarioFinancialDrawer.tsx b/src/app/portfolio/[slug]/(portfolio)/reporting/ScenarioFinancialDrawer.tsx index 435fa9d..89db037 100644 --- a/src/app/portfolio/[slug]/(portfolio)/reporting/ScenarioFinancialDrawer.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/reporting/ScenarioFinancialDrawer.tsx @@ -2,174 +2,319 @@ import { motion, AnimatePresence } from "framer-motion"; import { formatNumber } from "@/app/utils"; +import clsx from "clsx"; -// Premium Icons +/* Heroicons (outline) */ import { - Banknote, - ShieldAlert, - PiggyBank, - Scale, - Gauge, - Factory, - Home, - Users, -} from "lucide-react"; + ArrowTrendingUpIcon, + ClipboardDocumentCheckIcon, + ScaleIcon, + HomeIcon, + BoltIcon, + FireIcon, + ChartBarIcon, + WrenchIcon, +} from "@heroicons/react/24/outline"; -export function ScenarioFinancialDrawer({ - open, - metrics, -}: { +/* Lucide */ +import { Gauge } from "lucide-react"; + +/* ───────────────────────────────────────────── */ +/* Types */ +/* ───────────────────────────────────────────── */ + +interface ScenarioFinancialDrawerProps { open: boolean; metrics: any | null; +} + +/* ───────────────────────────────────────────── */ +/* Gradient Tokens */ +/* ───────────────────────────────────────────── */ + +const gradients = { + green: "bg-gradient-to-r from-green-700 via-green-400 to-green-700", + blue: "bg-gradient-to-r from-brandblue via-sky-400 to-brandblue", + purple: "bg-gradient-to-r from-purple-700 via-purple-400 to-purple-700", +}; + +/* ───────────────────────────────────────────── */ +/* Gradient Card Shell */ +/* ───────────────────────────────────────────── */ + +function GradientCard({ + gradient, + children, +}: { + gradient: string; + children: React.ReactNode; }) { return ( - - {open && metrics && ( - -
-

- Scenario Financial Summary -

- -
- - - - - - - - - - - - -
-
-
- )} -
+
+
{children}
+
); } +/* ───────────────────────────────────────────── */ +/* Single Metric Card */ +/* ───────────────────────────────────────────── */ + function Metric({ label, value, icon: Icon, color, - bg, + gradient, }: { label: string; value: string | number; - icon: any; + icon: React.ComponentType>; color: string; - bg: string; + gradient: string; }) { return ( -
-
- {/* coloured icon background */} -
- -
- - + +
+ + {value} + {label}
+
+ ); +} - - {value} - +/* ───────────────────────────────────────────── */ +/* Paired Metric Card (Reusable Everywhere) */ +/* ───────────────────────────────────────────── */ + +function PairedMetric({ + title, + icon: Icon, + primary, + secondary, + gradient, + iconClassName = "text-gray-700", +}: { + title: string; + icon: React.ComponentType>; + primary: { label: string; value: string }; + secondary: { label: string; value: string }; + gradient: string; + iconClassName?: string; +}) { + return ( + +
+
+ + {title} +
+ +
+
+

{primary.label}

+

+ {primary.value} +

+
+ +
+

{secondary.label}

+

+ {secondary.value} +

+
+
+
+
+ ); +} + +/* ───────────────────────────────────────────── */ +/* Section Header */ +/* ───────────────────────────────────────────── */ + +function Section({ + title, + subtitle, + icon: Icon, + gradient, + accentColor, + children, +}: { + title: string; + subtitle: string; + icon: React.ComponentType>; + gradient: string; + accentColor: string; + children: React.ReactNode; +}) { + return ( +
+
+
+ +
+ +
+ +
+

{title}

+

{subtitle}

+
+
+ +
+ {children} +
); } + +/* ───────────────────────────────────────────── */ +/* Main Drawer */ +/* ───────────────────────────────────────────── */ + +export function ScenarioFinancialDrawer({ + open, + metrics, +}: ScenarioFinancialDrawerProps) { + return ( + + {open && metrics && ( + +
+

+ Scenario Impact Summary +

+ + {/* BENEFITS */} +
+ + + + + +
+ + {/* COSTS */} +
+ + + + + +
+ + {/* COST EFFECTIVENESS */} +
+ +
+
+
+ )} +
+ ); +}