diff --git a/src/app/components/portfolio/SummaryBox.tsx b/src/app/components/portfolio/SummaryBox.tsx
new file mode 100644
index 00000000..80006664
--- /dev/null
+++ b/src/app/components/portfolio/SummaryBox.tsx
@@ -0,0 +1,203 @@
+"use client";
+import { useState } from "react";
+import { convertDaysToWorkingWeeks, formatNumber } from "@/app/utils";
+
+interface SummaryBoxProps {
+ scenarios: Array<{
+ id: bigint;
+ name: string;
+ budget: number | null;
+ totalCost: number | null;
+ co2EquivalentSavings: number | null;
+ totalWorkHours: number | null;
+ propertyValuationIncrease: number | null;
+ energySavings: number | null;
+ energyCostSavings: number | null;
+ labourDays: number | null;
+ isDefault: boolean;
+ }>;
+ numProperties: number;
+}
+
+function SummaryBox({ scenarios, numProperties }: SummaryBoxProps) {
+ // Get the default scenario
+ const defaultScenario =
+ scenarios.find((scenario) => scenario.isDefault) || scenarios[0];
+
+ const [selectedScenarioId, setSelectedScenarioId] = useState(
+ Number(defaultScenario.id)
+ );
+
+ const [budgetFormatted, setBudgetFormatted] = useState(
+ formatBudget(defaultScenario.budget)
+ );
+ const [totalCostFormatted, setTotalCostFormatted] = useState(
+ formatMoney(defaultScenario.totalCost)
+ );
+ const [totalValueIncreaseFormatted, setTotalValueIncreaseFormatted] =
+ useState(formatMoney(defaultScenario.propertyValuationIncrease));
+ const [energyCostSavingsFormatted, setEnergyCostSavingsFormatted] = useState(
+ formatMoney(defaultScenario.energyCostSavings)
+ );
+ const [co2EquivalentSavingsFormatted, setCo2EquivalentSavingsFormatted] =
+ useState(formatCo2(defaultScenario.co2EquivalentSavings));
+ const [energySavingsFormatted, setEnergySavingsFormatted] = useState(
+ formatKwh(defaultScenario.energySavings)
+ );
+ const [estimatedDurationFormatted, setEstimatedDurationFormatted] = useState(
+ convertDaysToWorkingWeeks(defaultScenario.labourDays)
+ );
+
+ const handleScenarioChange = (scenarioId: string) => {
+ setSelectedScenarioId(Number(scenarioId));
+ const selectedScenario = scenarios.find(
+ (scenario) => Number(scenario.id) === Number(scenarioId)
+ );
+
+ if (!selectedScenario) {
+ return;
+ }
+
+ setBudgetFormatted(formatBudget(selectedScenario.budget));
+ setTotalCostFormatted(formatMoney(selectedScenario.totalCost));
+ setTotalValueIncreaseFormatted(
+ formatMoney(selectedScenario.propertyValuationIncrease)
+ );
+ setEnergyCostSavingsFormatted(
+ formatMoney(selectedScenario.energyCostSavings)
+ );
+ setCo2EquivalentSavingsFormatted(
+ formatCo2(selectedScenario.co2EquivalentSavings)
+ );
+ setEnergySavingsFormatted(formatKwh(selectedScenario.energySavings));
+ setEstimatedDurationFormatted(
+ convertDaysToWorkingWeeks(selectedScenario.labourDays)
+ );
+ };
+
+ function formatMoney(amount: number | null) {
+ return amount === null ? "£0" : "£" + formatNumber(amount);
+ }
+
+ function formatBudget(budget: number | null) {
+ return budget === null ? "Not set" : formatMoney(budget);
+ }
+
+ function formatHours(hours: number | null) {
+ return hours === null ? "0 hours" : Math.round(hours) + " hours";
+ }
+
+ function formatCo2(co2: number | null) {
+ return co2 === null ? "-" : co2.toFixed(1) + " tonnes";
+ }
+
+ function formatKwh(energy: number | null) {
+ return energy === null ? "-" : formatNumber(energy) + " kWh";
+ }
+
+ return (
+
+
+ Portfolio Summary
+
+
+
+
+
+
+
+
+ Work Package
+
+
+
+
+ | Total Budget |
+ {budgetFormatted} |
+
+
+ | Total Cost |
+
+ {totalCostFormatted}
+ |
+
+
+ | Estimated Duration |
+
+ {estimatedDurationFormatted}
+ |
+
+
+ | Total properties |
+ {numProperties} |
+
+
+
+
+
+
+ Environmental Impact
+
+
+
+
+ |
+ Annual{" "}
+
+ CO2
+ {" "}
+ Savings
+ |
+
+ {co2EquivalentSavingsFormatted}
+ |
+
+
+ | Annual Energy Savings |
+
+ {energySavingsFormatted}
+ |
+
+
+
+
+
+
+ Financial Impact
+
+
+
+
+ | Annual Energy Bill Reduction |
+
+ {energyCostSavingsFormatted}
+ |
+
+
+ | Total Value Increase |
+
+ {totalValueIncreaseFormatted}
+ |
+
+
+
+
+
+
+ );
+}
+
+export default SummaryBox;
diff --git a/src/app/portfolio/[slug]/(portfolio)/page.tsx b/src/app/portfolio/[slug]/(portfolio)/page.tsx
index a754ede6..409ecdfc 100644
--- a/src/app/portfolio/[slug]/(portfolio)/page.tsx
+++ b/src/app/portfolio/[slug]/(portfolio)/page.tsx
@@ -4,6 +4,8 @@ import DataTable from "@/app/portfolio/[slug]/components/propertyTable";
import { columns } from "@/app/portfolio/[slug]/components/propertyTableColumns";
import { PropertyWithRelations } from "@/app/db/schema/property";
import { formatNumber, convertDaysToWorkingWeeks } from "@/app/utils";
+import SummaryBox from "@/app/components/portfolio/SummaryBox";
+import { is } from "cypress/types/bluebird";
// We enfore caching of data for 60 seconds
export const revalidate = 60;
@@ -22,175 +24,6 @@ function EmptyPropertyState() {
);
}
-interface SummaryBoxProps {
- budget: number | null;
- totalCost: number | null;
- numProperties: number;
- co2EquivalentSavings: number | null;
- totalWorkHours: number | null;
- propertyValuationIncrease: number | null;
- energySavings: number | null;
- energyCostSavings: number | null;
- estimatedDuration: number | null;
-}
-
-function SummaryBox({
- budget,
- totalCost,
- numProperties,
- co2EquivalentSavings,
- totalWorkHours,
- propertyValuationIncrease,
- energySavings,
- energyCostSavings,
- estimatedDuration,
-}: SummaryBoxProps) {
- function formatMoney(amount: number | null) {
- if (amount === null) {
- return "£0";
- } else {
- return "£" + formatNumber(amount);
- }
- }
-
- function formatBudget(budget: number | null) {
- if (budget === null) {
- return "Not set";
- } else {
- return formatMoney(budget);
- }
- }
-
- function formatHours(hours: number | null) {
- if (hours === null) {
- return "0 hours";
- } else {
- return Math.round(hours) + " hours";
- }
- }
-
- function formatCo2(co2: number | null) {
- if (co2 === null) {
- return "-";
- } else {
- return co2.toFixed(1) + " tonnes";
- }
- }
-
- function formatKwh(energy: number | null) {
- if (energy === null) {
- return "-";
- } else {
- return formatNumber(energy) + " kWh";
- }
- }
-
- const budgetFormatted = formatBudget(budget);
- const totalCostFormatted = formatMoney(totalCost);
- const totalValueIncreaseFormatted = formatMoney(propertyValuationIncrease);
- // const totalWorkHoursFormatted = formatHours(totalWorkHours);
- // const rentalYieldIncreaseFormatted = formatMoney(rentalYieldIncrease);
- const energyCostSavingsFormatted = formatMoney(energyCostSavings);
- const co2EquivalentSavingsFormatted = formatCo2(co2EquivalentSavings);
- const energySavingsFormatted = formatKwh(energySavings);
- const estimatedDurationFormatted =
- convertDaysToWorkingWeeks(estimatedDuration);
-
- return (
-
-
- Portfolio Summary
-
-
-
-
- Work Package
-
-
-
-
- | Total Budget |
- {budgetFormatted} |
-
-
- | Total Cost |
-
- {totalCostFormatted}
- |
-
-
-
- | Estimated Duration |
-
- {estimatedDurationFormatted}
- |
-
-
-
- | Total properties |
- {numProperties} |
-
-
-
-
-
- {/* Environmental Impact */}
-
- Environmental Impact
-
-
-
-
- |
- Annual{" "}
-
- CO2
- {" "}
- Savings
- |
-
- {co2EquivalentSavingsFormatted}
- |
-
-
- | Annual Energy Savings |
-
- {energySavingsFormatted}
- |
-
-
-
-
-
- {/* Financial Impact table */}
-
- Financial Impact
-
-
-
-
- |
- Annual Energy Bill Reduction
- |
-
- {energyCostSavingsFormatted}
- |
-
-
-
- | Total Value Increase |
-
- {totalValueIncreaseFormatted}
- |
-
-
-
-
-
-
- );
-}
-
export default async function Page({
params,
searchParams,
@@ -201,16 +34,43 @@ export default async function Page({
// This page is served from the server so we can make calls to the database
const portfolioId = params.slug;
- const portfolio = await getPortfolio(portfolioId);
- const portfolioPerformance = await getPortfolioPerformance(portfolioId);
- const defaultPerformance = portfolioPerformance.find(
- (perf) => perf.isDefault
- );
+ let portfolioPerformance = await getPortfolioPerformance(portfolioId);
+ let scenarios;
- const portfolioData = defaultPerformance || portfolio;
+ if (portfolioPerformance.length > 0) {
+ scenarios = portfolioPerformance.map((performance) => ({
+ id: BigInt(performance.id),
+ name: performance.name || "Default Scenario",
+ budget: performance.budget,
+ totalCost: performance.cost,
+ co2EquivalentSavings: performance.co2EquivalentSavings,
+ totalWorkHours: performance.totalWorkHours,
+ propertyValuationIncrease: performance.propertyValuationIncrease,
+ energySavings: performance.energySavings,
+ energyCostSavings: performance.energyCostSavings,
+ labourDays: performance.labourDays,
+ isDefault: performance.isDefault,
+ }));
+ } else {
+ const portfolio = await getPortfolio(portfolioId);
+ scenarios = [
+ {
+ id: BigInt(0),
+ name: "Default",
+ budget: portfolio.budget,
+ totalCost: portfolio.cost,
+ co2EquivalentSavings: portfolio.co2EquivalentSavings,
+ totalWorkHours: portfolio.totalWorkHours,
+ propertyValuationIncrease: portfolio.propertyValuationIncrease,
+ energySavings: portfolio.energySavings,
+ energyCostSavings: portfolio.energyCostSavings,
+ labourDays: portfolio.labourDays,
+ isDefault: true,
+ },
+ ];
+ }
- // Default limit to 1000 and offset to 0 for now - will handle pagination later
const properties: PropertyWithRelations[] = await getProperties(
portfolioId,
1000,
@@ -223,17 +83,8 @@ export default async function Page({