From 35aa72bce890d8cd518be0ce9c7ec844d8a5b4c6 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 14 Apr 2026 21:07:08 +0000 Subject: [PATCH 1/5] handling incorrect aggregation due to timezones --- src/app/db/schema/crm/hubspot_deal_table.ts | 2 +- .../live/CompletionTrendsChart.tsx | 58 ++++++++++++++++--- .../your-projects/live/SurveyIssuesPanel.tsx | 2 +- .../(portfolio)/your-projects/live/page.tsx | 2 +- .../(portfolio)/your-projects/live/types.ts | 1 + 5 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/app/db/schema/crm/hubspot_deal_table.ts b/src/app/db/schema/crm/hubspot_deal_table.ts index 04c73b6..3b7161e 100644 --- a/src/app/db/schema/crm/hubspot_deal_table.ts +++ b/src/app/db/schema/crm/hubspot_deal_table.ts @@ -48,7 +48,7 @@ export const hubspotDealData = pgTable("hubspot_deal_data", { damnpMouldAndRepairComments: text("damp_mould_and_repairs_comments"), confirmedSurveyDate: timestamp("confirmed_survey_date", { precision: 6, withTimezone: true }), confirmedSurveyTime: text("confirmed_survey_time"), - SurveyedDate: timestamp("surveyed_date", { precision: 6, withTimezone: true }), + surveyedDate: timestamp("surveyed_date", { precision: 6, withTimezone: true }), createdAt: timestamp("created_at", { precision: 6, withTimezone: true }) .defaultNow() diff --git a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/CompletionTrendsChart.tsx b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/CompletionTrendsChart.tsx index dfcd492..3fa3b4e 100644 --- a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/CompletionTrendsChart.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/CompletionTrendsChart.tsx @@ -110,9 +110,9 @@ const DESIGN_TYPE_ORDER = [ function getMondayOfWeek(date: Date): string { const d = new Date(date); - const day = d.getDay(); - d.setDate(d.getDate() - (day === 0 ? 6 : day - 1)); - d.setHours(0, 0, 0, 0); + const day = d.getUTCDay(); + d.setUTCDate(d.getUTCDate() - (day === 0 ? 6 : day - 1)); + d.setUTCHours(0, 0, 0, 0); return d.toISOString().split("T")[0]; } @@ -132,7 +132,7 @@ function fillWeekGaps(keys: string[]): string[] { const end = new Date(sorted[sorted.length - 1]); while (current <= end) { result.push(current.toISOString().split("T")[0]); - current.setDate(current.getDate() + 7); + current.setUTCDate(current.getUTCDate() + 7); } return result; } @@ -314,14 +314,28 @@ export default function CompletionTrendsChart({ const isDesign = metric === "design"; const isStacked = isCoordination || isAssessments || isLodgement || isDesign; - // External assessments with no date + // Assessments (retrofit or EPC) with no date const undatedAssessments = isAssessments ? deals.filter((d) => { const o = d.outcome ?? ""; - return (o === "Surveyed" || o === "Surveyed - Pending Upload") && !d.surveyedDate; + return ( + (o === "Surveyed" || o === "Surveyed - Pending Upload" || o === "EPC Completed") && + !d.surveyedDate + ); }) : []; + // Dated assessments broken down by type — used for summary badges + const retrofitDeals = isAssessments + ? deals.filter((d) => { + const o = d.outcome ?? ""; + return (o === "Surveyed" || o === "Surveyed - Pending Upload") && !!d.surveyedDate; + }) + : []; + const epcDeals = isAssessments + ? deals.filter((d) => d.outcome === "EPC Completed" && !!d.surveyedDate) + : []; + // Build chart data let chartData: Record[]; let categories: string[]; @@ -363,7 +377,37 @@ export default function CompletionTrendsChart({ Trends Over Time - {totalCompleted !== null && ( + {isAssessments ? ( +
+ {([ + { label: "Retrofit Assessments", dealList: retrofitDeals }, + { label: "EPCs", dealList: epcDeals }, + ] as const).filter(({ dealList }) => dealList.length > 0).map(({ label, dealList }) => ( + + ))} +
+ ) : totalCompleted !== null && (
{totalCompleted} diff --git a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/SurveyIssuesPanel.tsx b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/SurveyIssuesPanel.tsx index d8c576b..65cc8ab 100644 --- a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/SurveyIssuesPanel.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/SurveyIssuesPanel.tsx @@ -5,7 +5,7 @@ import { AlertCircle } from "lucide-react"; import { Card, CardContent } from "@/app/shadcn_components/ui/card"; import type { ClassifiedDeal } from "./types"; -const SUCCESSFUL_OUTCOMES = new Set(["Surveyed", "Surveyed - Pending Upload"]); +const SUCCESSFUL_OUTCOMES = new Set(["Surveyed", "Surveyed - Pending Upload", "EPC Completed"]); const COLUMNS: (keyof ClassifiedDeal)[] = [ "dealname", diff --git a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/page.tsx b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/page.tsx index 55da823..f7002fc 100644 --- a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/page.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/page.tsx @@ -53,7 +53,7 @@ function mapDbRowToHubspotDeal(row: DbDeal): HubspotDeal { measuresLodgementDate: row.measuresLodgementDate, fullLodgementDate: row.lodgementDate, confirmedSurveyDate: row.confirmedSurveyDate, - surveyedDate: row.SurveyedDate, + surveyedDate: row.surveyedDate, designType: row.dealType, createdAt: row.createdAt, updatedAt: row.updatedAt, diff --git a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/types.ts b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/types.ts index 931413c..40fa764 100644 --- a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/types.ts +++ b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/types.ts @@ -230,6 +230,7 @@ export type DocumentDrawerState = { export const SURVEYOR_OUTCOMES = [ "Surveyed", "Surveyed - Pending Upload", + "EPC Completed", "Tenant Refusal", "Other", "Not Viable", From b894591024167b154c9878a788fab73eb7c6185e Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 15 Apr 2026 08:31:07 +0000 Subject: [PATCH 2/5] added current epc tooltip --- .../[slug]/components/CurrentEpcTooltip.tsx | 51 +++++++++++++++++++ .../components/propertyTableColumns.tsx | 6 ++- 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/app/portfolio/[slug]/components/CurrentEpcTooltip.tsx diff --git a/src/app/portfolio/[slug]/components/CurrentEpcTooltip.tsx b/src/app/portfolio/[slug]/components/CurrentEpcTooltip.tsx new file mode 100644 index 0000000..9002030 --- /dev/null +++ b/src/app/portfolio/[slug]/components/CurrentEpcTooltip.tsx @@ -0,0 +1,51 @@ +"use client"; + +import { QuestionMarkCircleIcon } from "@heroicons/react/24/outline"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/app/shadcn_components/ui/tooltip"; + +export function CurrentEpcTooltip() { + return ( + + + + + + +
+

Current EPC Rating

+

How we calculate this rating

+
+
+

+ We show a modelled rating when: +

+
    +
  • The lodged EPC is expired or invalid
  • +
  • You have told us the property differs from the last survey
  • +
+
+
+

+ This rating may differ from the lodged EPC because we re-model under{" "} + SAP 10. +

+
+
+
+
+ ); +} diff --git a/src/app/portfolio/[slug]/components/propertyTableColumns.tsx b/src/app/portfolio/[slug]/components/propertyTableColumns.tsx index 50ff5b5..b447181 100644 --- a/src/app/portfolio/[slug]/components/propertyTableColumns.tsx +++ b/src/app/portfolio/[slug]/components/propertyTableColumns.tsx @@ -21,6 +21,7 @@ import { TENURE_OPTIONS, MAINFUEL_OPTIONS, } from "@/app/utils/propertyFilters"; +import { CurrentEpcTooltip } from "./CurrentEpcTooltip"; /* ----------------------------------------------------------------------- Helpers @@ -222,7 +223,10 @@ const coreColumns: ColumnDef[] = [ { accessorKey: "currentEpc", header: () => ( -
Current EPC
+
+ Current EPC + +
), cell: ({ row }) => (
From c01f5bee40a634012e8f13e1629725abd80f1920 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 15 Apr 2026 09:50:17 +0000 Subject: [PATCH 3/5] added notify for user when we've remodelled --- .../api/property-meta/[propertyId]/route.ts | 1 + src/app/db/schema/property.ts | 1 + .../assessment/EpcInfoTooltip.tsx | 67 ---------------- .../[propertyId]/assessment/page.tsx | 80 ++----------------- .../building-passport/[propertyId]/page.tsx | 77 ++---------------- 5 files changed, 14 insertions(+), 212 deletions(-) delete mode 100644 src/app/portfolio/[slug]/building-passport/[propertyId]/assessment/EpcInfoTooltip.tsx diff --git a/src/app/api/property-meta/[propertyId]/route.ts b/src/app/api/property-meta/[propertyId]/route.ts index 9bda547..81f47ae 100644 --- a/src/app/api/property-meta/[propertyId]/route.ts +++ b/src/app/api/property-meta/[propertyId]/route.ts @@ -26,6 +26,7 @@ export async function GET(request: NextRequest, props: { params: Promise<{ prope tenure: true, currentEpcRating: true, currentSapPoints: true, + originalSapPoints: true, updatedAt: true, currentValuation: true, }, diff --git a/src/app/db/schema/property.ts b/src/app/db/schema/property.ts index 3d2dd56..58e956f 100644 --- a/src/app/db/schema/property.ts +++ b/src/app/db/schema/property.ts @@ -34,6 +34,7 @@ export interface PropertyMeta { tenure: string; currentEpcRating: string; currentSapPoints: number; + originalSapPoints: number | null; updatedAt: string; currentValuation: number | null; detailsEpc: { diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/assessment/EpcInfoTooltip.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/assessment/EpcInfoTooltip.tsx deleted file mode 100644 index bdc7d17..0000000 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/assessment/EpcInfoTooltip.tsx +++ /dev/null @@ -1,67 +0,0 @@ -"use client"; - -import { QuestionMarkCircleIcon } from "@heroicons/react/24/outline"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@/app/shadcn_components/ui/tooltip"; - -const EPC_BANDS = [ - { band: "A", range: "92–100", color: "#117d58", desc: "Exceptional, near-zero energy bills, usually new-builds or eco-homes." }, - { band: "B", range: "81–91", color: "#2da55c", desc: "Very efficient, often featuring solar panels, high-grade insulation, and modern heating." }, - { band: "C", range: "69–80", color: "#8dbd40", desc: "Good, above-average efficiency; common target for retrofitting existing homes." }, - { band: "D", range: "55–68", color: "#f7cd14", desc: "Average, the typical rating for many homes in the UK." }, - { band: "E", range: "39–54", color: "#f3a96a", desc: "Below average, likely requires better insulation and boiler upgrades." }, - { band: "F", range: "21–38", color: "#ef8026", desc: "Poor, high energy costs and lower energy performance." }, - { band: "G", range: "1–20", color: "#e41e3b", desc: "Very poor, least efficient, high energy costs." }, -]; - -export function EpcInfoTooltip() { - return ( - - - - - - -
-

EPC Rating Bands

-

Based on the SAP score (1–100)

-
-
- {EPC_BANDS.map(({ band, range, color, desc }) => ( -
- - {band} - -
-

{range}

-

{desc}

-
-
- ))} -
-
-

- SAP score — Standard Assessment Procedure. A government-approved method for rating the energy performance of homes on a scale of 1 to 100. -

-
-
-
-
- ); -} diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/assessment/page.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/assessment/page.tsx index 78d7b26..a2123cb 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/assessment/page.tsx +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/assessment/page.tsx @@ -18,36 +18,10 @@ import { import { getSolarData, getSolarScenarioData } from "../solar-analysis/utils"; import PropertyMapWrapper from "../solar-analysis/PropertyMapWrapper"; import SolarSimulationWrapper from "../solar-analysis/SolarSimulationWrapper"; -import { EpcInfoTooltip } from "./EpcInfoTooltip"; +import { CurrentEfficiencyCard } from "@/app/components/building-passport/CurrentEfficiencyCard"; // ── Helpers ──────────────────────────────────────────────────────────────────── -function getEpcHex(letter: string | null | undefined): string { - switch (letter?.toUpperCase()) { - case "A": return "#117d58"; - case "B": return "#2da55c"; - case "C": return "#8dbd40"; - case "D": return "#f7cd14"; - case "E": return "#f3a96a"; - case "F": return "#ef8026"; - case "G": return "#e41e3b"; - default: return "#9ca3af"; - } -} - -function getEpcDescription(letter: string | null | undefined): string { - switch (letter?.toUpperCase()) { - case "A": - case "B": return "This property is performing at or above modern energy standards."; - case "C": return "This property meets modern energy performance benchmarks."; - case "D": return "This property is performing slightly below modern energy standards."; - case "E": return "This property is performing below modern energy standards."; - case "F": - case "G": return "This property is performing significantly below modern energy standards."; - default: return "Energy performance data is not yet available for this property."; - } -} - function getDirectionLabel(az: number): { label: string; short: string } { const norm = ((az % 360) + 360) % 360; if (norm >= 337.5 || norm < 22.5) return { short: "N", label: "North" }; @@ -211,10 +185,6 @@ export default async function PreAssessmentReport(props: { const rawSolar = await getSolarData(Number(propertyMeta.uprn)); const solarData = rawSolar ?? null; - const epcLetter = propertyMeta.currentEpcRating ?? null; - const sapScore = propertyMeta.currentSapPoints ?? 0; - const epcHex = getEpcHex(epcLetter); - // Solar derived values const sp = solarData?.googleApiResponse?.solarPotential ?? null; const solarScenarioData = solarData ? await getSolarScenarioData(String(solarData.id)) : null; @@ -265,49 +235,11 @@ export default async function PreAssessmentReport(props: {
{/* EPC Hero — matches overview page style */} -
-
-
-
-

- Current Efficiency State -

- -
-
- - {epcLetter ?? "—"} - - - / {sapScore || "—"} - -
-

- {getEpcDescription(epcLetter)} -

-
-
-
-
-
-
- Very Inefficient - Very Efficient -
-
-
+ {/* Right column: 3 metric cards + general features grid */}
diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/page.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/page.tsx index ee422a3..1035edf 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/page.tsx +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/page.tsx @@ -15,6 +15,7 @@ import { getInstalledMeasuresByUprn, } from "./utils"; import { HeritageTooltip } from "./HeritageTooltip"; +import { CurrentEfficiencyCard } from "@/app/components/building-passport/CurrentEfficiencyCard"; export const revalidate = 1; @@ -25,33 +26,6 @@ function formatGbp(value: number | null | undefined): string { return `£${Math.round(value).toLocaleString("en-GB")}`; } -/** Map EPC letter to its hex color from the project's palette */ -function getEpcHex(letter: string | null | undefined): string { - switch (letter?.toUpperCase()) { - case "A": return "#117d58"; - case "B": return "#2da55c"; - case "C": return "#8dbd40"; - case "D": return "#f7cd14"; - case "E": return "#f3a96a"; - case "F": return "#ef8026"; - case "G": return "#e41e3b"; - default: return "#9ca3af"; - } -} - -function getEpcDescription(letter: string | null | undefined): string { - switch (letter?.toUpperCase()) { - case "A": - case "B": return "This property is performing at or above modern energy standards."; - case "C": return "This property meets modern energy performance benchmarks."; - case "D": return "This property is performing slightly below modern energy standards."; - case "E": return "This property is performing below modern energy standards."; - case "F": - case "G": return "This property is performing significantly below modern energy standards."; - default: return "Energy performance data is not yet available for this property."; - } -} - // ── Sub-components ──────────────────────────────────────────────────────────── function SectionHeading({ icon, label }: { icon: React.ReactNode; label: string }) { @@ -102,10 +76,6 @@ export default async function BuildingPassportHome(props: { (conditionReport.hotWaterEnergyCostCurrent ?? 0) + (conditionReport.lightingEnergyCostCurrent ?? 0); - const epcLetter = propertyMeta.currentEpcRating ?? null; - const sapScore = propertyMeta.currentSapPoints ?? 0; - const epcHex = getEpcHex(epcLetter); - return (
@@ -113,46 +83,11 @@ export default async function BuildingPassportHome(props: {
{/* EPC Hero */} -
-
-
-

- Current Efficiency State -

-
- - {epcLetter ?? "—"} - - - / {sapScore || "—"} - -
-

- {getEpcDescription(epcLetter)} -

-
-
-
-
-
-
- Very Inefficient - Very Efficient -
-
-
+ {/* Energy Stats + Heritage Status */}
From ab872d8a73ee013c299040a3c111ab22d9689d80 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 15 Apr 2026 10:00:01 +0000 Subject: [PATCH 4/5] added missing tooltip files --- .../CurrentEfficiencyCard.tsx | 118 ++++++++++++++++++ .../building-passport/EpcInfoTooltip.tsx | 67 ++++++++++ .../building-passport/LodgedEpcTooltip.tsx | 58 +++++++++ 3 files changed, 243 insertions(+) create mode 100644 src/app/components/building-passport/CurrentEfficiencyCard.tsx create mode 100644 src/app/components/building-passport/EpcInfoTooltip.tsx create mode 100644 src/app/components/building-passport/LodgedEpcTooltip.tsx diff --git a/src/app/components/building-passport/CurrentEfficiencyCard.tsx b/src/app/components/building-passport/CurrentEfficiencyCard.tsx new file mode 100644 index 0000000..abcd2a6 --- /dev/null +++ b/src/app/components/building-passport/CurrentEfficiencyCard.tsx @@ -0,0 +1,118 @@ +import { sapToEpc } from "@/app/utils"; +import { EpcInfoTooltip } from "./EpcInfoTooltip"; +import { LodgedEpcTooltip } from "./LodgedEpcTooltip"; + +interface CurrentEfficiencyCardProps { + epcRating: string | null; + sapPoints: number; + originalSapPoints?: number | null; +} + +function getEpcHex(letter: string | null | undefined): string { + switch (letter?.toUpperCase()) { + case "A": return "#117d58"; + case "B": return "#2da55c"; + case "C": return "#8dbd40"; + case "D": return "#f7cd14"; + case "E": return "#f3a96a"; + case "F": return "#ef8026"; + case "G": return "#e41e3b"; + default: return "#9ca3af"; + } +} + +function getEpcDescription(letter: string | null | undefined): string { + switch (letter?.toUpperCase()) { + case "A": + case "B": return "This property is performing at or above modern energy standards."; + case "C": return "This property meets modern energy performance benchmarks."; + case "D": return "This property is performing slightly below modern energy standards."; + case "E": return "This property is performing below modern energy standards."; + case "F": + case "G": return "This property is performing significantly below modern energy standards."; + default: return "Energy performance data is not yet available for this property."; + } +} + +export function CurrentEfficiencyCard({ + epcRating, + sapPoints, + originalSapPoints, +}: CurrentEfficiencyCardProps) { + const epcHex = getEpcHex(epcRating); + const lodgedLetter = originalSapPoints ? sapToEpc(originalSapPoints) : null; + const showLodgedBadge = lodgedLetter !== null && lodgedLetter !== epcRating; + const lodgedHex = getEpcHex(lodgedLetter); + + return ( +
+
+ + {showLodgedBadge && ( +
+
+ + Lodged EPC + + +
+
+ + {lodgedLetter} + + {originalSapPoints != null && ( + + / {Math.round(originalSapPoints)} + + )} +
+
+ )} + +
+
+

+ Current Efficiency State +

+ +
+
+ + {epcRating ?? "—"} + + + / {sapPoints || "—"} + +
+

+ {getEpcDescription(epcRating)} +

+
+ +
+
+
+
+
+ Very Inefficient + Very Efficient +
+
+
+ ); +} diff --git a/src/app/components/building-passport/EpcInfoTooltip.tsx b/src/app/components/building-passport/EpcInfoTooltip.tsx new file mode 100644 index 0000000..bdc7d17 --- /dev/null +++ b/src/app/components/building-passport/EpcInfoTooltip.tsx @@ -0,0 +1,67 @@ +"use client"; + +import { QuestionMarkCircleIcon } from "@heroicons/react/24/outline"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/app/shadcn_components/ui/tooltip"; + +const EPC_BANDS = [ + { band: "A", range: "92–100", color: "#117d58", desc: "Exceptional, near-zero energy bills, usually new-builds or eco-homes." }, + { band: "B", range: "81–91", color: "#2da55c", desc: "Very efficient, often featuring solar panels, high-grade insulation, and modern heating." }, + { band: "C", range: "69–80", color: "#8dbd40", desc: "Good, above-average efficiency; common target for retrofitting existing homes." }, + { band: "D", range: "55–68", color: "#f7cd14", desc: "Average, the typical rating for many homes in the UK." }, + { band: "E", range: "39–54", color: "#f3a96a", desc: "Below average, likely requires better insulation and boiler upgrades." }, + { band: "F", range: "21–38", color: "#ef8026", desc: "Poor, high energy costs and lower energy performance." }, + { band: "G", range: "1–20", color: "#e41e3b", desc: "Very poor, least efficient, high energy costs." }, +]; + +export function EpcInfoTooltip() { + return ( + + + + + + +
+

EPC Rating Bands

+

Based on the SAP score (1–100)

+
+
+ {EPC_BANDS.map(({ band, range, color, desc }) => ( +
+ + {band} + +
+

{range}

+

{desc}

+
+
+ ))} +
+
+

+ SAP score — Standard Assessment Procedure. A government-approved method for rating the energy performance of homes on a scale of 1 to 100. +

+
+
+
+
+ ); +} diff --git a/src/app/components/building-passport/LodgedEpcTooltip.tsx b/src/app/components/building-passport/LodgedEpcTooltip.tsx new file mode 100644 index 0000000..b37b1f1 --- /dev/null +++ b/src/app/components/building-passport/LodgedEpcTooltip.tsx @@ -0,0 +1,58 @@ +"use client"; + +import { QuestionMarkCircleIcon } from "@heroicons/react/24/outline"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/app/shadcn_components/ui/tooltip"; + +export function LodgedEpcTooltip() { + return ( + + + + + + +
+

Lodged EPC Rating

+

From the official EPC register

+
+
+

+ This is the rating recorded on the{" "} + official EPC register at the time of the last survey. +

+

+ Your current rating may differ because we re-model this property under{" "} + SAP 10, which uses updated energy factors and can produce a different result than the original survey. +

+

+ We also re-model when the EPC has{" "} + expired or is{" "} + invalid, or when you have told us the property has{" "} + changed since the last survey. +

+
+
+

+ SAP 10 is the latest version of the{" "} + Standard Assessment Procedure, introduced to better reflect modern energy use and lower-carbon fuels. +

+
+
+
+
+ ); +} From 29ad7489c420f882a4c3f4440680c0a1556f346b Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 15 Apr 2026 13:01:09 +0000 Subject: [PATCH 5/5] tweaking colours on EPC bar --- .../CurrentEfficiencyCard.tsx | 23 ++++++++++++++++++- .../components/propertyTableColumns.tsx | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/app/components/building-passport/CurrentEfficiencyCard.tsx b/src/app/components/building-passport/CurrentEfficiencyCard.tsx index abcd2a6..723cb63 100644 --- a/src/app/components/building-passport/CurrentEfficiencyCard.tsx +++ b/src/app/components/building-passport/CurrentEfficiencyCard.tsx @@ -21,6 +21,27 @@ function getEpcHex(letter: string | null | undefined): string { } } +const EPC_STOPS = [ + { sap: 0, color: "#e41e3b" }, // G + { sap: 21, color: "#ef8026" }, // F + { sap: 39, color: "#f3a96a" }, // E + { sap: 55, color: "#f7cd14" }, // D + { sap: 69, color: "#8dbd40" }, // C + { sap: 81, color: "#2da55c" }, // B + { sap: 92, color: "#117d58" }, // A +]; + +function getEpcGradient(sapPoints: number, epcHex: string): string { + if (sapPoints <= 0) return epcHex; + const stops = EPC_STOPS.filter(s => s.sap <= sapPoints); + const gradientStops = stops.map(s => { + const pct = s.sap === 0 ? 0 : (s.sap / sapPoints) * 100; + return `${s.color} ${pct.toFixed(1)}%`; + }); + gradientStops.push(`${epcHex} 100%`); + return `linear-gradient(to right, ${gradientStops.join(", ")})`; +} + function getEpcDescription(letter: string | null | undefined): string { switch (letter?.toUpperCase()) { case "A": @@ -104,7 +125,7 @@ export function CurrentEfficiencyCard({ className="absolute inset-y-0 left-0 rounded-full" style={{ width: `${Math.min(100, Math.max(2, sapPoints))}%`, - background: "linear-gradient(to right, #e41e3b, #ef8026, #f7cd14, #8dbd40, #117d58)", + background: getEpcGradient(sapPoints, epcHex), }} />
diff --git a/src/app/portfolio/[slug]/components/propertyTableColumns.tsx b/src/app/portfolio/[slug]/components/propertyTableColumns.tsx index b447181..7ef022e 100644 --- a/src/app/portfolio/[slug]/components/propertyTableColumns.tsx +++ b/src/app/portfolio/[slug]/components/propertyTableColumns.tsx @@ -200,7 +200,7 @@ const coreColumns: ColumnDef[] = [