From ea600d49aaa4533e54a1f386b3de36a33f383caa Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Thu, 5 Sep 2024 18:42:35 +0100 Subject: [PATCH 01/28] adding variables to capture the impact of the new recommendations in the plan --- README.md | 8 +++++ .../building-passport/RecommendationCard.tsx | 4 +++ .../RecommendationContainer.tsx | 35 +++++++++++++++++++ src/app/db/schema/recommendations.ts | 5 ++- src/types/recommendations.ts | 3 ++ 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b58ac46f..e720d029 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next ## Getting Started +When first getting set up you'll firstly want to install the existing dependencies. To do this, simply run + +```bash +npm install +# or +yarn install +``` + First, run the development server: ```bash diff --git a/src/app/components/building-passport/RecommendationCard.tsx b/src/app/components/building-passport/RecommendationCard.tsx index cace8863..f53826c2 100644 --- a/src/app/components/building-passport/RecommendationCard.tsx +++ b/src/app/components/building-passport/RecommendationCard.tsx @@ -17,6 +17,7 @@ const alreadyInstalledStyling = const TitleMap = { mechanical_ventilation: "Mechanical Ventilation", + trickle_vents: "Trickle Vents", sealing_open_fireplace: "Sealing Open Fireplace", low_energy_lighting: "Low Energy Lighting", // Walls @@ -33,6 +34,7 @@ const TitleMap = { exposed_floor_insulation: "Exposed Floor Insulation", // Windows windows_glazing: "Window Glazing", + mixed_glazing: "Mixed - Secondary and Double Glazing", // Solar pv solar_pv: "Solar Photovoltaic Panels System", // Heating @@ -47,6 +49,8 @@ const TitleMap = { roof_insulation: "Roof Insulation", // Cylinder thermostat cylinder_thermostat: "Cylinder Thermostat", + // Draught proofing + draught_proofing: "Draught Proofing", }; type RecommendationCardProps = { diff --git a/src/app/components/building-passport/RecommendationContainer.tsx b/src/app/components/building-passport/RecommendationContainer.tsx index 951e6af4..50cf27b5 100644 --- a/src/app/components/building-passport/RecommendationContainer.tsx +++ b/src/app/components/building-passport/RecommendationContainer.tsx @@ -127,6 +127,21 @@ export default function RecommendationContainer({ (rec: Recommendation) => rec.default ) || emptyImpactState; + const defaultTrickleVentsRecommendations = + categorizedRecommendations.trickle_vents?.find( + (rec: Recommendation) => rec.default + ) || emptyImpactState; + + const defaultMixedGlazingRecommendations = + categorizedRecommendations.mixed_glazing?.find( + (rec: Recommendation) => rec.default + ) || emptyImpactState; + + const defaultDraughtProofingRecommendations = + categorizedRecommendations.draught_proofing?.find( + (rec: Recommendation) => rec.default + ) || emptyImpactState; + const [costMap, setCostMap] = useState({ wall_insulation: defaultWallsRecommendations.estimatedCost || 0, floor_insulation: defaultFloorRecommendations.estimatedCost || 0, @@ -145,6 +160,9 @@ export default function RecommendationContainer({ defaultSecondaryHeatingRecommendations.estimatedCost || 0, cylinder_thermostat: defaultCylinderThermostatRecommendations.estimatedCost || 0, + trickle_vents: defaultTrickleVentsRecommendations.estimatedCost || 0, + mixed_glazing: defaultMixedGlazingRecommendations.estimatedCost || 0, + draught_proofing: defaultDraughtProofingRecommendations.estimatedCost || 0, }); const [sapMap, setSapMap] = useState({ @@ -163,6 +181,9 @@ export default function RecommendationContainer({ secondary_heating: defaultSecondaryHeatingRecommendations.sapPoints || 0, cylinder_thermostat: defaultCylinderThermostatRecommendations.sapPoints || 0, + trickle_vents: defaultTrickleVentsRecommendations.sapPoints || 0, + mixed_glazing: defaultMixedGlazingRecommendations.sapPoints || 0, + draught_proofing: defaultDraughtProofingRecommendations.sapPoints || 0, }); const [labourDaysMap, setLabourDaysMap] = useState({ @@ -181,6 +202,9 @@ export default function RecommendationContainer({ secondary_heating: defaultSecondaryHeatingRecommendations.labourDays || 0, cylinder_thermostat: defaultCylinderThermostatRecommendations.labourDays || 0, + trickle_vents: defaultTrickleVentsRecommendations.labourDays || 0, + mixed_glazing: defaultMixedGlazingRecommendations.labourDays || 0, + draught_proofing: defaultDraughtProofingRecommendations.labourDays || 0, }); const [co2SavingsMap, setCo2SavingsMap] = useState({ @@ -204,6 +228,10 @@ export default function RecommendationContainer({ defaultSecondaryHeatingRecommendations.co2EquivalentSavings || 0, cylinder_thermostat: defaultCylinderThermostatRecommendations.co2EquivalentSavings || 0, + trickle_vents: defaultTrickleVentsRecommendations.co2EquivalentSavings || 0, + mixed_glazing: defaultMixedGlazingRecommendations.co2EquivalentSavings || 0, + draught_proofing: + defaultDraughtProofingRecommendations.co2EquivalentSavings || 0, }); const [energyCostSavingsMap, setEnergyCostSavingsMap] = @@ -228,6 +256,10 @@ export default function RecommendationContainer({ defaultSecondaryHeatingRecommendations.energyCostSavings || 0, cylinder_thermostat: defaultCylinderThermostatRecommendations.energyCostSavings || 0, + trickle_vents: defaultTrickleVentsRecommendations.energyCostSavings || 0, + mixed_glazing: defaultMixedGlazingRecommendations.energyCostSavings || 0, + draught_proofing: + defaultDraughtProofingRecommendations.energyCostSavings || 0, }); const [kwhSavingsMap, setKwhSavingsMap] = useState({ @@ -246,6 +278,9 @@ export default function RecommendationContainer({ secondary_heating: defaultSecondaryHeatingRecommendations.kwhSavings || 0, cylinder_thermostat: defaultCylinderThermostatRecommendations.kwhSavings || 0, + trickle_vents: defaultTrickleVentsRecommendations.kwhSavings || 0, + mixed_glazing: defaultMixedGlazingRecommendations.kwhSavings || 0, + draught_proofing: defaultDraughtProofingRecommendations.kwhSavings || 0, }); const [totalEstimatedCost, setTotalEstimatedCost] = useState( diff --git a/src/app/db/schema/recommendations.ts b/src/app/db/schema/recommendations.ts index b8899bba..d272b2a3 100644 --- a/src/app/db/schema/recommendations.ts +++ b/src/app/db/schema/recommendations.ts @@ -185,7 +185,10 @@ export type RecommendationType = | "hot_water_tank_insulation" | "heating_control" | "secondary_heating" - | "cylinder_thermostat"; + | "cylinder_thermostat" + | "trickle_vents" + | "mixed_glazing" + | "draught_proofing"; export type UnnestedRecommendation = { quantity: number; diff --git a/src/types/recommendations.ts b/src/types/recommendations.ts index e1139886..fd0f610f 100644 --- a/src/types/recommendations.ts +++ b/src/types/recommendations.ts @@ -12,4 +12,7 @@ export interface RecommendationMetricMap { heating_control: number; secondary_heating: number; cylinder_thermostat: number; + trickle_vents: number; + mixed_glazing: number; + draught_proofing: number; } From b550c47e1ae34a4ba2c8e8f7f9550d9d48ae49c6 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Thu, 5 Sep 2024 18:47:31 +0100 Subject: [PATCH 02/28] Adding structure for the energy assessment page --- .../components/building-passport/Toolbar.tsx | 18 +++++++++++------- .../[propertyId]/energy-assessment/page.tsx | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx diff --git a/src/app/components/building-passport/Toolbar.tsx b/src/app/components/building-passport/Toolbar.tsx index 8f57d216..58866edf 100644 --- a/src/app/components/building-passport/Toolbar.tsx +++ b/src/app/components/building-passport/Toolbar.tsx @@ -39,6 +39,16 @@ export function Toolbar({ propertyId, portfolioId }: ToolbarProps) { ); + const energyAssessmentsReportButton = ( + + + Energy Assessment + + ); + const solarAnalysisButton = ( - - Plan optimiser - */} + {energyAssessmentsReportButton} +
+
+ This property does not have a Domna energy assessement +
+

Please check back later for updates.

