diff --git a/src/app/components/building-passport/EnergyEfficiencyImpactCard.tsx b/src/app/components/building-passport/EnergyEfficiencyImpactCard.tsx new file mode 100644 index 0000000..1f9c207 --- /dev/null +++ b/src/app/components/building-passport/EnergyEfficiencyImpactCard.tsx @@ -0,0 +1,93 @@ +interface EnergyEfficiencyImpactCardProps { + currentEpcRating: string; + expectedEpcRating: string; + currentSapPoints: number; + expectedSapPoints: number; + totalSapPoints: number; +} + +export function EnergyEfficiencyImpactCard({ + currentEpcRating, + expectedEpcRating, + currentSapPoints, + expectedSapPoints, + totalSapPoints, +}: EnergyEfficiencyImpactCardProps) { + return ( +
+ + + + + + + + + + + + + + + + + + + + + +
Energy Efficiency Impact
Current EPC Rating: + {currentSapPoints + " " + currentEpcRating} +
Expected EPC Rating: + {Math.floor(expectedSapPoints) + " " + expectedEpcRating} +
+ Total SAP Points Improvement: + + {Math.round((totalSapPoints + Number.EPSILON) * 100) / 100} +
+
+ ); +} + +interface SecondaryEnergyEfficiencyImpactCardProps { + TotalCo2Savings: number; + totalEnergyCostSavings: number; + totalHeatDemandSavings: number; +} + +export function SecondaryEnergyEfficiencyImpactCard({ + TotalCo2Savings, + totalEnergyCostSavings, + totalHeatDemandSavings, +}: SecondaryEnergyEfficiencyImpactCardProps) { + return ( +
+ + + + + + + + + + + + + + + + + + + + + +
+ CO2 Reduction + {TotalCo2Savings.toFixed(1)}t
Energy Savings + {totalHeatDemandSavings.toFixed(0) + "kWh"} +
Energy Bill Savings{"£" + Math.round(totalEnergyCostSavings)}
+
+ ); +} diff --git a/src/app/components/building-passport/RecommendationCard.tsx b/src/app/components/building-passport/RecommendationCard.tsx index f882912..da9aeb9 100644 --- a/src/app/components/building-passport/RecommendationCard.tsx +++ b/src/app/components/building-passport/RecommendationCard.tsx @@ -35,6 +35,31 @@ const TitleMap = { roof_insulation: "Roof Insulation", }; +type RecommendationCardProps = { + componentType: RecommendationType; + recommendationData: Recommendation[]; + setCostMap: Dispatch>; + costMap: RecommendationMetricMap; + setTotalEstimatedCost: Dispatch>; + sapMap: RecommendationMetricMap; + setSapMap: Dispatch>; + setTotalSapPoints: Dispatch>; + currentSapPoints: number; + setExpectedEpcRating: Dispatch>; + setTotalLabourDays: Dispatch>; + labourDaysMap: RecommendationMetricMap; + setLabourDaysMap: Dispatch>; + setCo2SavingsMap: Dispatch>; + co2SavingsMap: RecommendationMetricMap; + setTotalCo2Savings: Dispatch>; + setEnergyCostSavingsMap: Dispatch>; + energyCostSavingsMap: RecommendationMetricMap; + setTotalEnergyCostSavings: Dispatch>; + setHeatDemandMap: Dispatch>; + heatDemandMap: RecommendationMetricMap; + setTotalHeatDemandSavings: Dispatch>; +}; + export default function RecommendationCard({ componentType, recommendationData, @@ -46,18 +71,19 @@ export default function RecommendationCard({ setTotalSapPoints, currentSapPoints, setExpectedEpcRating, -}: { - componentType: RecommendationType; - recommendationData: Recommendation[]; - setCostMap: Dispatch>; - costMap: RecommendationMetricMap; - setTotalEstimatedCost: Dispatch>; - sapMap: RecommendationMetricMap; - setSapMap: Dispatch>; - setTotalSapPoints: Dispatch>; - currentSapPoints: number; - setExpectedEpcRating: Dispatch>; -}) { + setTotalLabourDays, + labourDaysMap, + setLabourDaysMap, + setCo2SavingsMap, + co2SavingsMap, + setTotalCo2Savings, + setEnergyCostSavingsMap, + energyCostSavingsMap, + setTotalEnergyCostSavings, + setHeatDemandMap, + heatDemandMap, + setTotalHeatDemandSavings, +}: RecommendationCardProps) { const defaultComponent = recommendationData.find( (rec: Recommendation) => rec.default ) as Recommendation; @@ -138,6 +164,22 @@ export default function RecommendationCard({ setTotalSapPoints={setTotalSapPoints} currentSapPoints={currentSapPoints} setExpectedEpcRating={setExpectedEpcRating} + // Labour + setTotalLabourDays={setTotalLabourDays} + labourDaysMap={labourDaysMap} + setLabourDaysMap={setLabourDaysMap} + // Co2 + setCo2SavingsMap={setCo2SavingsMap} + co2SavingsMap={co2SavingsMap} + setTotalCo2Savings={setTotalCo2Savings} + // Energy Cost + setEnergyCostSavingsMap={setEnergyCostSavingsMap} + energyCostSavingsMap={energyCostSavingsMap} + setTotalEnergyCostSavings={setTotalEnergyCostSavings} + // Heat Demand + setHeatDemandMap={setHeatDemandMap} + heatDemandMap={heatDemandMap} + setTotalHeatDemandSavings={setTotalHeatDemandSavings} /> ); diff --git a/src/app/components/building-passport/RecommendationContainer.tsx b/src/app/components/building-passport/RecommendationContainer.tsx index 8d03d8a..db26845 100644 --- a/src/app/components/building-passport/RecommendationContainer.tsx +++ b/src/app/components/building-passport/RecommendationContainer.tsx @@ -5,14 +5,17 @@ import { RecommendationType, } from "@/app/db/schema/recommendations"; import RecommendationCard from "./RecommendationCard"; -import RecommendationCostSummaryCard from "./RecommendationCostSummaryCard"; +import WorksPackageCard from "./WorksPackageCard"; import { Separator } from "@/app/shadcn_components/ui/separator"; import { PropertyMeta } from "@/app/db/schema/property"; import { sapToEpc } from "@/app/utils"; import { useState } from "react"; import { sumRecommendationMetricMap } from "@/app/portfolio/[slug]/building-passport/[propertyId]/plans/utils"; import { RecommendationMetricMap } from "@/types/recommendations"; -import RecommendationEpcSummaryCard from "./RecommendationEpcSummaryCard"; +import { + EnergyEfficiencyImpactCard, + SecondaryEnergyEfficiencyImpactCard, +} from "./EnergyEfficiencyImpactCard"; interface RecommendationContainerProps { recommendations: Recommendation[]; @@ -32,6 +35,15 @@ const typeToCategoryMap: { [key in RecommendationType]?: RecommendationType } = exposed_floor_insulation: "floor_insulation", }; +const emptyImpactState = { + estimatedCost: 0, + sapPoints: 0, + labourDays: 0, + co2EquivalentSavings: 0, + energyCostSavings: 0, + heatDemand: 0, +}; + export default function RecommendationContainer({ recommendations, propertyMeta, @@ -48,37 +60,35 @@ export default function RecommendationContainer({ return acc; }, {} as Record); - console.log(categorizedRecommendations); - const defaultWallsRecommendations = categorizedRecommendations.wall_insulation?.find( (rec: Recommendation) => rec.default - ) || { estimatedCost: 0, sapPoints: 0 }; + ) || emptyImpactState; const defaultFloorRecommendations = categorizedRecommendations.floor_insulation?.find( (rec: Recommendation) => rec.default - ) || { estimatedCost: 0, sapPoints: 0 }; + ) || emptyImpactState; const defaultRoofRecommendations = categorizedRecommendations.roof_insulation?.find( (rec: Recommendation) => rec.default - ) || { estimatedCost: 0, sapPoints: 0 }; + ) || emptyImpactState; const defaultVentiliationRecommendations = categorizedRecommendations.mechanical_ventilation?.find( (rec: Recommendation) => rec.default - ) || { estimatedCost: 0, sapPoints: 0 }; + ) || emptyImpactState; const defaultFireplaceRecommendations = categorizedRecommendations.sealing_open_fireplace?.find( (rec: Recommendation) => rec.default - ) || { estimatedCost: 0, sapPoints: 0 }; + ) || emptyImpactState; const defaultLightingRecommendations = categorizedRecommendations.low_energy_lighting?.find( (rec: Recommendation) => rec.default - ) || { estimatedCost: 0, sapPoints: 0 }; + ) || emptyImpactState; const [costMap, setCostMap] = useState({ wall_insulation: defaultWallsRecommendations?.estimatedCost || 0, @@ -99,6 +109,49 @@ export default function RecommendationContainer({ low_energy_lighting: defaultLightingRecommendations.sapPoints || 0, }); + const [labourDaysMap, setLabourDaysMap] = useState({ + wall_insulation: defaultWallsRecommendations?.labourDays || 0, + floor_insulation: defaultFloorRecommendations.labourDays || 0, + roof_insulation: defaultRoofRecommendations.labourDays || 0, + mechanical_ventilation: defaultVentiliationRecommendations.labourDays || 0, + sealing_open_fireplace: defaultFireplaceRecommendations.labourDays || 0, + low_energy_lighting: defaultLightingRecommendations.labourDays || 0, + }); + + const [co2SavingsMap, setCo2SavingsMap] = useState({ + wall_insulation: defaultWallsRecommendations?.co2EquivalentSavings || 0, + floor_insulation: defaultFloorRecommendations.co2EquivalentSavings || 0, + roof_insulation: defaultRoofRecommendations.co2EquivalentSavings || 0, + mechanical_ventilation: + defaultVentiliationRecommendations.co2EquivalentSavings || 0, + sealing_open_fireplace: + defaultFireplaceRecommendations.co2EquivalentSavings || 0, + low_energy_lighting: + defaultLightingRecommendations.co2EquivalentSavings || 0, + }); + + const [energyCostSavingsMap, setEnergyCostSavingsMap] = + useState({ + wall_insulation: defaultWallsRecommendations?.energyCostSavings || 0, + floor_insulation: defaultFloorRecommendations.energyCostSavings || 0, + roof_insulation: defaultRoofRecommendations.energyCostSavings || 0, + mechanical_ventilation: + defaultVentiliationRecommendations.energyCostSavings || 0, + sealing_open_fireplace: + defaultFireplaceRecommendations.energyCostSavings || 0, + low_energy_lighting: + defaultLightingRecommendations.energyCostSavings || 0, + }); + + const [heatDemandMap, setHeatDemandMap] = useState({ + wall_insulation: defaultWallsRecommendations?.heatDemand || 0, + floor_insulation: defaultFloorRecommendations.heatDemand || 0, + roof_insulation: defaultRoofRecommendations.heatDemand || 0, + mechanical_ventilation: defaultVentiliationRecommendations.heatDemand || 0, + sealing_open_fireplace: defaultFireplaceRecommendations.heatDemand || 0, + low_energy_lighting: defaultLightingRecommendations.heatDemand || 0, + }); + const [totalEstimatedCost, setTotalEstimatedCost] = useState( sumRecommendationMetricMap(costMap) ); @@ -107,10 +160,25 @@ export default function RecommendationContainer({ sumRecommendationMetricMap(sapMap) ); + const [totalLabourDays, setTotalLabourDays] = useState( + sumRecommendationMetricMap(labourDaysMap) + ); + + const [totalCo2Savings, setTotalCo2Savings] = useState( + sumRecommendationMetricMap(co2SavingsMap) + ); + + const [totalEnergyCostSavings, setTotalEnergyCostSavings] = useState( + sumRecommendationMetricMap(energyCostSavingsMap) + ); + + const [totalHeatDemandSavings, setTotalHeatDemandSavings] = useState( + sumRecommendationMetricMap(heatDemandMap) + ); + const currentEpcRating = propertyMeta.currentEpcRating; const currentSapPoints = propertyMeta.currentSapPoints; - //TODO: Use Math.min while we have dummy SAP points const expectedSapPoints = Math.min(currentSapPoints + totalSapPoints, 100); const [expectedEpcRating, setExpectedEpcRating] = useState( sapToEpc(expectedSapPoints) @@ -119,14 +187,23 @@ export default function RecommendationContainer({ return ( <>
- + + -
@@ -141,14 +218,32 @@ export default function RecommendationContainer({ // entires means we loose the typing on the key componentType={componentType as RecommendationType} recommendationData={recommendationData} + // cost setCostMap={setCostMap} costMap={costMap} setTotalEstimatedCost={setTotalEstimatedCost} + // Sap setSapMap={setSapMap} sapMap={sapMap} setTotalSapPoints={setTotalSapPoints} currentSapPoints={currentSapPoints} setExpectedEpcRating={setExpectedEpcRating} + // Labour + setTotalLabourDays={setTotalLabourDays} + labourDaysMap={labourDaysMap} + setLabourDaysMap={setLabourDaysMap} + // Co2 + setCo2SavingsMap={setCo2SavingsMap} + co2SavingsMap={co2SavingsMap} + setTotalCo2Savings={setTotalCo2Savings} + // Energy Cost + setEnergyCostSavingsMap={setEnergyCostSavingsMap} + energyCostSavingsMap={energyCostSavingsMap} + setTotalEnergyCostSavings={setTotalEnergyCostSavings} + // Heat Demand + setHeatDemandMap={setHeatDemandMap} + heatDemandMap={heatDemandMap} + setTotalHeatDemandSavings={setTotalHeatDemandSavings} /> ); } diff --git a/src/app/components/building-passport/RecommendationCostSummaryCard.tsx b/src/app/components/building-passport/RecommendationCostSummaryCard.tsx deleted file mode 100644 index ccf5125..0000000 --- a/src/app/components/building-passport/RecommendationCostSummaryCard.tsx +++ /dev/null @@ -1,30 +0,0 @@ -"use client"; -import { formatNumber } from "@/app/utils"; - -export default function RecommendationCostSummaryCard({ - totalEstimatedCost, - totalSapPoints, -}: { - totalEstimatedCost: number; - totalSapPoints: number; -}) { - return ( - - - - - - - - - - - - -
Total Cost:{"£" + formatNumber(totalEstimatedCost)}
- Total SAP Points Improvement: - - {Math.round((totalSapPoints + Number.EPSILON) * 100) / 100} -
- ); -} diff --git a/src/app/components/building-passport/RecommendationEpcSummaryCard.tsx b/src/app/components/building-passport/RecommendationEpcSummaryCard.tsx deleted file mode 100644 index dcd1ebf..0000000 --- a/src/app/components/building-passport/RecommendationEpcSummaryCard.tsx +++ /dev/null @@ -1,25 +0,0 @@ -interface RecommendationEpcSummaryCardProps { - currentEpcRating: string; - expectedEpcRating: string; -} - -export default function RecommendationEpcSummaryCard({ - currentEpcRating, - expectedEpcRating, -}: RecommendationEpcSummaryCardProps) { - return ( - - - - - - - - - - - - -
Current EPC Rating:{currentEpcRating}
Expected EPC Rating:{expectedEpcRating}
- ); -} diff --git a/src/app/components/building-passport/RecommendationModal.tsx b/src/app/components/building-passport/RecommendationModal.tsx index 49f2541..9ff7356 100644 --- a/src/app/components/building-passport/RecommendationModal.tsx +++ b/src/app/components/building-passport/RecommendationModal.tsx @@ -22,6 +22,18 @@ interface RecommendationModalProps { setTotalSapPoints: Dispatch>; currentSapPoints: number; setExpectedEpcRating: Dispatch>; + setTotalLabourDays: Dispatch>; + labourDaysMap: RecommendationMetricMap; + setLabourDaysMap: Dispatch>; + setCo2SavingsMap: Dispatch>; + co2SavingsMap: RecommendationMetricMap; + setTotalCo2Savings: Dispatch>; + setEnergyCostSavingsMap: Dispatch>; + energyCostSavingsMap: RecommendationMetricMap; + setTotalEnergyCostSavings: Dispatch>; + setHeatDemandMap: Dispatch>; + heatDemandMap: RecommendationMetricMap; + setTotalHeatDemandSavings: Dispatch>; } export default function RecommendationModal({ @@ -38,6 +50,18 @@ export default function RecommendationModal({ setTotalSapPoints, currentSapPoints, setExpectedEpcRating, + setTotalLabourDays, + labourDaysMap, + setLabourDaysMap, + setCo2SavingsMap, + co2SavingsMap, + setTotalCo2Savings, + setEnergyCostSavingsMap, + energyCostSavingsMap, + setTotalEnergyCostSavings, + setHeatDemandMap, + heatDemandMap, + setTotalHeatDemandSavings, }: RecommendationModalProps) { const [saveButtonDisabled, setSaveButtonDisabled] = useState(true); @@ -77,8 +101,6 @@ export default function RecommendationModal({ // update the cost sum setTotalEstimatedCost(sumRecommendationMetricMap(newCostMap)); - console.log("B4", sapMap); - // Update the sap map const newSapMap = { ...sapMap, @@ -86,11 +108,8 @@ export default function RecommendationModal({ }; setSapMap(newSapMap); - console.log("AFTER", newSapMap); - // update the sap sum const newSapImprovement = sumRecommendationMetricMap(newSapMap); - console.log("newSapImprovement", newSapImprovement); setTotalSapPoints(newSapImprovement); // TODO: While we have placeholder SAP points, constrain to 100 @@ -98,6 +117,52 @@ export default function RecommendationModal({ // update the expected EPC rating setExpectedEpcRating(sapToEpc(newSapPoints)); + + // Update the labour days map + const newLabourDaysMap = { + ...labourDaysMap, + [title]: recommendationData[newIndex]?.labourDays || 0, + }; + + setLabourDaysMap(newLabourDaysMap); + + // update the labour days sum + setTotalLabourDays(sumRecommendationMetricMap(newLabourDaysMap)); + + // Update the co2 savings map + const newCo2SavingsMap = { + ...co2SavingsMap, + [title]: recommendationData[newIndex]?.co2EquivalentSavings || 0, + }; + + setCo2SavingsMap(newCo2SavingsMap); + + // update the co2 savings sum + setTotalCo2Savings(sumRecommendationMetricMap(newCo2SavingsMap)); + + // Update the energy cost savings map + const newEnergyCostSavingsMap = { + ...energyCostSavingsMap, + [title]: recommendationData[newIndex]?.energyCostSavings || 0, + }; + + setEnergyCostSavingsMap(newEnergyCostSavingsMap); + + // update the energy cost savings sum + setTotalEnergyCostSavings( + sumRecommendationMetricMap(newEnergyCostSavingsMap) + ); + + // Update the heat demand savings map + const newHeatDemandMap = { + ...heatDemandMap, + [title]: recommendationData[newIndex]?.heatDemand || 0, + }; + + setHeatDemandMap(newHeatDemandMap); + + // update the heat demand savings sum + setTotalHeatDemandSavings(sumRecommendationMetricMap(newHeatDemandMap)); } return ( diff --git a/src/app/components/building-passport/WorksPackageCard.tsx b/src/app/components/building-passport/WorksPackageCard.tsx new file mode 100644 index 0000000..a07fd40 --- /dev/null +++ b/src/app/components/building-passport/WorksPackageCard.tsx @@ -0,0 +1,35 @@ +"use client"; +import { convertDaysToWorkingWeeks, formatNumber } from "@/app/utils"; + +export default function WorksPackageCard({ + totalEstimatedCost, + totalLabourDays, +}: { + totalEstimatedCost: number; + totalLabourDays: number; +}) { + return ( + + + + + + + + + + + + + + + + + + + + + +
Works Package
Total Cost:{"£" + formatNumber(totalEstimatedCost)}
Trades required:{"1-2"}
Estimated Duration:{convertDaysToWorkingWeeks(totalLabourDays)}
+ ); +} diff --git a/src/app/db/migrations/0054_sharp_mojo.sql b/src/app/db/migrations/0054_sharp_mojo.sql new file mode 100644 index 0000000..16192c9 --- /dev/null +++ b/src/app/db/migrations/0054_sharp_mojo.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS "property_details_spatial" ( + "id" bigserial PRIMARY KEY NOT NULL, + "uprn" bigint, + "x_coordinate" real, + "y_coordinate" real, + "latitude" real, + "longitude" real, + "conservation_status" boolean, + "is_listed_building" boolean, + "is_heritage_building" boolean +); diff --git a/src/app/db/migrations/meta/0054_snapshot.json b/src/app/db/migrations/meta/0054_snapshot.json new file mode 100644 index 0000000..cd1fb13 --- /dev/null +++ b/src/app/db/migrations/meta/0054_snapshot.json @@ -0,0 +1,1442 @@ +{ + "version": "5", + "dialect": "pg", + "id": "b92e97d1-c6e9-4749-b205-ad3cf5470a4d", + "prevId": "5bab96d7-f042-492e-a08f-a2ad4e7a2f69", + "tables": { + "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 + } + }, + "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()" + } + }, + "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": {} + }, + "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 + } + }, + "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 + }, + "adjusted_energy_consumption": { + "name": "adjusted_energy_consumption", + "type": "real", + "primaryKey": false, + "notNull": 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 + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "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()" + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "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" + } + }, + "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 + }, + "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 + } + }, + "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": {} + }, + "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": { + "cost_unit": { + "name": "cost_unit", + "values": { + "gbp_sq_meter": "gbp_sq_meter", + "gbp_per_unit": "gbp_per_unit", + "gbp_per_m2": "gbp_per_m2" + } + }, + "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" + } + }, + "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", + "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" + } + }, + "unit_quantity": { + "name": "unit_quantity", + "values": { + "m2": "m2", + "part": "part" + } + } + }, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/src/app/portfolio/[slug]/(portfolio)/page.tsx b/src/app/portfolio/[slug]/(portfolio)/page.tsx index 483b864..11ed240 100644 --- a/src/app/portfolio/[slug]/(portfolio)/page.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/page.tsx @@ -3,7 +3,7 @@ import { getPortfolio, getProperties } from "../utils"; import DataTable from "@/app/portfolio/[slug]/components/propertyTable"; import { columns } from "@/app/portfolio/[slug]/components/propertyTableColumns"; import { PropertyWithRelations } from "@/app/db/schema/property"; -import { formatNumber } from "@/app/utils"; +import { formatNumber, convertDaysToWorkingWeeks } from "@/app/utils"; // We enfore caching of data for 60 seconds export const revalidate = 60; @@ -87,35 +87,6 @@ function SummaryBox({ } } - function convertDaysToWorkingWeeks(days: number | null) { - if (days === null) { - return "-"; - } - - const workingDaysPerWeek = 5; - - // Convert days to working weeks - const workingWeeks = days / workingDaysPerWeek; - - // Determine the range - let lowerBound = Math.floor(workingWeeks); - let upperBound = Math.ceil(workingWeeks); - - // Adjust if the fraction is very small, you might not count it as a full extra week - if (workingWeeks - lowerBound < 0.2) { - upperBound = lowerBound; - } - - if (lowerBound === 1 && upperBound === 1) { - return "1-2 weeks"; - } - - // Format the output - return lowerBound === upperBound - ? `${lowerBound} weeks` - : `${lowerBound}-${upperBound} weeks`; - } - const budgetFormatted = formatBudget(budget); const totalCostFormatted = formatMoney(totalCost); const totalValueIncreaseFormatted = formatMoney(propertyValuationIncrease); diff --git a/src/app/utils.ts b/src/app/utils.ts index ed5347b..a8b1ccf 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -1,5 +1,38 @@ import { Rating } from "./db/schema/property"; +export function convertDaysToWorkingWeeks(days: number | null) { + if (days === null) { + return "-"; + } + + const workingDaysPerWeek = 5; + + // Convert days to working weeks + const workingWeeks = days / workingDaysPerWeek; + + // Determine the range + let lowerBound = Math.floor(workingWeeks); + let upperBound = Math.ceil(workingWeeks); + + // Adjust if the fraction is very small, you might not count it as a full extra week + if (workingWeeks - lowerBound < 0.2) { + upperBound = lowerBound; + } + + if (lowerBound === 0 && upperBound === 1) { + return "1 week"; + } + + if (lowerBound === 1 && upperBound === 1) { + return "1-2 weeks"; + } + + // Format the output + return lowerBound === upperBound + ? `${lowerBound} weeks` + : `${lowerBound}-${upperBound} weeks`; +} + export const getEpcColorClass = (letter: string) => { switch (letter.toUpperCase()) { case "A":