From 8ecc159bf3008ca96817c9aa3b3c062d91d58f3f Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Wed, 5 Nov 2025 20:09:20 +0000 Subject: [PATCH] save current progress --- src/app/db/schema/crm/hubspot_deal_table.ts | 3 + src/app/globals.css | 2 + src/app/page.tsx | 2 + .../your-projects/live/DealStageChart.tsx | 127 ++++++++++-------- .../(portfolio)/your-projects/live/Report.tsx | 73 +++++++--- 5 files changed, 130 insertions(+), 77 deletions(-) diff --git a/src/app/db/schema/crm/hubspot_deal_table.ts b/src/app/db/schema/crm/hubspot_deal_table.ts index 753a737..7b28343 100644 --- a/src/app/db/schema/crm/hubspot_deal_table.ts +++ b/src/app/db/schema/crm/hubspot_deal_table.ts @@ -15,6 +15,9 @@ export const hubspotDealData = pgTable("hubspot_deal_data", { outcome: text("outcome"), outcomeNotes: text("outcome_notes"), + majorConditionIssueDescription: text("major_condition_issue_description"), + majorConditionIssuePhotos: text("major_condition_issue_photos"), + createdAt: timestamp("created_at", { precision: 6, withTimezone: true }) .defaultNow() .notNull(), diff --git a/src/app/globals.css b/src/app/globals.css index 3b98d12..7c2a88d 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -2,6 +2,8 @@ @tailwind components; @tailwind utilities; + + /* 🌞 Light Theme (raw HSL values) */ :root { --background: 0 0% 100%; diff --git a/src/app/page.tsx b/src/app/page.tsx index fcabe67..5e7f0c4 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -5,6 +5,8 @@ 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: { searchParams: Promise<{ error?: string }>; 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 dc999eb..0466604 100644 --- a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/DealStageChart.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/DealStageChart.tsx @@ -7,11 +7,11 @@ const STAGE_ORDER = [ "Initial planning", "Booking team to contact tenant", "Survey in progress", - "Not viable", - "Needs Heating Upgrade installed", - "Needs support", "Coordination + design", - "Ready to be installed", + "Ready for install", + "Installed", + "Needs support from HA", + "Not viable for funding", ]; const stage = (label: string) => STAGE_ORDER.find((s) => s === label)!; @@ -26,8 +26,8 @@ const STAGE_LABELS: Record = { "1984184569": stage("Booking team to contact tenant"), "3569572028": stage("Booking team to contact tenant"), "3570936026": stage("Booking team to contact tenant"), - "2663668937": stage("Booking team to contact tenant"), - "1984401629": 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"), @@ -35,27 +35,25 @@ const STAGE_LABELS: Record = { "1617223913": stage("Survey in progress"), "3206388924": stage("Survey in progress"), "1617223915": stage("Survey in progress"), - "1617223917": stage("Survey in progress"), - "2571417798": stage("Survey in progress"), - "1887736000": stage("Survey in progress"), - "1617223916": stage("Survey in progress"), - "2628341989": stage("Survey in progress"), - "3441170637": stage("Survey in progress"), + "1617223917": stage("Not viable for funding"), + "2571417798": stage("Booking team to contact tenant"), - "1887735998": stage("Not viable"), + "1617223916": stage("Coordination + design"), + "2628341989": stage("Coordination + design"), + "3441170637": stage("Coordination + design"), - "3061261536": stage("Needs Heating Upgrade installed"), - "1887735999": stage("Needs Heating Upgrade installed"), - "3016601828": stage("Needs Heating Upgrade installed"), + "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"), - "1617223914": stage("Needs support"), - "2628233422": stage("Needs support"), - "2702650617": stage("Needs support"), - "2473886962": stage("Needs support"), - - "1668803774": stage("Coordination + design"), - "3440363736": stage("Coordination + design"), - "2769407183": stage("Needs Heating Upgrade installed"), + "1668803774": stage("Ready for install"), + "3440363736": stage("Ready for install"), + "2769407183": stage("Needs support from HA"), }; interface DealStageChartProps { @@ -67,31 +65,18 @@ export function DealStageChart({ deals, onOpenTable }: DealStageChartProps) { const data = useMemo(() => { const counts: Record = {}; - // Count deals by stage deals.forEach((d) => { const stageId = d.dealstage || "unknown"; const stageName = STAGE_LABELS[stageId] || "Unknown Stage"; counts[stageName] = (counts[stageName] || 0) + 1; }); - // Ensure every stage in STAGE_ORDER has a default of 0 - const complete = STAGE_ORDER.map((name) => ({ + return STAGE_ORDER.map((name) => ({ name, value: counts[name] || 0, })); - - // Sort according to STAGE_ORDER (just in case) - return complete.sort((a, b) => { - const aIndex = STAGE_ORDER.indexOf(a.name); - const bIndex = STAGE_ORDER.indexOf(b.name); - return ( - (aIndex === -1 ? Number.MAX_SAFE_INTEGER : aIndex) - - (bIndex === -1 ? Number.MAX_SAFE_INTEGER : bIndex) - ); - }); }, [deals]); - // ✅ Calculate total deals const total = useMemo(() => deals.length, [deals]); const handleBarClick = (value: { name: string; value: number }) => { @@ -103,31 +88,61 @@ export function DealStageChart({ deals, onOpenTable }: DealStageChartProps) { onOpenTable?.(value.name, filtered); }; + // ✅ Split into normal and 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 ( - -
- - Project Progress by Stage - -

- Click a bar to view related properties -

+
{/* Reduced gap */} + {/* Main Progress Chart */} + +
+ + Project Progress by Stage + +

+ Click a bar to view related properties +

+

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

+
- {/* ✅ Total count */} -

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

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

+ Click to explore exceptions +

+
+ + +
+
); } diff --git a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/Report.tsx b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/Report.tsx index 30b0fb1..0cdb911 100644 --- a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/Report.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/Report.tsx @@ -10,13 +10,12 @@ import { CardTitle, CardContent, } from "@/app/shadcn_components/ui/card"; -import { Home, AlertTriangle, BarChart3 } from "lucide-react"; +import { Home, AlertTriangle } from "lucide-react"; import { motion } from "framer-motion"; interface ReportsProps { deals: Record[]; } - const MAJOR_CONDITION_STAGE_ID = "3061261536"; export default function LiveTracker({ deals }: ReportsProps) { @@ -32,7 +31,10 @@ export default function LiveTracker({ deals }: ReportsProps) { const [openTable, setOpenTable] = useState<{ stage: string; data: any[]; + columns: string[]; + columnLabels: Record; } | null>(null); + const projectCodes = Object.keys(groupedDeals); const [currentProjectCode, setCurrentProjectCode] = useState(projectCodes[0]); const currentDeals = groupedDeals[currentProjectCode]; @@ -43,8 +45,23 @@ export default function LiveTracker({ deals }: ReportsProps) { const majorIssues = majorConditionDeals.length; const majorPercent = ((majorIssues / totalProperties) * 100).toFixed(1); - const handleOpenTable = (stage: string, filteredDeals: any[]) => { - setOpenTable({ stage, data: filteredDeals }); + const handleOpenTable = ( + stage: string, + filteredDeals: any[], + columns?: string[], + columnLabels?: Record + ) => { + setOpenTable({ + stage, + data: filteredDeals, + columns: + columns || ["dealname", "landlordPropertyId"], + columnLabels: + columnLabels || { + dealname: "Address Ref.", + landlordPropertyId: "Property Ref.", + }, + }); }; if (!deals?.length) { @@ -66,18 +83,42 @@ export default function LiveTracker({ deals }: ReportsProps) { icon={Home} title="Total Properties" value={totalProperties} - onClick={() => handleOpenTable("All Properties", deals)} + onClick={() => + handleOpenTable( + "All Properties", + deals, + ["dealname", "landlordPropertyId", "projectCode"], + { + dealname: "Address Ref.", + landlordPropertyId: "Property Ref.", + projectCode: "Project Code", + } + ) + } accent="brandblue" /> {/* Major Issues */} - handleOpenTable("Major Condition Issues", majorConditionDeals) + handleOpenTable( + "Awaab's Law Reporting", + majorConditionDeals, + [ + "dealname", + "landlordPropertyId", + "majorConditionIssueDescription", + ], + { + dealname: "Address Ref.", + landlordPropertyId: "Property Ref.", + majorConditionIssueDescription: "Surveyor's Notes" + } + ) } accent="red" /> @@ -155,18 +196,8 @@ export default function LiveTracker({ deals }: ReportsProps) {
@@ -185,7 +216,7 @@ export default function LiveTracker({ deals }: ReportsProps) { ); } -/** 🔸Small stat card to match DashboardSummary visuals */ +/** 🔸Small stat card component */ function StatCard({ icon: Icon, title,