+
+ + ); +} From 8147a0d4b738dee655ee115de4b7de467c3b53fb Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 9 Sep 2024 12:15:55 +0100 Subject: [PATCH 03/28] Added the documents table to the app - needs api setup to generate pre-signed urls --- src/app/components/Buttons.tsx | 19 +++++ src/app/db/schema/energy_assessments.ts | 8 ++ .../energy-assessment/DocumentsTable.tsx | 85 +++++++++++++++++++ .../[propertyId]/energy-assessment/page.tsx | 48 +++++++++-- .../building-passport/[propertyId]/utils.ts | 39 ++++++++- 5 files changed, 191 insertions(+), 8 deletions(-) create mode 100644 src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx diff --git a/src/app/components/Buttons.tsx b/src/app/components/Buttons.tsx index 7da4ab2e..a5688009 100644 --- a/src/app/components/Buttons.tsx +++ b/src/app/components/Buttons.tsx @@ -18,3 +18,22 @@ export function TanButton({ ); } + +export function BrandBlueButton({ + label, + onClick, +}: { + label: string; + onClick: Dispatch>; +}) { + // General tan colored button + return ( + + ); +} diff --git a/src/app/db/schema/energy_assessments.ts b/src/app/db/schema/energy_assessments.ts index 44736f7b..6f2a1330 100644 --- a/src/app/db/schema/energy_assessments.ts +++ b/src/app/db/schema/energy_assessments.ts @@ -195,3 +195,11 @@ export const energyAssessmentDocuments = pgTable( // Types for the new table export type EnergyAssessment = InferModel; +export type EnergyAssessmentScenario = InferModel< + typeof energyAssessmentScenarios, + "select" +>; +export type EnergyAssessmentDocument = InferModel< + typeof energyAssessmentDocuments, + "select" +>; diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx new file mode 100644 index 00000000..49fae6e3 --- /dev/null +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx @@ -0,0 +1,85 @@ +"use client"; + +import React from "react"; +import { + Table, + TableBody, + TableCell, + TableRow, +} from "@/app/shadcn_components/ui/table"; +import { BrandBlueButton } from "@/app/components/Buttons"; +import { DocumentType } from "@/app/db/schema/energy_assessments"; + +// Use the type directly from the array +type Document = { + id: bigint; + documentType: (typeof DocumentType)[number]; // Create a union type from the array + documentLocation: string; + uploadedAt: Date; +}; + +type Props = { + documents: Document[]; + allowedTypes: (typeof DocumentType)[number][]; // Use the union type for allowedTypes as well +}; + +// Descriptions based on the document types +const descriptions: { [key in (typeof DocumentType)[number]]: string } = { + EPR: "Contains current energy performance of your home, and recommendations for improvement", + "Condition Report": + "Detailed report on property condition, including photographs", + "Evidence Report": "Additional photographic evidence from the survey", + "Summary Information": "Summarized survey outputs and site notes", + "Floor Plan": "Layout of the property and room/window dimensions", + "Scenario Draft EPC": "Draft EPC based on a given scenario", + "Scenario Site Notes": "Site notes from the scenario analysis", +}; + +export const DocumentsTable: React.FC = ({ + documents, + allowedTypes, +}) => { + const relevantDocuments = documents.filter((doc) => + allowedTypes.includes(doc.documentType) + ); + + // Track displayed descriptions + const displayedDescriptions: Record<(typeof DocumentType)[number], boolean> = + allowedTypes.reduce((acc, type) => { + acc[type] = false; + return acc; + }, {} as Record<(typeof DocumentType)[number], boolean>); + + const processedDocuments = relevantDocuments.map((doc) => { + const showDescription = !displayedDescriptions[doc.documentType]; + if (showDescription) { + displayedDescriptions[doc.documentType] = true; + } + return { ...doc, showDescription }; + }); + + return ( + + + {processedDocuments.map((doc) => ( + + + {doc.documentType} + + + {doc.showDescription ? descriptions[doc.documentType] : ""} + + + + console.log(`Downloading ${doc.documentLocation}`) + } + /> + + + ))} + +
+ ); +}; diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx index 925a9e2f..936f80bd 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx @@ -1,17 +1,51 @@ +import { + getEnergyAssessment, + getEnergyAssessmentDocuments, + getPropertyMeta, +} from "../utils"; + +// EnergyAssessmentsPage.tsx +import { DocumentsTable } from "./DocumentsTable"; + export default async function EnergyAssessmentsPage({ params, }: { params: { slug: string; propertyId: string }; }) { - // If there's no solar data, we cannot display the page + const propertyMeta = await getPropertyMeta(params.propertyId); + const ea = await getEnergyAssessment(propertyMeta.uprn); + + if (!ea) { + return ( +
+
+
+ This property does not have a Domna energy assessment +
+

Please check back later for updates.

