mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-30 12:55:02 +00:00
Added scenarios mechanism for summary page
This commit is contained in:
parent
097d80c69e
commit
24527703be
6 changed files with 181 additions and 139 deletions
|
|
@ -1,46 +1,46 @@
|
|||
import { portfolio } from "@/app/db/schema/portfolio";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { db } from "@/app/db/db";
|
||||
import { z } from "zod";
|
||||
import { formatNumber } from "@/app/utils";
|
||||
import { DataItem, ChartData } from "@/app/portfolio/[slug]/utils";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { scenario } from "@/app/db/schema/recommendations";
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { portfolioId: string } }
|
||||
{ params }: { params: { scenarioId: string } }
|
||||
) {
|
||||
const portfolioId = params.portfolioId;
|
||||
const scenarioId = params.scenarioId;
|
||||
|
||||
const data = await db
|
||||
.select({
|
||||
name: portfolio.name,
|
||||
cost: portfolio.cost,
|
||||
epcBreakdownPostRetrofit: portfolio.epcBreakdownPostRetrofit,
|
||||
numberOfProperties: portfolio.numberOfProperties,
|
||||
nUnitsToRetrofit: portfolio.nUnitsToRetrofit,
|
||||
co2PerUnitPostRetrofit: portfolio.co2PerUnitPostRetrofit,
|
||||
energyBillPerUnitPostRetrofit: portfolio.energyBillPerUnitPostRetrofit,
|
||||
name: scenario.name,
|
||||
cost: scenario.cost,
|
||||
epcBreakdownPostRetrofit: scenario.epcBreakdownPostRetrofit,
|
||||
numberOfProperties: scenario.numberOfProperties,
|
||||
nUnitsToRetrofit: scenario.nUnitsToRetrofit,
|
||||
co2PerUnitPostRetrofit: scenario.co2PerUnitPostRetrofit,
|
||||
energyBillPerUnitPostRetrofit: scenario.energyBillPerUnitPostRetrofit,
|
||||
energyConsumptionPerUnitPostRetrofit:
|
||||
portfolio.energyConsumptionPerUnitPostRetrofit,
|
||||
valuationImprovementPerUnit: portfolio.valuationImprovementPerUnit,
|
||||
costPerUnit: portfolio.costPerUnit,
|
||||
costPerCo2Saved: portfolio.costPerCo2Saved,
|
||||
costPerSapPoint: portfolio.costPerSapPoint,
|
||||
valuationReturnOnInvestment: portfolio.valuationReturnOnInvestment,
|
||||
scenario.energyConsumptionPerUnitPostRetrofit,
|
||||
valuationImprovementPerUnit: scenario.valuationImprovementPerUnit,
|
||||
costPerUnit: scenario.costPerUnit,
|
||||
costPerCo2Saved: scenario.costPerCo2Saved,
|
||||
costPerSapPoint: scenario.costPerSapPoint,
|
||||
valuationReturnOnInvestment: scenario.valuationReturnOnInvestment,
|
||||
})
|
||||
.from(portfolio)
|
||||
.where(eq(portfolio.id, BigInt(portfolioId)));
|
||||
.from(scenario)
|
||||
.where(eq(scenario.id, BigInt(scenarioId)));
|
||||
|
||||
if (data.length === 0) {
|
||||
throw new Error("Portfolio not found");
|
||||
throw new Error("Scenario not found");
|
||||
}
|
||||
|
||||
if (data.length > 1) {
|
||||
throw new Error("More than one portfolio found");
|
||||
throw new Error("More than one scenario found");
|
||||
}
|
||||
|
||||
const portfolioName = data[0].name;
|
||||
const scenarioName = data[0].name || "Default";
|
||||
|
||||
// Format the data we need for the overview
|
||||
const output: DataItem[] = [
|
||||
|
|
@ -48,7 +48,7 @@ export async function GET(
|
|||
title: "EPCs",
|
||||
scenarios: [
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: scenarioName,
|
||||
data: JSON.parse(
|
||||
data[0].epcBreakdownPostRetrofit || "[]"
|
||||
) as ChartData[],
|
||||
|
|
@ -59,7 +59,7 @@ export async function GET(
|
|||
title: "# Units",
|
||||
scenarios: [
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: scenarioName,
|
||||
data: String(data[0].numberOfProperties) || "",
|
||||
},
|
||||
],
|
||||
|
|
@ -68,7 +68,7 @@ export async function GET(
|
|||
title: "# Units to retrofit",
|
||||
scenarios: [
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: scenarioName,
|
||||
data: String(data[0].nUnitsToRetrofit) || "",
|
||||
},
|
||||
],
|
||||
|
|
@ -77,7 +77,7 @@ export async function GET(
|
|||
title: "Co2/unit",
|
||||
scenarios: [
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: scenarioName,
|
||||
data: data[0].co2PerUnitPostRetrofit || "",
|
||||
},
|
||||
],
|
||||
|
|
@ -86,7 +86,7 @@ export async function GET(
|
|||
title: "Annual energy bill/unit",
|
||||
scenarios: [
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: scenarioName,
|
||||
data: data[0].energyBillPerUnitPostRetrofit || "",
|
||||
},
|
||||
],
|
||||
|
|
@ -95,7 +95,7 @@ export async function GET(
|
|||
title: "Annual Energy consumption (kWh)/unit",
|
||||
scenarios: [
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: scenarioName,
|
||||
data: data[0].energyConsumptionPerUnitPostRetrofit || "",
|
||||
},
|
||||
],
|
||||
|
|
@ -104,7 +104,7 @@ export async function GET(
|
|||
title: "Cost (£)",
|
||||
scenarios: [
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: scenarioName,
|
||||
data: "£" + formatNumber(data[0].cost || 0),
|
||||
},
|
||||
],
|
||||
|
|
@ -112,19 +112,19 @@ export async function GET(
|
|||
{
|
||||
title: "Cost (£)/unit",
|
||||
scenarios: [
|
||||
{ scenarioName: portfolioName, data: data[0].costPerUnit || "" },
|
||||
{ scenarioName: scenarioName, data: data[0].costPerUnit || "" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "£ per CO2 reduction",
|
||||
scenarios: [
|
||||
{ scenarioName: portfolioName, data: data[0].costPerCo2Saved || "" },
|
||||
{ scenarioName: scenarioName, data: data[0].costPerCo2Saved || "" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "£ per SAP point",
|
||||
scenarios: [
|
||||
{ scenarioName: portfolioName, data: data[0].costPerSapPoint || "" },
|
||||
{ scenarioName: scenarioName, data: data[0].costPerSapPoint || "" },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -132,7 +132,7 @@ export async function GET(
|
|||
"Potential valuation improvement/unit - highly indicative EPC effect*",
|
||||
scenarios: [
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: scenarioName,
|
||||
data: data[0].valuationImprovementPerUnit || "",
|
||||
},
|
||||
],
|
||||
|
|
@ -142,7 +142,7 @@ export async function GET(
|
|||
"Potential valuation return on investment - highly indicative EPC effect*",
|
||||
scenarios: [
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: scenarioName,
|
||||
data: data[0].valuationReturnOnInvestment || "",
|
||||
},
|
||||
],
|
||||
|
|
@ -4,18 +4,18 @@ import { Fragment, useState } from "react";
|
|||
const SelectComparisonModal = ({
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
userPortfolios,
|
||||
scenarios,
|
||||
onAddColumn,
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
userPortfolios: { name: string; id: bigint }[];
|
||||
scenarios: { name: string | null; id: bigint }[];
|
||||
onAddColumn: (columnName: string) => void;
|
||||
}) => {
|
||||
const [selectedPortfolio, setSelectedPortfolio] = useState("");
|
||||
const [selectedScenario, setSelectedScenario] = useState("");
|
||||
|
||||
const addColumn = () => {
|
||||
onAddColumn(selectedPortfolio);
|
||||
onAddColumn(selectedScenario);
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
|
|
@ -65,15 +65,15 @@ const SelectComparisonModal = ({
|
|||
<div className="mt-2">
|
||||
<select
|
||||
className="w-full p-2 border border-gray-300 rounded-md"
|
||||
value={selectedPortfolio}
|
||||
onChange={(e) => setSelectedPortfolio(e.target.value)}
|
||||
value={selectedScenario}
|
||||
onChange={(e) => setSelectedScenario(e.target.value)}
|
||||
>
|
||||
{userPortfolios.map((portfolio) => (
|
||||
{scenarios.map((scenario) => (
|
||||
<option
|
||||
key={portfolio.id.toString()}
|
||||
value={String(portfolio.id)}
|
||||
key={scenario.id.toString()}
|
||||
value={String(scenario.id)}
|
||||
>
|
||||
{portfolio.name}
|
||||
{scenario.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ import EpcBarChart from "./EpcBarChart";
|
|||
import { DataItem, ChartData } from "@/app/portfolio/[slug]/utils";
|
||||
import { QueryKey, UseQueryOptions, useQuery } from "@tanstack/react-query";
|
||||
|
||||
export async function getComparsionOverviewPortfolioData(
|
||||
portfolioId: string
|
||||
export async function getComparsionOverviewScenarioData(
|
||||
scenarioId: string
|
||||
): Promise<Promise<DataItem[]>> {
|
||||
const url = process.env.URL || "" + `/api/portfolio/summary/${portfolioId}`;
|
||||
const url = process.env.URL || "" + `/api/portfolio/scenario/${scenarioId}`;
|
||||
|
||||
// This is the client side counterpart to getOverviewPortfolioData
|
||||
// This is the client side counterpart to getOverviewScenarioData
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
|
|
@ -37,21 +37,21 @@ export async function getComparsionOverviewPortfolioData(
|
|||
return response.json();
|
||||
}
|
||||
|
||||
interface UseFetchPortfolioSummaryProps {
|
||||
portfolioId: string; // Assuming portfolioId can be null to handle cases before it's available
|
||||
interface UseFetchScenarioSummaryProps {
|
||||
scenarioId: string; // Assuming scenarioId can be null to handle cases before it's available
|
||||
onSuccess: (data: DataItem[]) => void;
|
||||
options?: Omit<UseQueryOptions<DataItem[], Error>, "queryKey" | "queryFn">;
|
||||
}
|
||||
|
||||
export const useFetchPortfolioSummary = ({
|
||||
portfolioId,
|
||||
export const useFetchScenarioSummary = ({
|
||||
scenarioId,
|
||||
onSuccess,
|
||||
}: UseFetchPortfolioSummaryProps) => {
|
||||
}: UseFetchScenarioSummaryProps) => {
|
||||
return useQuery({
|
||||
queryKey: ["portfolioSummary", portfolioId],
|
||||
queryFn: () => getComparsionOverviewPortfolioData(portfolioId as string), // Ensure non-null assertion or handle potential null inside the function
|
||||
queryKey: ["scenarioSummary", scenarioId],
|
||||
queryFn: () => getComparsionOverviewScenarioData(scenarioId as string), // Ensure non-null assertion or handle potential null inside the function
|
||||
onSuccess,
|
||||
enabled: portfolioId !== "no-id", // Ensure the query only runs when portfolioId is not null
|
||||
enabled: scenarioId !== "no-id", // Ensure the query only runs when portfolioId is not null
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -64,10 +64,10 @@ const shouldHighlightRow = (title: string) => {
|
|||
|
||||
const SummaryTable = ({
|
||||
data,
|
||||
userPortfolios,
|
||||
scenarios,
|
||||
}: {
|
||||
data: DataItem[];
|
||||
userPortfolios: { name: string; id: bigint }[];
|
||||
scenarios: { name: string | null; id: bigint }[];
|
||||
}) => {
|
||||
// TODO: Right now a user can ad multiple comparisons of the same column, we should prevent this
|
||||
|
||||
|
|
@ -117,16 +117,15 @@ const SummaryTable = ({
|
|||
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
const [selectedPortfolioId, setSelectedPortfolioId] =
|
||||
useState<string>("no-id");
|
||||
const [selectedScenarioId, setSelectedScenarioId] = useState<string>("no-id");
|
||||
|
||||
// Fetch and handle data when a new portfolio is selected
|
||||
// Fetch and handle data when a new scenario is selected
|
||||
const {
|
||||
isLoading,
|
||||
error,
|
||||
data: fetchedData,
|
||||
} = useFetchPortfolioSummary({
|
||||
portfolioId: selectedPortfolioId,
|
||||
} = useFetchScenarioSummary({
|
||||
scenarioId: selectedScenarioId,
|
||||
onSuccess: (fetchedData: DataItem[]) => {
|
||||
const updatedData = managedData.map((item) => ({
|
||||
...item,
|
||||
|
|
@ -171,15 +170,17 @@ const SummaryTable = ({
|
|||
<div className="my-8">
|
||||
<button
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
className="mb-4 p-2 bg-brandgold text-white rounded"
|
||||
className="mb-4 p-2 bg-brandgold text-white rounded disabled:opacity-50"
|
||||
// If scenarios is empty, we disable this button
|
||||
disabled={scenarios.length === 0}
|
||||
>
|
||||
Add Comparison
|
||||
</button>
|
||||
<SelectComparisonModal
|
||||
isOpen={isModalOpen}
|
||||
setIsOpen={setIsModalOpen}
|
||||
userPortfolios={userPortfolios}
|
||||
onAddColumn={(id: string) => setSelectedPortfolioId(id)}
|
||||
scenarios={scenarios}
|
||||
onAddColumn={(id: string) => setSelectedScenarioId(id)}
|
||||
/>
|
||||
|
||||
<div className="overflow-x-auto shadow-md sm:rounded-lg">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import SummaryTable from "@/app/components/portfolio/summary/SummaryTable";
|
||||
import { getUserPortfolios, getOverviewPortfolioData } from "../../utils";
|
||||
import {
|
||||
getNonDefaultPortfolioScenarios,
|
||||
getOverviewPortfolioData,
|
||||
} from "../../utils";
|
||||
|
||||
export default async function PortfolioSummary({
|
||||
params,
|
||||
|
|
@ -11,14 +14,23 @@ export default async function PortfolioSummary({
|
|||
|
||||
// Retrieve all of the names and ids of the portfolios, attributed to this user
|
||||
// Get user id from the session
|
||||
const userPortfolios = await getUserPortfolios(portfolioId);
|
||||
const scenarios = await getNonDefaultPortfolioScenarios(portfolioId);
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4">
|
||||
<h1 className="text-3xl text-gray-700 font-bold my-4">Summary</h1>
|
||||
<SummaryTable data={data} userPortfolios={userPortfolios} />
|
||||
<SummaryTable data={data} scenarios={scenarios} />
|
||||
<div className="text-sm text-gray-600 mb-5">
|
||||
* Valuation improvement is based on these studies from
|
||||
<a
|
||||
className="ml-1 underline"
|
||||
href="https://www.rightmove.co.uk/news/articles/property-news/average-uk-energy-bills-how-to-save-money/#:~:text=A%20'green%20premium'%20that%20could,Rightmove%20Greener%20Homes%20Report%202023)."
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Rightmove
|
||||
</a>
|
||||
,
|
||||
<a
|
||||
className="ml-1 underline"
|
||||
href="https://www.knightfrank.com/research/article/2022-10-11-improving-your-epc-rating-could-increase-your-homes-value-by-up-to-20"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { formatNumber } from "@/app/utils";
|
||||
import { and, ConsoleLogWriter, eq, inArray } from "drizzle-orm";
|
||||
import { and, eq, inArray } from "drizzle-orm";
|
||||
import { db } from "@/app/db/db";
|
||||
import { portfolio, portfolioUsers } from "@/app/db/schema/portfolio";
|
||||
import { portfolio } from "@/app/db/schema/portfolio";
|
||||
import { property } from "@/app/db/schema/property";
|
||||
import type { Portfolio } from "@/app/db/schema/portfolio";
|
||||
import type { PropertyWithRelations } from "@/app/db/schema/property";
|
||||
|
|
@ -71,33 +71,70 @@ export async function getOverviewPortfolioData(
|
|||
): Promise<DataItem[]> {
|
||||
// Keep just the columns we need for the overview
|
||||
|
||||
const data = await db
|
||||
// We firstly attempt to retrieve the data from the scenarios table, starting with the default
|
||||
// scenario. If the default scenario doesn't exist, we'll use the data from the
|
||||
// portfolio
|
||||
let data = await db
|
||||
.select({
|
||||
name: portfolio.name,
|
||||
cost: portfolio.cost,
|
||||
epcBreakdownPreRetrofit: portfolio.epcBreakdownPreRetrofit,
|
||||
epcBreakdownPostRetrofit: portfolio.epcBreakdownPostRetrofit,
|
||||
numberOfProperties: portfolio.numberOfProperties,
|
||||
nUnitsToRetrofit: portfolio.nUnitsToRetrofit,
|
||||
co2PerUnitPreRetrofit: portfolio.co2PerUnitPreRetrofit,
|
||||
co2PerUnitPostRetrofit: portfolio.co2PerUnitPostRetrofit,
|
||||
energyBillPerUnitPreRetrofit: portfolio.energyBillPerUnitPreRetrofit,
|
||||
energyBillPerUnitPostRetrofit: portfolio.energyBillPerUnitPostRetrofit,
|
||||
name: scenario.name,
|
||||
cost: scenario.cost,
|
||||
epcBreakdownPreRetrofit: scenario.epcBreakdownPreRetrofit,
|
||||
epcBreakdownPostRetrofit: scenario.epcBreakdownPostRetrofit,
|
||||
numberOfProperties: scenario.numberOfProperties,
|
||||
nUnitsToRetrofit: scenario.nUnitsToRetrofit,
|
||||
co2PerUnitPreRetrofit: scenario.co2PerUnitPreRetrofit,
|
||||
co2PerUnitPostRetrofit: scenario.co2PerUnitPostRetrofit,
|
||||
energyBillPerUnitPreRetrofit: scenario.energyBillPerUnitPreRetrofit,
|
||||
energyBillPerUnitPostRetrofit: scenario.energyBillPerUnitPostRetrofit,
|
||||
energyConsumptionPerUnitPreRetrofit:
|
||||
portfolio.energyConsumptionPerUnitPreRetrofit,
|
||||
scenario.energyConsumptionPerUnitPreRetrofit,
|
||||
energyConsumptionPerUnitPostRetrofit:
|
||||
portfolio.energyConsumptionPerUnitPostRetrofit,
|
||||
valuationImprovementPerUnit: portfolio.valuationImprovementPerUnit,
|
||||
costPerUnit: portfolio.costPerUnit,
|
||||
costPerCo2Saved: portfolio.costPerCo2Saved,
|
||||
costPerSapPoint: portfolio.costPerSapPoint,
|
||||
valuationReturnOnInvestment: portfolio.valuationReturnOnInvestment,
|
||||
scenario.energyConsumptionPerUnitPostRetrofit,
|
||||
valuationImprovementPerUnit: scenario.valuationImprovementPerUnit,
|
||||
costPerUnit: scenario.costPerUnit,
|
||||
costPerCo2Saved: scenario.costPerCo2Saved,
|
||||
costPerSapPoint: scenario.costPerSapPoint,
|
||||
valuationReturnOnInvestment: scenario.valuationReturnOnInvestment,
|
||||
})
|
||||
.from(portfolio)
|
||||
.where(eq(portfolio.id, BigInt(portfolioId)));
|
||||
.from(scenario)
|
||||
.where(
|
||||
and(
|
||||
eq(scenario.portfolioId, BigInt(portfolioId)),
|
||||
eq(scenario.isDefault, true)
|
||||
)
|
||||
);
|
||||
|
||||
if (data.length === 0) {
|
||||
throw new Error("Portfolio not found");
|
||||
// If we have no data, we fetch it from the scenarios. This will facilitate this for older portfolios
|
||||
data = await db
|
||||
.select({
|
||||
name: portfolio.name,
|
||||
cost: portfolio.cost,
|
||||
epcBreakdownPreRetrofit: portfolio.epcBreakdownPreRetrofit,
|
||||
epcBreakdownPostRetrofit: portfolio.epcBreakdownPostRetrofit,
|
||||
numberOfProperties: portfolio.numberOfProperties,
|
||||
nUnitsToRetrofit: portfolio.nUnitsToRetrofit,
|
||||
co2PerUnitPreRetrofit: portfolio.co2PerUnitPreRetrofit,
|
||||
co2PerUnitPostRetrofit: portfolio.co2PerUnitPostRetrofit,
|
||||
energyBillPerUnitPreRetrofit: portfolio.energyBillPerUnitPreRetrofit,
|
||||
energyBillPerUnitPostRetrofit: portfolio.energyBillPerUnitPostRetrofit,
|
||||
energyConsumptionPerUnitPreRetrofit:
|
||||
portfolio.energyConsumptionPerUnitPreRetrofit,
|
||||
energyConsumptionPerUnitPostRetrofit:
|
||||
portfolio.energyConsumptionPerUnitPostRetrofit,
|
||||
valuationImprovementPerUnit: portfolio.valuationImprovementPerUnit,
|
||||
costPerUnit: portfolio.costPerUnit,
|
||||
costPerCo2Saved: portfolio.costPerCo2Saved,
|
||||
costPerSapPoint: portfolio.costPerSapPoint,
|
||||
valuationReturnOnInvestment: portfolio.valuationReturnOnInvestment,
|
||||
})
|
||||
.from(portfolio)
|
||||
.where(eq(portfolio.id, BigInt(portfolioId)));
|
||||
|
||||
// If we still have no data, we throw and error
|
||||
if (data.length === 0) {
|
||||
throw new Error("Portfolio not found");
|
||||
}
|
||||
}
|
||||
|
||||
if (data.length > 1) {
|
||||
|
|
@ -118,7 +155,7 @@ export async function getOverviewPortfolioData(
|
|||
) as ChartData[],
|
||||
},
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: portfolioName || "Default",
|
||||
data: JSON.parse(
|
||||
data[0].epcBreakdownPostRetrofit || "[]"
|
||||
) as ChartData[],
|
||||
|
|
@ -133,7 +170,7 @@ export async function getOverviewPortfolioData(
|
|||
data: String(data[0].numberOfProperties) || "",
|
||||
},
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: portfolioName || "Default",
|
||||
data: String(data[0].numberOfProperties) || "",
|
||||
},
|
||||
],
|
||||
|
|
@ -143,7 +180,7 @@ export async function getOverviewPortfolioData(
|
|||
scenarios: [
|
||||
{ scenarioName: "Today", data: "-" },
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: portfolioName || "Default",
|
||||
data: String(data[0].nUnitsToRetrofit) || "",
|
||||
},
|
||||
],
|
||||
|
|
@ -153,7 +190,7 @@ export async function getOverviewPortfolioData(
|
|||
scenarios: [
|
||||
{ scenarioName: "Today", data: data[0].co2PerUnitPreRetrofit || "" },
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: portfolioName || "Default",
|
||||
data: data[0].co2PerUnitPostRetrofit || "",
|
||||
},
|
||||
],
|
||||
|
|
@ -166,7 +203,7 @@ export async function getOverviewPortfolioData(
|
|||
data: data[0].energyBillPerUnitPreRetrofit || "",
|
||||
},
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: portfolioName || "Default",
|
||||
data: data[0].energyBillPerUnitPostRetrofit || "",
|
||||
},
|
||||
],
|
||||
|
|
@ -179,7 +216,7 @@ export async function getOverviewPortfolioData(
|
|||
data: data[0].energyConsumptionPerUnitPreRetrofit || "",
|
||||
},
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: portfolioName || "Default",
|
||||
data: data[0].energyConsumptionPerUnitPostRetrofit || "",
|
||||
},
|
||||
],
|
||||
|
|
@ -189,7 +226,7 @@ export async function getOverviewPortfolioData(
|
|||
scenarios: [
|
||||
{ scenarioName: "Today", data: "" },
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: portfolioName || "Default",
|
||||
data: "£" + formatNumber(data[0].cost || 0),
|
||||
},
|
||||
],
|
||||
|
|
@ -198,21 +235,30 @@ export async function getOverviewPortfolioData(
|
|||
title: "Cost (£)/unit",
|
||||
scenarios: [
|
||||
{ scenarioName: "Today", data: "" },
|
||||
{ scenarioName: portfolioName, data: data[0].costPerUnit || "" },
|
||||
{
|
||||
scenarioName: portfolioName || "Default",
|
||||
data: data[0].costPerUnit || "",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "£ per CO2 reduction",
|
||||
scenarios: [
|
||||
{ scenarioName: "Today", data: "" },
|
||||
{ scenarioName: portfolioName, data: data[0].costPerCo2Saved || "" },
|
||||
{
|
||||
scenarioName: portfolioName || "Default",
|
||||
data: data[0].costPerCo2Saved || "",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "£ per SAP point",
|
||||
scenarios: [
|
||||
{ scenarioName: "Today", data: "" },
|
||||
{ scenarioName: portfolioName, data: data[0].costPerSapPoint || "" },
|
||||
{
|
||||
scenarioName: portfolioName || "Default",
|
||||
data: data[0].costPerSapPoint || "",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -221,7 +267,7 @@ export async function getOverviewPortfolioData(
|
|||
scenarios: [
|
||||
{ scenarioName: "Today", data: "" },
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: portfolioName || "Default",
|
||||
data: data[0].valuationImprovementPerUnit || "",
|
||||
},
|
||||
],
|
||||
|
|
@ -232,7 +278,7 @@ export async function getOverviewPortfolioData(
|
|||
scenarios: [
|
||||
{ scenarioName: "Today", data: "" },
|
||||
{
|
||||
scenarioName: portfolioName,
|
||||
scenarioName: portfolioName || "Default",
|
||||
data: data[0].valuationReturnOnInvestment || "",
|
||||
},
|
||||
],
|
||||
|
|
@ -242,42 +288,25 @@ export async function getOverviewPortfolioData(
|
|||
return output;
|
||||
}
|
||||
|
||||
export async function getUserPortfolios(
|
||||
export async function getNonDefaultPortfolioScenarios(
|
||||
portfolioId: string
|
||||
): Promise<{ id: bigint; name: string }[]> {
|
||||
// This function will retrieve all of the portfolio ids attributed to a user
|
||||
// This can be done by querying the portfolioUsers table
|
||||
): Promise<{ id: bigint; name: string | null }[]> {
|
||||
// For a given portfolio Id, we want to retrieve all of the scenarios that are attributed to this portfolio
|
||||
// We exclude the default scenario
|
||||
const scenarios = await db
|
||||
.select({
|
||||
id: scenario.id,
|
||||
name: scenario.name,
|
||||
})
|
||||
.from(scenario)
|
||||
.where(
|
||||
and(
|
||||
eq(scenario.portfolioId, BigInt(portfolioId)),
|
||||
eq(scenario.isDefault, false)
|
||||
)
|
||||
);
|
||||
|
||||
const portfolioUser = await db.query.portfolioUsers.findFirst({
|
||||
where: eq(portfolioUsers.portfolioId, BigInt(portfolioId)),
|
||||
});
|
||||
|
||||
if (!portfolioUser) {
|
||||
throw new Error("Portfolio not found");
|
||||
}
|
||||
|
||||
const userId = portfolioUser?.userId;
|
||||
|
||||
if (!userId) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
|
||||
// We now filter portfolioUsers by the userId
|
||||
const data = await db.query.portfolioUsers.findMany({
|
||||
where: eq(portfolioUsers.userId, userId),
|
||||
with: {
|
||||
portfolio: {
|
||||
// Make sure this matches the relation name defined in relations setup
|
||||
columns: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Extract the portfolio data from the nested object
|
||||
return data.map((item) => item.portfolio);
|
||||
return scenarios;
|
||||
}
|
||||
|
||||
export async function getProperties(
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"allowSyntheticDefaultImports": true,
|
||||
// "target": "es5",
|
||||
"target": "ESNext",
|
||||
"target": "es5",
|
||||
// "target": "ESNext",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue