From b3253d7e84572c5fb0e0499e7e2f7d57cff1b701 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Thu, 6 Nov 2025 12:44:42 +0000 Subject: [PATCH] push to production --- src/app/page.tsx | 1 - .../your-projects/live/DealStageChart.tsx | 206 +++++++++++------- .../live/SurveyedResultsPieChart.tsx | 139 ++++++++---- 3 files changed, 224 insertions(+), 122 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 5e7f0c4..3de27a2 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -5,7 +5,6 @@ import MicrosoftSignInButton from "./components/signin/MicrosoftSignInButton"; import EmailSignInButton from "./components/signin/EmailSignInButton"; import { redirect } from "next/navigation"; import Image from "next/image"; -import "@tremor/react/dist/esm/tremor.css"; export default async function Home(props: { diff --git a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/DealStageChart.tsx b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/DealStageChart.tsx index 0466604..b9d7dbe 100644 --- a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/DealStageChart.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/DealStageChart.tsx @@ -9,56 +9,85 @@ const STAGE_ORDER = [ "Survey in progress", "Coordination + design", "Ready for install", - "Installed", + "Installed - Ready for Post work EPC", "Needs support from HA", "Not viable for funding", ]; const stage = (label: string) => STAGE_ORDER.find((s) => s === label)!; +// 🏷️ Deal stage → display stage mapping const STAGE_LABELS: Record = { - "1617223910": stage("Initial planning"), - "3583836399": stage("Initial planning"), + "1617223910": stage("Initial planning"), // 0 - [Ops] Backlog + "3583836399": stage("Initial planning"), // 0 - [Ops] Route Planning - "3589581001": stage("Booking team to contact tenant"), - "3569878239": stage("Booking team to contact tenant"), - "1617223911": stage("Booking team to contact tenant"), - "1984184569": stage("Booking team to contact tenant"), - "3569572028": stage("Booking team to contact tenant"), - "3570936026": stage("Booking team to contact tenant"), - "2663668937": stage("Needs support from HA"), - "1984401629": stage("Survey in progress"), - "2558220518": stage("Booking team to contact tenant"), - "3474594026": stage("Booking team to contact tenant"), + "3589581001": stage("Booking team to contact tenant"), // 1 - [Bookings] Ready for Bookings Team + "3569878239": stage("Booking team to contact tenant"), // 1 - [Bookings] Send initial booking SMS + "1617223911": stage("Booking team to contact tenant"), // 1 - [Bookings] Send Email + "1984184569": stage("Booking team to contact tenant"), // 1 - [Bookings] Phone booking + "3569572028": stage("Booking team to contact tenant"), // 1 - [Bookings] Preferences received from Tenant + "3570936026": stage("Booking team to contact tenant"), // 1 - [Bookings] Send Confirmation Comms + "2663668937": stage("Needs support from HA"), // 4 - [Bookings/Sales] Booking issues - needs HA support (Check with Aidan) + "1984401629": stage("Survey in progress"), // 2 - [Bookings/Ops/Sales] No Contact Details - Ready for Route + "2558220518": stage("Booking team to contact tenant"), // 1 - [Ops] Not attempted - needs reallocation + "3474594026": stage("Booking team to contact tenant"), // 1 - [Ops/Bookings] Rebooked - Needs updating - "1617223912": stage("Survey in progress"), - "1617223913": stage("Survey in progress"), - "3206388924": stage("Survey in progress"), - "1617223915": stage("Survey in progress"), - "1617223917": stage("Not viable for funding"), - "2571417798": stage("Booking team to contact tenant"), + "1617223912": stage("Survey in progress"), // 2 - [Ops] Ready for Assignment to Route + "1617223913": stage("Survey in progress"), // 2 - [Ops] Survey in Progress + "3206388924": stage("Survey in progress"), // 2 - [Ops] Surveyed - Pending Upload from Surveyor + "1617223915": stage("Survey in progress"), // 2 - [Ops] No Access - Need Sign Off + "1617223917": stage("Not viable for funding"), // 3 - [Ops] No Access - No Revisit + "2571417798": stage("Booking team to contact tenant"), // 1 - [Ops] Surveyed under 2019 - Needs Re-survey - "1617223916": stage("Coordination + design"), - "2628341989": stage("Coordination + design"), - "3441170637": stage("Coordination + design"), + "1617223916": stage("Coordination + design"), // 5 - [Ops] Properties to Review Manually + "2628341989": stage("Coordination + design"), // 5 - [Ops] Assessment needs correction + "3441170637": stage("Coordination + design"), // 5 - [Ops] Awaiting PV Design - "1887735998": stage("Not viable for funding"), - "3061261536": stage("Needs support from HA"), - "1887735999": stage("Needs support from HA"), - "3016601828": stage("Needs support from HA"), - "1617223914": stage("Coordination + design"), - "2628233422": stage("Coordination + design"), - "2702650617": stage("Coordination + design"), - "2473886962": stage("Coordination + design"), + "1887735998": stage("Not viable for funding"), // 3 - [Ops] Not Viable + "3061261536": stage("Needs support from HA"), // 4 - [Sales/Tech] Major condition issue + "1887735999": stage("Needs support from HA"), // 4 - [Ops] Needs HA Works + "3016601828": stage("Needs support from HA"), // 4 - [Engagement Team] EPC C Before Works + "1617223914": stage("Coordination + design"), // 5 - [Ops] Surveyed in Pashub, Transit Job to Co-ordination + "2628233422": stage("Coordination + design"), // 5 - [Coordination] Ready for coordination + "2702650617": stage("Coordination + design"), // 5 - [Design] Ready for Design + "2473886962": stage("Coordination + design"), // 5 - [Design] Design in progress - "1668803774": stage("Ready for install"), - "3440363736": stage("Ready for install"), - "2769407183": stage("Needs support from HA"), + "1668803774": stage("Ready for install"), // 6 - [Finance] Ready for Invoicing + "3440363736": stage("Ready for install"), // 6 - [Finance] Needs Invoicing - Files Sent + "2769407183": stage("Needs support from HA"), // 4 - [Ops] PV - Needs Heating Upgrade (Pre EPR D) }; +// 🧩 Reasons for exception stages (HA support / Not viable) +const STAGE_REASONS: Record = { + // ---- Needs support from HA ---- + "2663668937": "Booking issues due to tenant difficulties.", + "3061261536": "Awaab's Law", + "1887735999": "", + "3016601828": "RA is currently EPR C. Convert to EPC?", + "2769407183": "Needs HA heating upgrade. Domna/HA discussion required.", + + // ---- Not viable for funding ---- + "1617223917": "", + "1887735998": "", +}; + +// ✅ Define an explicit Deal type for clarity +interface Deal { + dealname: string; + landlordPropertyId: string; + dealstage: string; + reason?: string; + [key: string]: any; +} + interface DealStageChartProps { - deals: any[]; - onOpenTable?: (stageName: string, filteredDeals: any[]) => void; + deals: Deal[]; + onOpenTable?: ( + stageName: string, + filteredDeals: Deal[], + columns?: string[], + columnLabels?: { [key: string]: string } + ) => void; } export function DealStageChart({ deals, onOpenTable }: DealStageChartProps) { @@ -77,44 +106,72 @@ export function DealStageChart({ deals, onOpenTable }: DealStageChartProps) { })); }, [deals]); - const total = useMemo(() => deals.length, [deals]); + const total = deals.length; const handleBarClick = (value: { name: string; value: number }) => { - const filtered = deals.filter((d) => { - const stageId = d.dealstage || "unknown"; - const stageName = STAGE_LABELS[stageId] || "Unknown Stage"; - return stageName === value.name; - }); - onOpenTable?.(value.name, filtered); + const filteredDeals: Deal[] = deals + .filter((d) => { + const stageName = STAGE_LABELS[d.dealstage] || "Unknown Stage"; + return stageName === value.name; + }) + .map((d) => ({ + ...d, + // ✅ Always provide a string to avoid undefined issues + reason: STAGE_REASONS[d.dealstage] ?? "", + })); + + const isException = + value.name === "Needs support from HA" || + value.name === "Not viable for funding"; + + // Add "Reason" column if it's an exception stage + const columns = isException + ? ["dealname", "landlordPropertyId", "reason"] + : ["dealname", "landlordPropertyId"]; + + const columnLabels = isException + ? { + dealname: "Address Ref.", + landlordPropertyId: "Property Ref.", + reason: "Reason", + } + : { + dealname: "Address Ref.", + landlordPropertyId: "Property Ref.", + }; + + // ✅ Explicit cast ensures no type mismatch + onOpenTable?.(value.name, filteredDeals, columns, columnLabels as Record); }; - // ✅ Split into normal and exception stages + // Split into normal + exception stages const normalStages = data.filter( (d) => !["Needs support from HA", "Not viable for funding"].includes(d.name) && - d.name && d.name !== "" ); + const exceptionStages = data.filter((d) => ["Needs support from HA", "Not viable for funding"].includes(d.name) ); - return ( -
{/* Reduced gap */} - {/* Main Progress Chart */} - -
- - Project Progress by Stage - -

- Click a bar to view related properties -

-

- Total: {total.toLocaleString()} properties -

-
+return ( +
+ {/* ✅ Main Progress Chart */} + +
+ + Project Progress by Stage + +

+ Click a bar to view related properties +

+

+ Total: {total.toLocaleString()} properties +

+
+
- +
+
- {/* Exception Chart */} - -
- - Needs HA Support & Not Viable - -

- Click to explore exceptions -

-
+ {/* 🚨 Exception Chart */} + +
+ + Needs HA Support & Not Viable + +

+ Click to explore exception properties (reasons appear in table) +

+
+
- -
- ); +
+
+
+); } diff --git a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/SurveyedResultsPieChart.tsx b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/SurveyedResultsPieChart.tsx index df1bda9..b126c43 100644 --- a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/SurveyedResultsPieChart.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/SurveyedResultsPieChart.tsx @@ -1,7 +1,7 @@ "use client"; import { DonutChart, Card, Title } from "@tremor/react"; -import { useMemo } from "react"; +import { useMemo, useState } from "react"; interface SurveyedPieChartProps { deals: Record[]; @@ -33,6 +33,7 @@ export default function SurveyedPieChart({ "slate-400", "gray-300", "gray-100", + "gray-200", ]; const data = useMemo(() => { @@ -57,55 +58,97 @@ export default function SurveyedPieChart({ onOpenTable?.(value.name, filteredDeals); }; + const [hovered, setHovered] = useState(null); + return ( - - {/* Header */} -
- - Survey Performance - -

- Click a segment or label to view filtered properties -

-
- - {/* Chart */} -
- `${n.toLocaleString()}`} - colors={colors} - onValueChange={handleClick} - showLabel={false} - className="w-64 h-64 cursor-pointer" - customTooltip={({ payload }) => { - const item = payload?.[0]?.payload; - if (!item) return null; - const { name, amount } = item; - return ( -
-
- {name} - {amount.toLocaleString()} -
-
- ); - }} - /> - {data.length > 0 && ( -
- - {data.reduce((a, b) => a + b.amount, 0)} - + + {/* Header */} +
+ + Survey Performance + +

+ Click a segment or label to view filtered properties +

- )} -
- + {/* Donut Chart (Centered) */} +
+ `${n.toLocaleString()}`} + colors={colors} + onValueChange={handleClick} + showLabel={false} + className="w-64 h-64 cursor-pointer" + customTooltip={({ payload }) => { + const item = payload?.[0]?.payload; + if (!item) return null; + const { name, amount } = item; + return ( +
+
+ + {name} + + {amount.toLocaleString()} +
+
+ ); + }} + /> + {data.length > 0 && ( +
+ + {data.reduce((a, b) => a + b.amount, 0)} + +
+ )} +
+ + {/* Legend (Clean Grid Layout) */} +
+ {data.map((item, idx) => ( +
handleClick(item)} + onMouseEnter={() => setHovered(item.name)} + onMouseLeave={() => setHovered(null)} + className="relative flex items-center space-x-2 text-sm text-gray-700 hover:text-gray-900 cursor-pointer transition-colors" + > + + + {item.name} + + + {item.percentage}% + + + {/* Tooltip on hover */} + {hovered === item.name && ( +
+
+ + {item.name} + + {item.amount.toLocaleString()} +
+
+ )} +
+ ))} +
+ ); }