+
+
+ ); + } + + const documents = await getEnergyAssessmentDocuments(ea.id); + + const table1AllowedTypes = [ + "EPR", + "Condition Report", + "Evidence Report", + "Summary Information", + "Floor Plan", + ]; return ( -
-
-
- This property does not have a Domna energy assessement -
-

Please check back later for updates.

+
+
Core Survey Documents
+
+
); diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/utils.ts b/src/app/portfolio/[slug]/building-passport/[propertyId]/utils.ts index d33e968e..4e0f1361 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/utils.ts +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/utils.ts @@ -1,4 +1,3 @@ -import { recommendation } from "../../../../db/schema/recommendations"; import { Recommendation, planRecommendations, @@ -18,11 +17,49 @@ import { import { plan, Plan } from "@/app/db/schema/recommendations"; import { getRating } from "@/app/utils"; import { eq, desc } from "drizzle-orm"; +import { + energyAssessment, + EnergyAssessment, + energyAssessmentDocuments, + EnergyAssessmentDocument, +} from "@/app/db/schema/energy_assessments"; type RecommendationList = { recommendation: Recommendation; }[]; +export async function getEnergyAssessment( + uprn: number +): Promise { + const data = await db.query.energyAssessment.findFirst({ + where: eq(energyAssessment.uprn, BigInt(uprn)), + }); + + if (!data) { + // If there's no data, we return an empty array. This signififies that no energy assessment has been conducted + return {} as EnergyAssessment; + } + + return data; +} + +export async function getEnergyAssessmentDocuments( + energyAssessmentId: bigint +): Promise { + const data = await db.query.energyAssessmentDocuments.findMany({ + where: eq( + energyAssessmentDocuments.energyAssessmentId, + BigInt(energyAssessmentId) + ), + }); + + if (!data) { + throw new Error("Network response was not ok"); + } + + return data; +} + export async function getRecommendations( planId: string ): Promise { From 07be8eaf8407e0dab6e6661e00bf8ccc589774e8 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 9 Sep 2024 14:52:34 +0100 Subject: [PATCH 04/28] presign api working but want to use temp aws credentials| --- .../api/energy-assessment-documents/route.ts | 49 +++++++++++++++++ .../energy-assessment/DocumentsTable.tsx | 54 ++++++++++++------- 2 files changed, 84 insertions(+), 19 deletions(-) create mode 100644 src/app/api/energy-assessment-documents/route.ts diff --git a/src/app/api/energy-assessment-documents/route.ts b/src/app/api/energy-assessment-documents/route.ts new file mode 100644 index 00000000..89b9140d --- /dev/null +++ b/src/app/api/energy-assessment-documents/route.ts @@ -0,0 +1,49 @@ +// pages/api/get-presigned-url.ts +import S3 from "aws-sdk/clients/s3"; +import { NextRequest, NextResponse } from "next/server"; +import { z } from "zod"; + +const PresignedUrlBodySchema = z.object({ + fileKey: z.string(), +}); + +export async function POST(request: NextRequest) { + const body = await request.json(); + let validatedBody; + + try { + validatedBody = PresignedUrlBodySchema.parse(body); + } catch (error) { + console.error("Invalid input: ", error); + return new NextResponse(JSON.stringify({ msg: "Invalid input" }), { + status: 400, + }); + } + + try { + const s3 = new S3({ + signatureVersion: "v4", + region: process.env.PRESIGN_AWS_REGION, + accessKeyId: process.env.RETROFIT_ENERGY_ASSESSMENTS_AWS_ACCESS_KEY, + secretAccessKey: process.env.ENERGY_ASSESSMENTS_AWS_SECRET, + }); + + const { fileKey } = validatedBody; + + // Presigned URL is valid for 5 minutes + const preSignedUrl = await s3.getSignedUrl("getObject", { + Bucket: process.env.RETROFIT_ENERGY_ASSESSMENTS_BUCKET, + Key: fileKey, + Expires: 5 * 60, + }); + + return new NextResponse(JSON.stringify({ url: preSignedUrl }), { + status: 200, + }); + } catch (error) { + console.error(error); + return new NextResponse(JSON.stringify({ msg: "Internal server error" }), { + status: 500, + }); + } +} diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx index 49fae6e3..05d63182 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx @@ -8,13 +8,14 @@ import { TableRow, } from "@/app/shadcn_components/ui/table"; import { BrandBlueButton } from "@/app/components/Buttons"; +import { useMutation } from "@tanstack/react-query"; import { DocumentType } from "@/app/db/schema/energy_assessments"; // Use the type directly from the array type Document = { id: bigint; documentType: (typeof DocumentType)[number]; // Create a union type from the array - documentLocation: string; + documentLocation: string; // S3 file key uploadedAt: Date; }; @@ -35,6 +36,21 @@ const descriptions: { [key in (typeof DocumentType)[number]]: string } = { "Scenario Site Notes": "Site notes from the scenario analysis", }; +// Fetch the presigned URL from the API +async function generatePresignedUrl(fileKey: string) { + const response = await fetch("/api/energy-assessment-documents", { + method: "POST", + body: JSON.stringify({ fileKey }), + }); + + if (!response.ok) { + throw new Error("Failed to generate presigned URL"); + } + + const data = await response.json(); + return data.url; +} + export const DocumentsTable: React.FC = ({ documents, allowedTypes, @@ -43,38 +59,38 @@ export const DocumentsTable: React.FC = ({ allowedTypes.includes(doc.documentType) ); - // Track displayed descriptions - const displayedDescriptions: Record<(typeof DocumentType)[number], boolean> = - allowedTypes.reduce((acc, type) => { - acc[type] = false; - return acc; - }, {} as Record<(typeof DocumentType)[number], boolean>); - - const processedDocuments = relevantDocuments.map((doc) => { - const showDescription = !displayedDescriptions[doc.documentType]; - if (showDescription) { - displayedDescriptions[doc.documentType] = true; + // Mutation to handle the presigned URL generation + const { mutate: fetchPresignedUrl } = useMutation( + async (fileKey: string) => await generatePresignedUrl(fileKey), + { + onSuccess: (url) => { + window.open(url, "_blank"); // Open the file in a new tab + }, + onError: (error) => { + console.error("Error generating presigned URL:", error); + }, } - return { ...doc, showDescription }; - }); + ); + + const handleDownload = (documentLocation: string) => { + fetchPresignedUrl(documentLocation); // Generate URL and open in new tab + }; return ( - {processedDocuments.map((doc) => ( + {relevantDocuments.map((doc) => ( {doc.documentType} - {doc.showDescription ? descriptions[doc.documentType] : ""} + {descriptions[doc.documentType] || ""} - console.log(`Downloading ${doc.documentLocation}`) - } + onClick={() => handleDownload(doc.documentLocation)} // Call the download handler /> From 2b17aad9a791a151e4f48a36c9f2ddd85deb1d50 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 9 Sep 2024 15:00:14 +0100 Subject: [PATCH 05/28] using rotating aws credentials --- .../api/energy-assessment-documents/route.ts | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/app/api/energy-assessment-documents/route.ts b/src/app/api/energy-assessment-documents/route.ts index 89b9140d..db263ca2 100644 --- a/src/app/api/energy-assessment-documents/route.ts +++ b/src/app/api/energy-assessment-documents/route.ts @@ -1,12 +1,28 @@ // pages/api/get-presigned-url.ts import S3 from "aws-sdk/clients/s3"; +import STS from "aws-sdk/clients/sts"; // Import STS for temporary credentials import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; +// Validate the input const PresignedUrlBodySchema = z.object({ fileKey: z.string(), }); +// Function to get temporary credentials using GetSessionToken +async function getTemporaryCredentials() { + const sts = new STS({ + accessKeyId: process.env.RETROFIT_ENERGY_ASSESSMENTS_AWS_ACCESS_KEY, // Your permanent access key + secretAccessKey: process.env.ENERGY_ASSESSMENTS_AWS_SECRET, // Your permanent secret access key + region: process.env.PRESIGN_AWS_REGION, + }); + + // Request temporary credentials with GetSessionToken + const data = await sts.getSessionToken({ DurationSeconds: 900 }).promise(); // Token valid for 15 minutes + return data.Credentials; +} + +// API handler export async function POST(request: NextRequest) { const body = await request.json(); let validatedBody; @@ -21,27 +37,32 @@ export async function POST(request: NextRequest) { } try { + // Get temporary credentials using GetSessionToken + const credentials = await getTemporaryCredentials(); + + // Initialize S3 with temporary credentials const s3 = new S3({ signatureVersion: "v4", region: process.env.PRESIGN_AWS_REGION, - accessKeyId: process.env.RETROFIT_ENERGY_ASSESSMENTS_AWS_ACCESS_KEY, - secretAccessKey: process.env.ENERGY_ASSESSMENTS_AWS_SECRET, + accessKeyId: credentials.AccessKeyId, + secretAccessKey: credentials.SecretAccessKey, + sessionToken: credentials.SessionToken, // Include session token }); const { fileKey } = validatedBody; - // Presigned URL is valid for 5 minutes + // Generate presigned URL valid for 5 minutes const preSignedUrl = await s3.getSignedUrl("getObject", { Bucket: process.env.RETROFIT_ENERGY_ASSESSMENTS_BUCKET, Key: fileKey, - Expires: 5 * 60, + Expires: 5 * 60, // URL expiration in seconds }); return new NextResponse(JSON.stringify({ url: preSignedUrl }), { status: 200, }); } catch (error) { - console.error(error); + console.error("Error generating presigned URL:", error); return new NextResponse(JSON.stringify({ msg: "Internal server error" }), { status: 500, }); From 22aea5ec6e175ce983fec78cef2f3a689b340d08 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 9 Sep 2024 15:12:41 +0100 Subject: [PATCH 06/28] fixing typescript error --- .../api/energy-assessment-documents/route.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/app/api/energy-assessment-documents/route.ts b/src/app/api/energy-assessment-documents/route.ts index db263ca2..86512b72 100644 --- a/src/app/api/energy-assessment-documents/route.ts +++ b/src/app/api/energy-assessment-documents/route.ts @@ -17,9 +17,20 @@ async function getTemporaryCredentials() { region: process.env.PRESIGN_AWS_REGION, }); - // Request temporary credentials with GetSessionToken - const data = await sts.getSessionToken({ DurationSeconds: 900 }).promise(); // Token valid for 15 minutes - return data.Credentials; + try { + // Request temporary credentials with GetSessionToken + const data = await sts.getSessionToken({ DurationSeconds: 900 }).promise(); // Token valid for 15 minutes + + // Check if credentials are present + if (!data.Credentials) { + throw new Error("Failed to retrieve temporary credentials"); + } + + return data.Credentials; + } catch (error) { + console.error("Error fetching temporary credentials:", error); + throw error; + } } // API handler From fd0417f3e38aa1fa7f5ac2dfc7abbb5914202118 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 9 Sep 2024 16:42:21 +0100 Subject: [PATCH 07/28] styling energy assessment page --- src/app/components/Buttons.tsx | 15 +- src/app/components/StatusBadge.tsx | 6 +- .../components/building-passport/EpcCard.tsx | 21 ++- src/app/db/schema/energy_assessments.ts | 6 + src/app/db/schema/relations.ts | 15 ++ src/app/layout.tsx | 1 - .../energy-assessment/DocumentsTable.tsx | 5 +- .../[propertyId]/energy-assessment/page.tsx | 148 +++++++++++++++++- .../building-passport/[propertyId]/utils.ts | 6 +- tailwind.config.js | 1 + 10 files changed, 203 insertions(+), 21 deletions(-) diff --git a/src/app/components/Buttons.tsx b/src/app/components/Buttons.tsx index a5688009..9d2d87d7 100644 --- a/src/app/components/Buttons.tsx +++ b/src/app/components/Buttons.tsx @@ -19,18 +19,27 @@ export function TanButton({ ); } -export function BrandBlueButton({ +export function BrandButton({ label, onClick, + backgroundColor, }: { label: string; onClick: Dispatch>; + backgroundColor: "brandblue" | "brandgold"; // Restrict backgroundColor to these two options }) { - // General tan colored button + // Dictionary to map background colors to hover colors + const hoverColors = { + brandblue: "hover:bg-hoverblue", + brandgold: "hover:bg-hovergold", + }; + return (
{kwh && ( - {" "} - {/* Added vertical padding to each row */} )} {carbon && ( - {" "} )} diff --git a/src/app/db/schema/energy_assessments.ts b/src/app/db/schema/energy_assessments.ts index 6f2a1330..61d924ea 100644 --- a/src/app/db/schema/energy_assessments.ts +++ b/src/app/db/schema/energy_assessments.ts @@ -203,3 +203,9 @@ export type EnergyAssessmentDocument = InferModel< typeof energyAssessmentDocuments, "select" >; + +// We define a type for the energyassessment docments that embeds a scenario in +// the document +export type EnergyAssessmentDocumentWithScenario = EnergyAssessmentDocument & { + scenario: EnergyAssessmentScenario | null; +}; diff --git a/src/app/db/schema/relations.ts b/src/app/db/schema/relations.ts index dfc44bd6..87e7d4cc 100644 --- a/src/app/db/schema/relations.ts +++ b/src/app/db/schema/relations.ts @@ -1,3 +1,7 @@ +import { + energyAssessmentDocuments, + energyAssessmentScenarios, +} from "./energy_assessments"; // This script contains ALL relations for the database, used by drizzle-orm import { relations } from "drizzle-orm"; @@ -139,3 +143,14 @@ export const nonIntrusiveSurveyNotesRelations = relations( }), }) ); + +// Define a relation from a EnergyAssessmentDocument to EnergyAssessmentScenario. This is a many to one +export const energyAssessmentDocumentsRelations = relations( + energyAssessmentDocuments, + ({ one }) => ({ + scenario: one(energyAssessmentScenarios, { + fields: [energyAssessmentDocuments.scenarioId], + references: [energyAssessmentScenarios.id], + }), + }) +); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 1aa6c8e0..c2f4e51f 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -10,7 +10,6 @@ import { Inter } from "next/font/google"; // If loading a variable font, you don't need to specify the font weight const inter = Inter({ subsets: ["latin"], - display: "swap", }); export const metadata = { diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx index 05d63182..e142b93f 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx @@ -7,7 +7,7 @@ import { TableCell, TableRow, } from "@/app/shadcn_components/ui/table"; -import { BrandBlueButton } from "@/app/components/Buttons"; +import { BrandButton } from "@/app/components/Buttons"; import { useMutation } from "@tanstack/react-query"; import { DocumentType } from "@/app/db/schema/energy_assessments"; @@ -88,9 +88,10 @@ export const DocumentsTable: React.FC = ({ {descriptions[doc.documentType] || ""} - handleDownload(doc.documentLocation)} // Call the download handler + backgroundColor="brandgold" /> diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx index 936f80bd..092d0e43 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx @@ -1,11 +1,67 @@ +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "@/app/shadcn_components/ui/card"; +import EpcCard from "@/app/components/building-passport/EpcCard"; import { getEnergyAssessment, getEnergyAssessmentDocuments, getPropertyMeta, } from "../utils"; - -// EnergyAssessmentsPage.tsx import { DocumentsTable } from "./DocumentsTable"; +import { getEpcColorClass } from "@/app/utils"; + +// Helper function to clean scenario names +function cleanScenarioName(scenarioName: string | undefined) { + if (!scenarioName) { + return scenarioName; + } + + return scenarioName.startsWith("Scenario") + ? scenarioName.replace(/^Scenario\s*/i, "").trim() + : scenarioName; +} + +type InfoCardProps = { + title: string; + value: number | string; + unit: string; +}; + +export const InfoCard: React.FC = ({ title, value, unit }) => { + const isEnergyRating = title === "Energy Rating"; + const bgColorClass = isEnergyRating ? getEpcColorClass(value.toString()) : ""; // Get the EPC color if it's Energy Rating + + return ( + + + + {title} + + + +
+ {value} + {unit} +
+
+
+ ); +}; export default async function EnergyAssessmentsPage({ params, @@ -38,15 +94,93 @@ export default async function EnergyAssessmentsPage({ "Floor Plan", ]; + const scenarioAllowedTypes = ["Scenario Draft EPC", "Scenario Site Notes"]; + + // Separate core documents and scenario-specific documents + const coreDocuments = documents.filter( + (doc) => + doc.scenarioId === null && table1AllowedTypes.includes(doc.documentType) + ); + + const scenarioDocuments = documents.filter( + (doc) => + doc.scenarioId !== null && scenarioAllowedTypes.includes(doc.documentType) + ); + + // Extract unique scenarios from the documents + const scenarios = Array.from( + new Set(scenarioDocuments.map((doc) => doc.scenario?.scenarioName)) + ).filter(Boolean); // Filter out any null or undefined scenario names + return (
-
Core Survey Documents
-
- + {/* EPC Rating Card */} + + + + + + + + + + {/* Estimated Hot Water kWh */} +
+ + {/* Core Survey Documents */} +
+
+ Core Survey Documents +
+
+ +
+
+ + {/* Scenario-Specific Documents */} + {scenarios.map((scenarioName) => { + const scenarioDocs = scenarioDocuments.filter( + (doc) => doc.scenario?.scenarioName === scenarioName + ); + + return ( +
+
+ Scenario: {cleanScenarioName(scenarioName)} +
+ +
+ ); + })}
); } diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/utils.ts b/src/app/portfolio/[slug]/building-passport/[propertyId]/utils.ts index 4e0f1361..37ded61a 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/utils.ts +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/utils.ts @@ -22,6 +22,7 @@ import { EnergyAssessment, energyAssessmentDocuments, EnergyAssessmentDocument, + EnergyAssessmentDocumentWithScenario, } from "@/app/db/schema/energy_assessments"; type RecommendationList = { @@ -45,12 +46,15 @@ export async function getEnergyAssessment( export async function getEnergyAssessmentDocuments( energyAssessmentId: bigint -): Promise { +): Promise { const data = await db.query.energyAssessmentDocuments.findMany({ where: eq( energyAssessmentDocuments.energyAssessmentId, BigInt(energyAssessmentId) ), + with: { + scenario: true, + }, }); if (!data) { diff --git a/tailwind.config.js b/tailwind.config.js index 8e2051d0..7521e9e3 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -101,6 +101,7 @@ module.exports = { brandtan: "#d3b488", hovertan: "#947750", brandgold: "#f1bb06", + hovergold: "#c79d12", brandbrown: "#3d1e05", brandmidblue: "#3943b7", border: "hsl(var(--border))", From f71670305002ef1ce72d9b4d7ddaf0e11f521568 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 9 Sep 2024 16:47:08 +0100 Subject: [PATCH 08/28] removing epc card --- .../building-passport/[propertyId]/energy-assessment/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx index 092d0e43..871f9b0b 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx @@ -4,7 +4,7 @@ import { CardHeader, CardTitle, } from "@/app/shadcn_components/ui/card"; -import EpcCard from "@/app/components/building-passport/EpcCard"; + import { getEnergyAssessment, getEnergyAssessmentDocuments, @@ -30,7 +30,7 @@ type InfoCardProps = { unit: string; }; -export const InfoCard: React.FC = ({ title, value, unit }) => { +const InfoCard: React.FC = ({ title, value, unit }) => { const isEnergyRating = title === "Energy Rating"; const bgColorClass = isEnergyRating ? getEpcColorClass(value.toString()) : ""; // Get the EPC color if it's Energy Rating From 0311c770eb9b1e41f759ff6fbae03bdc781e5e64 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 9 Sep 2024 17:05:40 +0100 Subject: [PATCH 09/28] Added new status type --- src/app/components/StatusBadge.tsx | 6 + src/app/db/migrations/0096_married_umar.sql | 1 + src/app/db/migrations/meta/0096_snapshot.json | 2939 +++++++++++++++++ src/app/db/migrations/meta/_journal.json | 7 + src/app/db/schema/portfolio.ts | 1 + 5 files changed, 2954 insertions(+) create mode 100644 src/app/db/migrations/0096_married_umar.sql create mode 100644 src/app/db/migrations/meta/0096_snapshot.json diff --git a/src/app/components/StatusBadge.tsx b/src/app/components/StatusBadge.tsx index f4818c39..0999b1e6 100644 --- a/src/app/components/StatusBadge.tsx +++ b/src/app/components/StatusBadge.tsx @@ -64,6 +64,12 @@ const statusColor: { hoverText: "This portfolio is currently in the assessment stage", propertyHoverText: "This property is currently in the assessment stage", }, + survey: { + class: "bg-emerald-500 hover:bg-emerald-500", + text: "survey", + hoverText: "This portfolio is currently in the survey stage", + propertyHoverText: "This property is currently in the survey stage", + }, tendering: { class: "bg-emerald-500 hover:bg-emerald-500", text: "tendering", diff --git a/src/app/db/migrations/0096_married_umar.sql b/src/app/db/migrations/0096_married_umar.sql new file mode 100644 index 00000000..701a081e --- /dev/null +++ b/src/app/db/migrations/0096_married_umar.sql @@ -0,0 +1 @@ +ALTER TYPE "status" ADD VALUE 'survey'; \ No newline at end of file diff --git a/src/app/db/migrations/meta/0096_snapshot.json b/src/app/db/migrations/meta/0096_snapshot.json new file mode 100644 index 00000000..aa2d4dff --- /dev/null +++ b/src/app/db/migrations/meta/0096_snapshot.json @@ -0,0 +1,2939 @@ +{ + "version": "5", + "dialect": "pg", + "id": "3a1cf4ff-f281-43fd-bd26-76684746ac05", + "prevId": "9466abc6-4c85-4936-8b79-69ebe1ad8753", + "tables": { + "energy_assessments": { + "name": "energy_assessments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "uprn_source": { + "name": "uprn_source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "property_type": { + "name": "property_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "building_reference_number": { + "name": "building_reference_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "current_energy_efficiency": { + "name": "current_energy_efficiency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_energy_rating": { + "name": "current_energy_rating", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "address1": { + "name": "address1", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "address2": { + "name": "address2", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "address3": { + "name": "address3", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "posttown": { + "name": "posttown", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "postcode": { + "name": "postcode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "county": { + "name": "county", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "constituency": { + "name": "constituency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "constituency_label": { + "name": "constituency_label", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "low_energy_fixed_light_count": { + "name": "low_energy_fixed_light_count", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "construction_age_band": { + "name": "construction_age_band", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mainheat_energy_eff": { + "name": "mainheat_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "windows_env_eff": { + "name": "windows_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lighting_energy_eff": { + "name": "lighting_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environment_impact_potential": { + "name": "environment_impact_potential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mainheatcont_description": { + "name": "mainheatcont_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sheating_energy_eff": { + "name": "sheating_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "local_authority": { + "name": "local_authority", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "local_authority_label": { + "name": "local_authority_label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fixed_lighting_outlets_count": { + "name": "fixed_lighting_outlets_count", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "energy_tariff": { + "name": "energy_tariff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mechanical_ventilation": { + "name": "mechanical_ventilation", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "solar_water_heating_flag": { + "name": "solar_water_heating_flag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "co2_emissions_potential": { + "name": "co2_emissions_potential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "number_heated_rooms": { + "name": "number_heated_rooms", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "floor_description": { + "name": "floor_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "energy_consumption_potential": { + "name": "energy_consumption_potential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "built_form": { + "name": "built_form", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "number_open_fireplaces": { + "name": "number_open_fireplaces", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "windows_description": { + "name": "windows_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "glazed_area": { + "name": "glazed_area", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "inspection_date": { + "name": "inspection_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true + }, + "mains_gas_flag": { + "name": "mains_gas_flag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "co2_emiss_curr_per_floor_area": { + "name": "co2_emiss_curr_per_floor_area", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "heat_loss_corridor": { + "name": "heat_loss_corridor", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "unheated_corridor_length": { + "name": "unheated_corridor_length", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "flat_storey_count": { + "name": "flat_storey_count", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "roof_energy_eff": { + "name": "roof_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "total_floor_area": { + "name": "total_floor_area", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environment_impact_current": { + "name": "environment_impact_current", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "roof_description": { + "name": "roof_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "floor_energy_eff": { + "name": "floor_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "number_habitable_rooms": { + "name": "number_habitable_rooms", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hot_water_env_eff": { + "name": "hot_water_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mainheatc_energy_eff": { + "name": "mainheatc_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "main_fuel": { + "name": "main_fuel", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lighting_env_eff": { + "name": "lighting_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "windows_energy_eff": { + "name": "windows_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "floor_env_eff": { + "name": "floor_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sheating_env_eff": { + "name": "sheating_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lighting_description": { + "name": "lighting_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "roof_env_eff": { + "name": "roof_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "walls_energy_eff": { + "name": "walls_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "photo_supply": { + "name": "photo_supply", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lighting_cost_potential": { + "name": "lighting_cost_potential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mainheat_env_eff": { + "name": "mainheat_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "multi_glaze_proportion": { + "name": "multi_glaze_proportion", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "main_heating_controls": { + "name": "main_heating_controls", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "flat_top_storey": { + "name": "flat_top_storey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secondheat_description": { + "name": "secondheat_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "walls_env_eff": { + "name": "walls_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "transaction_type": { + "name": "transaction_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "extension_count": { + "name": "extension_count", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mainheatc_env_eff": { + "name": "mainheatc_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lmk_key": { + "name": "lmk_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "wind_turbine_count": { + "name": "wind_turbine_count", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenure": { + "name": "tenure", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "floor_level": { + "name": "floor_level", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "potential_energy_efficiency": { + "name": "potential_energy_efficiency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "potential_energy_rating": { + "name": "potential_energy_rating", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hot_water_energy_eff": { + "name": "hot_water_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "low_energy_lighting": { + "name": "low_energy_lighting", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "walls_description": { + "name": "walls_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hotwater_description": { + "name": "hotwater_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "co2_emissions_current": { + "name": "co2_emissions_current", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "heating_cost_current": { + "name": "heating_cost_current", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "heating_cost_potential": { + "name": "heating_cost_potential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hot_water_cost_current": { + "name": "hot_water_cost_current", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hot_water_cost_potential": { + "name": "hot_water_cost_potential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lighting_cost_current": { + "name": "lighting_cost_current", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "energy_consumption_current": { + "name": "energy_consumption_current", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lodgement_date": { + "name": "lodgement_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "lodgement_datetime": { + "name": "lodgement_datetime", + "type": "timestamp (6)", + "primaryKey": false, + "notNull": true + }, + "mainheat_description": { + "name": "mainheat_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "floor_height": { + "name": "floor_height", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "glazed_type": { + "name": "glazed_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_location": { + "name": "file_location", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "surveyor_name": { + "name": "surveyor_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "surveyor_company": { + "name": "surveyor_company", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "space_heating_kwh": { + "name": "space_heating_kwh", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "water_heating_kwh": { + "name": "water_heating_kwh", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "number_of_doors": { + "name": "number_of_doors", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "number_of_insulated_doors": { + "name": "number_of_insulated_doors", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "number_of_floors": { + "name": "number_of_floors", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "insulation_wall_area": { + "name": "insulation_wall_area", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "heat_loss_perimeter": { + "name": "heat_loss_perimeter", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "party_wall_length": { + "name": "party_wall_length", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "perimeter": { + "name": "perimeter", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "rooms_with_bath_and_or_shower": { + "name": "rooms_with_bath_and_or_shower", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "rooms_with_mixer_shower_no_bath": { + "name": "rooms_with_mixer_shower_no_bath", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "room_with_bath_and_mixer_shower": { + "name": "room_with_bath_and_mixer_shower", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "percent_draftproofed": { + "name": "percent_draftproofed", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "has_hot_water_cylinder": { + "name": "has_hot_water_cylinder", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "cylinder_insulation_type": { + "name": "cylinder_insulation_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cylinder_insulation_thickness": { + "name": "cylinder_insulation_thickness", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cylinder_thermostat": { + "name": "cylinder_thermostat", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "main_dwelling_ground_floor_area": { + "name": "main_dwelling_ground_floor_area", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "number_of_windows": { + "name": "number_of_windows", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "windows_area": { + "name": "windows_area", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {} + }, + "energy_assessment_documents": { + "name": "energy_assessment_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "energy_assessment_id": { + "name": "energy_assessment_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "document_type": { + "name": "document_type", + "type": "document_type", + "primaryKey": false, + "notNull": true + }, + "document_location": { + "name": "document_location", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "scenario_id": { + "name": "scenario_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "energy_assessment_documents_energy_assessment_id_energy_assessments_id_fk": { + "name": "energy_assessment_documents_energy_assessment_id_energy_assessments_id_fk", + "tableFrom": "energy_assessment_documents", + "tableTo": "energy_assessments", + "columnsFrom": [ + "energy_assessment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "energy_assessment_documents_scenario_id_energy_assessment_scenarios_id_fk": { + "name": "energy_assessment_documents_scenario_id_energy_assessment_scenarios_id_fk", + "tableFrom": "energy_assessment_documents", + "tableTo": "energy_assessment_scenarios", + "columnsFrom": [ + "scenario_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {} + }, + "energy_assessment_scenarios": { + "name": "energy_assessment_scenarios", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "scenario_name": { + "name": "scenario_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "energy_assessment_id": { + "name": "energy_assessment_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "energy_assessment_scenarios_energy_assessment_id_energy_assessments_id_fk": { + "name": "energy_assessment_scenarios_energy_assessment_id_energy_assessments_id_fk", + "tableFrom": "energy_assessment_scenarios", + "tableTo": "energy_assessments", + "columnsFrom": [ + "energy_assessment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {} + }, + "material": { + "name": "material", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "type", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "depth": { + "name": "depth", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "depth_unit": { + "name": "depth_unit", + "type": "depth_unit", + "primaryKey": false, + "notNull": false + }, + "cost_unit": { + "name": "cost_unit", + "type": "cost_unit", + "primaryKey": false, + "notNull": false + }, + "r_value_per_mm": { + "name": "r_value_per_mm", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "r_value_unit": { + "name": "r_value_unit", + "type": "r_value_unit", + "primaryKey": false, + "notNull": false + }, + "thermal_conductivity": { + "name": "thermal_conductivity", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "thermal_conductivity_unit": { + "name": "thermal_conductivity_unit", + "type": "thermal_conductivity_unit", + "primaryKey": false, + "notNull": false + }, + "link": { + "name": "link", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "prime_material_cost": { + "name": "prime_material_cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "material_cost": { + "name": "material_cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "labour_cost": { + "name": "labour_cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "labour_hours_per_unit": { + "name": "labour_hours_per_unit", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "plant_cost": { + "name": "plant_cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "total_cost": { + "name": "total_cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "cost": { + "name": "cost", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_installer_quote": { + "name": "is_installer_quote", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {} + }, + "portfolio": { + "name": "portfolio", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "budget": { + "name": "budget", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "status", + "primaryKey": false, + "notNull": true + }, + "goal": { + "name": "goal", + "type": "goal", + "primaryKey": false, + "notNull": true + }, + "cost": { + "name": "cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "number_of_properties": { + "name": "number_of_properties", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "co2_equivalent_savings": { + "name": "co2_equivalent_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_savings": { + "name": "energy_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_cost_savings": { + "name": "energy_cost_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "property_valuation_increase": { + "name": "property_valuation_increase", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "rental_yield_increase": { + "name": "rental_yield_increase", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "total_work_hours": { + "name": "total_work_hours", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "labour_days": { + "name": "labour_days", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "epc_breakdown_pre_retrofit": { + "name": "epc_breakdown_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "epc_breakdown_post_retrofit": { + "name": "epc_breakdown_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "n_units_to_retrofit": { + "name": "n_units_to_retrofit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "co2_per_unit_pre_retrofit": { + "name": "co2_per_unit_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "co2_per_unit_post_retrofit": { + "name": "co2_per_unit_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_bill_per_unit_pre_retrofit": { + "name": "energy_bill_per_unit_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_bill_per_unit_post_retrofit": { + "name": "energy_bill_per_unit_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_consumption_per_unit_pre_retrofit": { + "name": "energy_consumption_per_unit_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_consumption_per_unit_post_retrofit": { + "name": "energy_consumption_per_unit_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valuation_improvement_per_unit": { + "name": "valuation_improvement_per_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost_per_unit": { + "name": "cost_per_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost_per_co2_saved": { + "name": "cost_per_co2_saved", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost_per_sap_point": { + "name": "cost_per_sap_point", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valuation_return_on_investment": { + "name": "valuation_return_on_investment", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {} + }, + "portfolioUsers": { + "name": "portfolioUsers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "role", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "portfolioUsers_user_id_user_id_fk": { + "name": "portfolioUsers_user_id_user_id_fk", + "tableFrom": "portfolioUsers", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "portfolioUsers_portfolio_id_portfolio_id_fk": { + "name": "portfolioUsers_portfolio_id_portfolio_id_fk", + "tableFrom": "portfolioUsers", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {} + }, + "non_intrusive_survey": { + "name": "non_intrusive_survey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "survey_date": { + "name": "survey_date", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "surveyor": { + "name": "surveyor", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {} + }, + "non_intrusive_survey_notes": { + "name": "non_intrusive_survey_notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "survey_id": { + "name": "survey_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "non_intrusive_survey_notes_survey_id_non_intrusive_survey_id_fk": { + "name": "non_intrusive_survey_notes_survey_id_non_intrusive_survey_id_fk", + "tableFrom": "non_intrusive_survey_notes", + "tableTo": "non_intrusive_survey", + "columnsFrom": [ + "survey_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {} + }, + "property": { + "name": "property", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "creation_status": { + "name": "creation_status", + "type": "creation_status", + "primaryKey": false, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "building_reference_number": { + "name": "building_reference_number", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "status", + "primaryKey": false, + "notNull": false + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postcode": { + "name": "postcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "has_pre_condition_report": { + "name": "has_pre_condition_report", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "has_recommendations": { + "name": "has_recommendations", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "property_type": { + "name": "property_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "built_form": { + "name": "built_form", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "local_authority": { + "name": "local_authority", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "constituency": { + "name": "constituency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "number_of_rooms": { + "name": "number_of_rooms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "year_built": { + "name": "year_built", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenure": { + "name": "tenure", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "current_epc_rating": { + "name": "current_epc_rating", + "type": "epc", + "primaryKey": false, + "notNull": false + }, + "current_sap_points": { + "name": "current_sap_points", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "current_valuation": { + "name": "current_valuation", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "property_portfolio_id_portfolio_id_fk": { + "name": "property_portfolio_id_portfolio_id_fk", + "tableFrom": "property", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {} + }, + "property_details_epc": { + "name": "property_details_epc", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "full_address": { + "name": "full_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "total_floor_area": { + "name": "total_floor_area", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "walls": { + "name": "walls", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "walls_rating": { + "name": "walls_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "roof": { + "name": "roof", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "roof_rating": { + "name": "roof_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "floor": { + "name": "floor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "floor_rating": { + "name": "floor_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "windows": { + "name": "windows", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "windows_rating": { + "name": "windows_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "heating": { + "name": "heating", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "heating_rating": { + "name": "heating_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "heating_controls": { + "name": "heating_controls", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "heating_controls_rating": { + "name": "heating_controls_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "hot_water": { + "name": "hot_water", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hot_water_rating": { + "name": "hot_water_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "lighting": { + "name": "lighting", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lighting_rating": { + "name": "lighting_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "mainfuel": { + "name": "mainfuel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ventilation": { + "name": "ventilation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "solar_pv": { + "name": "solar_pv", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "solar_hot_water": { + "name": "solar_hot_water", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "wind_turbine": { + "name": "wind_turbine", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "floor_height": { + "name": "floor_height", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "number_heated_rooms": { + "name": "number_heated_rooms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heat_loss_corridor": { + "name": "heat_loss_corridor", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "unheated_corridor_length": { + "name": "unheated_corridor_length", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "number_of_open_fireplaces": { + "name": "number_of_open_fireplaces", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "number_of_extensions": { + "name": "number_of_extensions", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "number_of_storeys": { + "name": "number_of_storeys", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "mains_gas": { + "name": "mains_gas", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "energy_tariff": { + "name": "energy_tariff", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "primary_energy_consumption": { + "name": "primary_energy_consumption", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "co2_emissions": { + "name": "co2_emissions", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "current_energy_demand": { + "name": "current_energy_demand", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "current_energy_demand_heating_hotwater": { + "name": "current_energy_demand_heating_hotwater", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "estimated": { + "name": "estimated", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "property_details_epc_property_id_property_id_fk": { + "name": "property_details_epc_property_id_property_id_fk", + "tableFrom": "property_details_epc", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "property_details_epc_portfolio_id_portfolio_id_fk": { + "name": "property_details_epc_portfolio_id_portfolio_id_fk", + "tableFrom": "property_details_epc", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {} + }, + "property_details_meter": { + "name": "property_details_meter", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "energy_supplier": { + "name": "energy_supplier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gas_supplier": { + "name": "gas_supplier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "meter_reading_total": { + "name": "meter_reading_total", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "meter_reading_electricity": { + "name": "meter_reading_electricity", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "meter_reading_gas": { + "name": "meter_reading_gas", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {} + }, + "property_details_spatial": { + "name": "property_details_spatial", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "x_coordinate": { + "name": "x_coordinate", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "y_coordinate": { + "name": "y_coordinate", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "latitude": { + "name": "latitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "longitude": { + "name": "longitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "conservation_status": { + "name": "conservation_status", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "is_listed_building": { + "name": "is_listed_building", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "is_heritage_building": { + "name": "is_heritage_building", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {} + }, + "property_targets": { + "name": "property_targets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "epc": { + "name": "epc", + "type": "epc", + "primaryKey": false, + "notNull": false + }, + "heat_demand": { + "name": "heat_demand", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "property_targets_property_id_property_id_fk": { + "name": "property_targets_property_id_property_id_fk", + "tableFrom": "property_targets", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "property_targets_portfolio_id_portfolio_id_fk": { + "name": "property_targets_portfolio_id_portfolio_id_fk", + "tableFrom": "property_targets", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {} + }, + "plan": { + "name": "plan", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "scenario_id": { + "name": "scenario_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "valuation_increase_lower_bound": { + "name": "valuation_increase_lower_bound", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "valuation_increase_upper_bound": { + "name": "valuation_increase_upper_bound", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "valuation_increase_average": { + "name": "valuation_increase_average", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "plan_portfolio_id_portfolio_id_fk": { + "name": "plan_portfolio_id_portfolio_id_fk", + "tableFrom": "plan", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "plan_property_id_property_id_fk": { + "name": "plan_property_id_property_id_fk", + "tableFrom": "plan", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "plan_scenario_id_scenario_id_fk": { + "name": "plan_scenario_id_scenario_id_fk", + "tableFrom": "plan", + "tableTo": "scenario", + "columnsFrom": [ + "scenario_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {} + }, + "plan_recommendations": { + "name": "plan_recommendations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "plan_id": { + "name": "plan_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "recommendation_id": { + "name": "recommendation_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "plan_recommendations_plan_id_plan_id_fk": { + "name": "plan_recommendations_plan_id_plan_id_fk", + "tableFrom": "plan_recommendations", + "tableTo": "plan", + "columnsFrom": [ + "plan_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "plan_recommendations_recommendation_id_recommendation_id_fk": { + "name": "plan_recommendations_recommendation_id_recommendation_id_fk", + "tableFrom": "plan_recommendations", + "tableTo": "recommendation", + "columnsFrom": [ + "recommendation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {} + }, + "recommendation": { + "name": "recommendation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "estimated_cost": { + "name": "estimated_cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "default": { + "name": "default", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "starting_u_value": { + "name": "starting_u_value", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "new_u_value": { + "name": "new_u_value", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "sap_points": { + "name": "sap_points", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "heat_demand": { + "name": "heat_demand", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "kwh_savings": { + "name": "kwh_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "co2_equivalent_savings": { + "name": "co2_equivalent_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_savings": { + "name": "energy_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_cost_savings": { + "name": "energy_cost_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "property_valuation_increase": { + "name": "property_valuation_increase", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "rental_yield_increase": { + "name": "rental_yield_increase", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "total_work_hours": { + "name": "total_work_hours", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "labour_days": { + "name": "labour_days", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "already_installed": { + "name": "already_installed", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "recommendation_property_id_property_id_fk": { + "name": "recommendation_property_id_property_id_fk", + "tableFrom": "recommendation", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {} + }, + "recommendation_materials": { + "name": "recommendation_materials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "recommendation_id": { + "name": "recommendation_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "material_id": { + "name": "material_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "depth": { + "name": "depth", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "quantity_unit": { + "name": "quantity_unit", + "type": "unit_quantity", + "primaryKey": false, + "notNull": true + }, + "estimated_cost": { + "name": "estimated_cost", + "type": "real", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "recommendation_materials_recommendation_id_recommendation_id_fk": { + "name": "recommendation_materials_recommendation_id_recommendation_id_fk", + "tableFrom": "recommendation_materials", + "tableTo": "recommendation", + "columnsFrom": [ + "recommendation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "recommendation_materials_material_id_material_id_fk": { + "name": "recommendation_materials_material_id_material_id_fk", + "tableFrom": "recommendation_materials", + "tableTo": "material", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {} + }, + "scenario": { + "name": "scenario", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "budget": { + "name": "budget", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "housing_type": { + "name": "housing_type", + "type": "housing_type", + "primaryKey": false, + "notNull": true + }, + "goal": { + "name": "goal", + "type": "goal", + "primaryKey": false, + "notNull": true + }, + "trigger_file_path": { + "name": "trigger_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "already_installed_file_path": { + "name": "already_installed_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "patches_file_path": { + "name": "patches_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "non_invasive_recommendations_file_path": { + "name": "non_invasive_recommendations_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "exclusions": { + "name": "exclusions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "multi_plan": { + "name": "multi_plan", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "cost": { + "name": "cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "total_work_hours": { + "name": "total_work_hours", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_savings": { + "name": "energy_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "co2_equivalent_savings": { + "name": "co2_equivalent_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_cost_savings": { + "name": "energy_cost_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "property_valuation_increase": { + "name": "property_valuation_increase", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "labour_days": { + "name": "labour_days", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "epc_breakdown_pre_retrofit": { + "name": "epc_breakdown_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "epc_breakdown_post_retrofit": { + "name": "epc_breakdown_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "number_of_properties": { + "name": "number_of_properties", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "n_units_to_retrofit": { + "name": "n_units_to_retrofit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "co2_per_unit_pre_retrofit": { + "name": "co2_per_unit_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "co2_per_unit_post_retrofit": { + "name": "co2_per_unit_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_bill_per_unit_pre_retrofit": { + "name": "energy_bill_per_unit_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_bill_per_unit_post_retrofit": { + "name": "energy_bill_per_unit_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_consumption_per_unit_pre_retrofit": { + "name": "energy_consumption_per_unit_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_consumption_per_unit_post_retrofit": { + "name": "energy_consumption_per_unit_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valuation_improvement_per_unit": { + "name": "valuation_improvement_per_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost_per_unit": { + "name": "cost_per_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost_per_co2_saved": { + "name": "cost_per_co2_saved", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost_per_sap_point": { + "name": "cost_per_sap_point", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valuation_return_on_investment": { + "name": "valuation_return_on_investment", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "scenario_portfolio_id_portfolio_id_fk": { + "name": "scenario_portfolio_id_portfolio_id_fk", + "tableFrom": "scenario", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {} + }, + "solar": { + "name": "solar", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "longitude": { + "name": "longitude", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "latitude": { + "name": "latitude", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "google_api_response": { + "name": "google_api_response", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {} + }, + "solar_scenario": { + "name": "solar_scenario", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "solar_id": { + "name": "solar_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "scenario_type": { + "name": "scenario_type", + "type": "scenario_type", + "primaryKey": false, + "notNull": true + }, + "number_panels": { + "name": "number_panels", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "array_kwhp": { + "name": "array_kwhp", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "lifetime_dc_kwh": { + "name": "lifetime_dc_kwh", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "yearly_dc_kwh": { + "name": "yearly_dc_kwh", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "lifetime_ac_kwh": { + "name": "lifetime_ac_kwh", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "yearly_ac_kwh": { + "name": "yearly_ac_kwh", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "cost": { + "name": "cost", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "expected_payback_years": { + "name": "expected_payback_years", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "panelled_roof_area": { + "name": "panelled_roof_area", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "solar_scenario_solar_id_solar_id_fk": { + "name": "solar_scenario_solar_id_solar_id_fk", + "tableFrom": "solar_scenario", + "tableTo": "solar", + "columnsFrom": [ + "solar_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "firstName": { + "name": "firstName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oauth_id": { + "name": "oauth_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oauth_provider": { + "name": "oauth_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {} + } + }, + "enums": { + "document_type": { + "name": "document_type", + "values": { + "EPR": "EPR", + "Condition Report": "Condition Report", + "Evidence Report": "Evidence Report", + "Summary Information": "Summary Information", + "Floor Plan": "Floor Plan", + "Scenario Draft EPC": "Scenario Draft EPC", + "Scenario Site Notes": "Scenario Site Notes" + } + }, + "cost_unit": { + "name": "cost_unit", + "values": { + "gbp_sq_meter": "gbp_sq_meter", + "gbp_per_unit": "gbp_per_unit", + "gbp_per_m2": "gbp_per_m2", + "gbp_per_m": "gbp_per_m" + } + }, + "depth_unit": { + "name": "depth_unit", + "values": { + "mm": "mm" + } + }, + "type": { + "name": "type", + "values": { + "suspended_floor_insulation": "suspended_floor_insulation", + "solid_floor_insulation": "solid_floor_insulation", + "external_wall_insulation": "external_wall_insulation", + "internal_wall_insulation": "internal_wall_insulation", + "cavity_wall_insulation": "cavity_wall_insulation", + "mechanical_ventilation": "mechanical_ventilation", + "loft_insulation": "loft_insulation", + "exposed_floor_insulation": "exposed_floor_insulation", + "flat_roof_insulation": "flat_roof_insulation", + "room_roof_insulation": "room_roof_insulation", + "iwi_wall_demolition": "iwi_wall_demolition", + "iwi_vapour_barrier": "iwi_vapour_barrier", + "iwi_redecoration": "iwi_redecoration", + "suspended_floor_demolition": "suspended_floor_demolition", + "suspended_floor_redecoration": "suspended_floor_redecoration", + "suspended_floor_vapour_barrier": "suspended_floor_vapour_barrier", + "solid_floor_demolition": "solid_floor_demolition", + "solid_floor_preparation": "solid_floor_preparation", + "solid_floor_vapour_barrier": "solid_floor_vapour_barrier", + "solid_floor_redecoration": "solid_floor_redecoration", + "ewi_wall_demolition": "ewi_wall_demolition", + "ewi_wall_preparation": "ewi_wall_preparation", + "ewi_wall_redecoration": "ewi_wall_redecoration", + "low_energy_lighting_installation": "low_energy_lighting_installation", + "flat_roof_preparation": "flat_roof_preparation", + "flat_roof_vapour_barrier": "flat_roof_vapour_barrier", + "flat_roof_waterproofing": "flat_roof_waterproofing", + "windows_glazing": "windows_glazing" + } + }, + "r_value_unit": { + "name": "r_value_unit", + "values": { + "square_meter_kelvin_per_watt": "square_meter_kelvin_per_watt" + } + }, + "thermal_conductivity_unit": { + "name": "thermal_conductivity_unit", + "values": { + "watt_per_meter_kelvin": "watt_per_meter_kelvin" + } + }, + "goal": { + "name": "goal", + "values": { + "Valuation Improvement": "Valuation Improvement", + "Increasing EPC": "Increasing EPC", + "Reducing CO2 emissions": "Reducing CO2 emissions", + "Energy Savings": "Energy Savings", + "None": "None" + } + }, + "role": { + "name": "role", + "values": { + "creator": "creator", + "admin": "admin", + "read": "read", + "write": "write" + } + }, + "status": { + "name": "status", + "values": { + "scoping": "scoping", + "survey": "survey", + "assessment": "assessment", + "tendering": "tendering", + "project underway": "project underway", + "completion; status: on track": "completion; status: on track", + "completion; status: delayed": "completion; status: delayed", + "completion; status: at risk": "completion; status: at risk", + "completion; status: completed": "completion; status: completed", + "needs review": "needs review" + } + }, + "epc": { + "name": "epc", + "values": { + "A": "A", + "B": "B", + "C": "C", + "D": "D", + "E": "E", + "F": "F", + "G": "G" + } + }, + "creation_status": { + "name": "creation_status", + "values": { + "LOADING": "LOADING", + "READY": "READY", + "ERROR": "ERROR" + } + }, + "housing_type": { + "name": "housing_type", + "values": { + "Private": "Private", + "Social": "Social" + } + }, + "unit_quantity": { + "name": "unit_quantity", + "values": { + "m2": "m2", + "part": "part" + } + }, + "scenario_type": { + "name": "scenario_type", + "values": { + "unit": "unit", + "building": "building" + } + } + }, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/src/app/db/migrations/meta/_journal.json b/src/app/db/migrations/meta/_journal.json index e28f9973..432e6110 100644 --- a/src/app/db/migrations/meta/_journal.json +++ b/src/app/db/migrations/meta/_journal.json @@ -673,6 +673,13 @@ "when": 1725474928372, "tag": "0095_sloppy_ikaris", "breakpoints": true + }, + { + "idx": 96, + "version": "5", + "when": 1725897920431, + "tag": "0096_married_umar", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/app/db/schema/portfolio.ts b/src/app/db/schema/portfolio.ts index 35198797..6cf833ef 100644 --- a/src/app/db/schema/portfolio.ts +++ b/src/app/db/schema/portfolio.ts @@ -13,6 +13,7 @@ import { InferModel } from "drizzle-orm"; export const PortfolioStatus: [string, ...string[]] = [ "scoping", + "survey", "assessment", "tendering", "project underway", From be347143f1dfee3c341355dcfcc5e92006f4f08f Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 9 Sep 2024 18:05:21 +0100 Subject: [PATCH 10/28] minor tweaks --- src/app/components/StatusBadge.tsx | 2 +- .../building-passport/[propertyId]/energy-assessment/page.tsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/components/StatusBadge.tsx b/src/app/components/StatusBadge.tsx index 0999b1e6..938f1358 100644 --- a/src/app/components/StatusBadge.tsx +++ b/src/app/components/StatusBadge.tsx @@ -65,7 +65,7 @@ const statusColor: { propertyHoverText: "This property is currently in the assessment stage", }, survey: { - class: "bg-emerald-500 hover:bg-emerald-500", + class: "bg-brandblue hover:bg-hoverblue", text: "survey", hoverText: "This portfolio is currently in the survey stage", propertyHoverText: "This property is currently in the survey stage", diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx index 871f9b0b..4522de87 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/page.tsx @@ -71,7 +71,9 @@ export default async function EnergyAssessmentsPage({ const propertyMeta = await getPropertyMeta(params.propertyId); const ea = await getEnergyAssessment(propertyMeta.uprn); - if (!ea) { + // ea will be an empty object {} if there is no energy assessment + + if (Object.keys(ea).length === 0) { return (
From 8b3a9ca74e3f51bc783abea4a1fd4f9404642ee1 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 9 Sep 2024 22:54:55 +0100 Subject: [PATCH 11/28] Attempting to serve static file --- src/app/api/auth/[...nextauth]/route.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts index bd4ae388..96517f0c 100644 --- a/src/app/api/auth/[...nextauth]/route.ts +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -36,6 +36,11 @@ export const AuthOptions: NextAuthOptions = { clientId: AZURE_AD_CLIENT_ID, clientSecret: AZURE_AD_CLIENT_SECRET, tenantId: AZURE_AD_TENANT_ID, + authorization: { + params: { + prompt: "consent", + }, + }, }), ], pages: { From 6d8bfd0bd1a5c44becacbeba08ca4e1daeb31b17 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 9 Sep 2024 23:04:59 +0100 Subject: [PATCH 12/28] serve well know through public dir --- public/.well-known/microsoft-identity-association.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 public/.well-known/microsoft-identity-association.json diff --git a/public/.well-known/microsoft-identity-association.json b/public/.well-known/microsoft-identity-association.json new file mode 100644 index 00000000..975f09e1 --- /dev/null +++ b/public/.well-known/microsoft-identity-association.json @@ -0,0 +1,7 @@ +{ + "associatedApplications": [ + { + "applicationId": "069e75ee-ba54-45ff-ba77-a06f29c0e21c" + } + ] +} \ No newline at end of file From 5a1a9ef1ab4be03f14ab8c6f10e18f35dc7b68bd Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 9 Sep 2024 23:21:37 +0100 Subject: [PATCH 13/28] force deploy --- .../[propertyId]/energy-assessment/DocumentsTable.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx index e142b93f..a472b93c 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx @@ -61,6 +61,7 @@ export const DocumentsTable: React.FC = ({ // Mutation to handle the presigned URL generation const { mutate: fetchPresignedUrl } = useMutation( + // Use the file key as the argument to generate the URL async (fileKey: string) => await generatePresignedUrl(fileKey), { onSuccess: (url) => { From d1c68fbbfd5c5d546102de04d15384486942694e Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 9 Sep 2024 23:23:02 +0100 Subject: [PATCH 14/28] force deploy --- .../[propertyId]/energy-assessment/DocumentsTable.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx index a472b93c..bb67488b 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx @@ -55,6 +55,7 @@ export const DocumentsTable: React.FC = ({ documents, allowedTypes, }) => { + // Filter the documents based on the allowed types const relevantDocuments = documents.filter((doc) => allowedTypes.includes(doc.documentType) ); From df0d3a1f2f9c32133fc040cfb7cc23cecf964d1c Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 10 Sep 2024 00:35:11 +0100 Subject: [PATCH 15/28] force deploy --- .../[propertyId]/energy-assessment/DocumentsTable.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx index bb67488b..5a58717f 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx @@ -75,7 +75,8 @@ export const DocumentsTable: React.FC = ({ ); const handleDownload = (documentLocation: string) => { - fetchPresignedUrl(documentLocation); // Generate URL and open in new tab + // Generate URL and open in new tab + fetchPresignedUrl(documentLocation); }; return ( From 4559df9e8be5a120f36b3e1450f1d0a8e7422329 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 10 Sep 2024 03:40:10 +0100 Subject: [PATCH 16/28] ms login working --- src/app/api/auth/[...nextauth]/route.ts | 19 +++++++++++-------- .../signin/MicrosoftSignInButton.jsx | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts index 96517f0c..b6aea4e4 100644 --- a/src/app/api/auth/[...nextauth]/route.ts +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -1,15 +1,16 @@ import NextAuth, { NextAuthOptions } from "next-auth"; import GoogleProvider from "next-auth/providers/google"; -import AzureADProvider from "next-auth/providers/azure-ad"; +import AzureADB2CProvider from "next-auth/providers/azure-ad-b2c"; import { db } from "@/app/db/db"; import { user as userTable, User } from "@/app/db/schema/users"; import { eq } from "drizzle-orm"; const { GOOGLE_CLIENT_ID = "", GOOGLE_CLIENT_SECRET = "" } = process.env; const { - AZURE_AD_CLIENT_ID = "", - AZURE_AD_CLIENT_SECRET = "", - AZURE_AD_TENANT_ID = "", + AZURE_AD_B2C_TENANT_NAME = "", + AZURE_AD_B2C_CLIENT_ID = "", + AZURE_AD_B2C_CLIENT_SECRET = "", + AZURE_AD_B2C_PRIMARY_USER_FLOW = "", } = process.env; type OauthProvider = "google"; @@ -32,12 +33,14 @@ export const AuthOptions: NextAuthOptions = { }, }, }), - AzureADProvider({ - clientId: AZURE_AD_CLIENT_ID, - clientSecret: AZURE_AD_CLIENT_SECRET, - tenantId: AZURE_AD_TENANT_ID, + AzureADB2CProvider({ + tenantId: AZURE_AD_B2C_TENANT_NAME, + clientId: AZURE_AD_B2C_CLIENT_ID, + clientSecret: AZURE_AD_B2C_CLIENT_SECRET, + primaryUserFlow: AZURE_AD_B2C_PRIMARY_USER_FLOW, authorization: { params: { + scope: `https://${process.env.AZURE_AD_B2C_TENANT_NAME}.onmicrosoft.com/api/demo.read https://${process.env.AZURE_AD_B2C_TENANT_NAME}.onmicrosoft.com/api/demo.write offline_access openid`, prompt: "consent", }, }, diff --git a/src/app/components/signin/MicrosoftSignInButton.jsx b/src/app/components/signin/MicrosoftSignInButton.jsx index bb881fd3..f53399f4 100644 --- a/src/app/components/signin/MicrosoftSignInButton.jsx +++ b/src/app/components/signin/MicrosoftSignInButton.jsx @@ -13,7 +13,7 @@ const MicrosoftSignInButton = () => { +
+ + {/* Reserve space for the error message */} +
+ {error &&

You are not a valid user.

} +
+ + ); +}
{kwh.toFixed(0)} kWh
- {carbon}t CO 2 + {carbon}t CO2