diff --git a/src/app/components/building-passport/Toolbar.tsx b/src/app/components/building-passport/Toolbar.tsx index 966f283..48f7d2e 100644 --- a/src/app/components/building-passport/Toolbar.tsx +++ b/src/app/components/building-passport/Toolbar.tsx @@ -184,8 +184,8 @@ export function Toolbar({ setShowToast(false)} - message="Survey Booked Successfully!" - subtext="Your Survey Request is with Domna and we will be in contact. πŸŽ‰" + message="Survey Request Recieved!" + subtext="We'll be in contact soon. πŸŽ‰" /> ); diff --git a/src/app/portfolio/[slug]/(portfolio)/live-projects/DealStageChart.tsx b/src/app/portfolio/[slug]/(portfolio)/live-projects/DealStageChart.tsx index 4a6bd73..b4dec63 100644 --- a/src/app/portfolio/[slug]/(portfolio)/live-projects/DealStageChart.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/live-projects/DealStageChart.tsx @@ -1,17 +1,16 @@ "use client"; -import { useState, useMemo } from "react"; +import { useMemo } from "react"; import { BarList, Card, Title } from "@tremor/react"; -import TableViewer from "./TableViewer"; const STAGE_ORDER = [ - "Initial Planning", // 0 - "Booking Team to contact Tenant", // 1 - "Survey in Progress", // 2 - "Not viable", // 3 - "Needs HA Support", // 4 - "Coordination + Design", // 5 - "Ready to be installed" //7 + "Initial Planning", + "Booking Team to contact Tenant", + "Survey in Progress", + "Not viable", + "Needs HA Support", + "Coordination + Design", + "Ready to be installed", ]; const STAGE_LABELS: Record = { @@ -36,37 +35,18 @@ const STAGE_LABELS: Record = { "3061261536": STAGE_ORDER[4], "2571417798": "[Ops] Surveyed under 2019 - Needs Re-survey", "1617223914": STAGE_ORDER[5], - "1887736000": "[Deprecated, please don't use] Files Missing From Assessor", + "1887736000": "[Deprecated] Files Missing From Assessor", "1617223916": "[Ops] Properties to Review Manually", "2628341989": STAGE_ORDER[2], - "3441170637": STAGE_ORDER[2], // check if assessor or coordination + "3441170637": STAGE_ORDER[2], "2628233422": STAGE_ORDER[5], "1887735999": STAGE_ORDER[4], - "1960060104": "[Ops] HA Informed", - "1960060105": "[Ops] HA Works Scheduled", - "1960060106": "[Ops] HA Works Complete", - "1668803772": "[Ops] ERF Delivered to HA", - "1668803773": "[Ops] ERF Signed", - "2769407183": "[Ops] PV - Needs Heating Upgrade (Pre EPR D)", - "2769407184": "[Ops] Talk to client, Needs Heating Upgrade (Pre EPR C)", "2702650617": STAGE_ORDER[5], "2473886962": STAGE_ORDER[5], "3016601828": STAGE_ORDER[4], - "3389868276": "[Engagement Team] Blocked - Needs Completion of Pilot", - "3389880508": "[Engagement Team] Blocked - Installer Negotiation", - "3399016689": "[Engagement Team] Eligible but blocked - part of incomplete flat", - "1668803774": STAGE_ORDER[6], // Ready for Invoicing - "3440363736": STAGE_ORDER[6], // [Finance] Needs Invoicing - Files Sent - "1618526429": "[Ops] Invoiced - Send Files to Installer", - "3080225005": "[Ops] Files Sent to Installer", - "1961258215": "[Ops] Installer Cancelled - Finalized", - "1961258214": "[Ops] Installer Cancelled - In Progress", - "1961258213": "[Ops] Install Scheduled", - "1617223918": "[Ops] Install Complete", - "1961258216": "[Compliance] Lodgement Complete", - "1961258217": "[Compliance] Documentation Sent to HA", - "3027432668": "[Team ???] Submitted to " -} + "1668803774": STAGE_ORDER[6], + "3440363736": STAGE_ORDER[6], +}; interface DealStageChartProps { deals: any[]; @@ -97,26 +77,35 @@ export function DealStageChart({ deals, onOpenTable }: DealStageChartProps) { }); }, [deals]); - // handle click event 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); }; return ( - - Project Progress - + +
+ + Project Progress by Stage + +

+ Click a bar to view related properties +

+
+ +
+ +
); -} \ No newline at end of file +} diff --git a/src/app/portfolio/[slug]/(portfolio)/live-projects/Report.tsx b/src/app/portfolio/[slug]/(portfolio)/live-projects/Report.tsx index 91bad8b..09c2999 100644 --- a/src/app/portfolio/[slug]/(portfolio)/live-projects/Report.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/live-projects/Report.tsx @@ -9,32 +9,26 @@ interface ReportsProps { deals: Record[]; } -// 🟩 Stage mapping: β€œMajor Condition Issues” = dealstage 3061261536 const MAJOR_CONDITION_STAGE_ID = "3061261536"; export default function LiveTracker({ deals }: ReportsProps) { - const [openTable, setOpenTable] = useState<{ - stage: string; - data: any[]; - } | null>(null); + const [openTable, setOpenTable] = useState<{ stage: string; data: any[] } | null>(null); const handleOpenTable = (stage: string, filteredDeals: any[]) => { setOpenTable({ stage, data: filteredDeals }); }; - if (!deals || deals.length === 0) { + if (!deals?.length) { return ( -
+
No deal data available.
); } - // Group deals by projectCode const groupedDeals = deals.reduce((acc, deal) => { const project = deal.projectCode || "Unknown Project"; - if (!acc[project]) acc[project] = []; - acc[project].push(deal); + (acc[project] ||= []).push(deal); return acc; }, {} as Record); @@ -42,77 +36,59 @@ export default function LiveTracker({ deals }: ReportsProps) { const [currentProjectCode, setCurrentProjectCode] = useState(projectCodes[0]); const currentDeals = groupedDeals[currentProjectCode]; - // πŸ”Ή Compute overall summary (across all projects) const totalProperties = deals.length; - const majorConditionDeals = deals.filter( - (d) => d.dealstage === MAJOR_CONDITION_STAGE_ID - ); + const majorConditionDeals = deals.filter(d => d.dealstage === MAJOR_CONDITION_STAGE_ID); const majorIssues = majorConditionDeals.length; const majorPercent = ((majorIssues / totalProperties) * 100).toFixed(1); - // πŸ”Ή Click handlers - const handleTotalClick = () => { - console.log("Opening all deals (global)"); - handleOpenTable("All Properties", deals); - }; - - const handleMajorClick = () => { - console.log("Opening all Major Condition Issues (global)"); - handleOpenTable("Major Condition Issues", majorConditionDeals); - }; - return ( -
- {/* πŸ”Ή Global Overview Row */} -
-

+
+ {/* 🌍 Global Portfolio Overview */} +
+

🌍 Global Portfolio Overview

-
- {/* Total Properties */} +
+ {/* Total */} - {/* Major Condition Issues */} + {/* Major Issues */} - {/* Project Dropdown Selector */} -
+ {/* Project Selector */} +
-
+
- - {/* Custom dropdown arrow */} -
- β–Ό -
+
β–Ό
- {/* πŸ”Ή Project-Level Section */} -
-

+ {/* πŸ“Š Project Insights */} +
+

πŸ“Š Project-Level Insights

-

- Showing data for{" "} - - {currentProjectCode} - +

+ Showing data for {currentProjectCode}

-
-
- +
+
+
-
- +
+
- {/* πŸ”Ή Modal Table */} + {/* πŸ”Ή Modal */} {openTable && ( -
-
-

+
+
+

{openTable.stage} β€” {openTable.data.length} Properties

[]; onOpenTable?: (outcome: string, filteredDeals: Record[]) => void; } + export default function SurveyedPieChart({ deals, onOpenTable, }: SurveyedPieChartProps) { - const [selected, setSelected] = useState(null); - const surveyorOutcomes = [ "Surveyed", "Surveyed - Pending Upload", @@ -27,14 +26,12 @@ export default function SurveyedPieChart({ const data = useMemo(() => { const outcomeCounts: Record = {}; - deals.forEach((deal) => { const outcome = deal.outcome; if (outcome && surveyorOutcomes.includes(outcome)) { outcomeCounts[outcome] = (outcomeCounts[outcome] || 0) + 1; } }); - return Object.entries(outcomeCounts).map(([name, amount]) => ({ name, amount, @@ -42,18 +39,20 @@ export default function SurveyedPieChart({ }, [deals]); const handleClick = (value: { name: string; amount: number }) => { - if (!value) return; // guard clause + if (!value) return; const filteredDeals = deals.filter((d) => d.outcome === value.name); - setSelected(null); // remove highlight after click onOpenTable?.(value.name, filteredDeals); }; return ( - -
- - Surveyed Outcome + <Card className="max-w-lg mx-auto bg-white rounded-2xl shadow-md hover:shadow-lg transition-all duration-200 p-6"> + <div className="flex flex-col items-center space-y-4"> + <Title className="text-gray-800 text-lg font-semibold tracking-tight text-center"> + Survey Outcomes +

+ Click a segment to view filtered properties +

`${n.toLocaleString()}`} colors={[ - "sky", // light airy blue - "cyan", // bright modern blue - "blue", // brand-level core blue - "indigo", // deeper professional tone - "violet", // slight bluish-purple contrast - "slate", // muted cool gray-blue - "lightBlue", // soft complementary blue - "navy", // deep accent - "azure", // fresh pop for clarity + "sky", + "cyan", + "blue", + "indigo", + "violet", + "slate", + "lightBlue", + "navy", + "azure", ]} - className="w-64 h-64 cursor-pointer" + className="w-64 h-64 cursor-pointer transition-transform hover:scale-[1.03]" onValueChange={handleClick} />
diff --git a/src/app/portfolio/[slug]/(portfolio)/live-projects/TableViewer.tsx b/src/app/portfolio/[slug]/(portfolio)/live-projects/TableViewer.tsx index 0488332..c35a586 100644 --- a/src/app/portfolio/[slug]/(portfolio)/live-projects/TableViewer.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/live-projects/TableViewer.tsx @@ -4,13 +4,12 @@ import { useState, useMemo } from "react"; interface TableViewerProps { data: Record[]; - columns?: string[]; // optional: which columns to show - columnLabels?: Record; // πŸ‘ˆ map data keys to display names + columns?: string[]; + columnLabels?: Record; } export default function TableViewer({ data, columns, columnLabels }: TableViewerProps) { const [searchTerms, setSearchTerms] = useState>({}); - const visibleColumns = columns?.length ? columns : Object.keys(data?.[0] || {}); const filteredData = useMemo(() => { @@ -25,25 +24,20 @@ export default function TableViewer({ data, columns, columnLabels }: TableViewer }, [data, searchTerms, visibleColumns]); return ( -
+
- - + + {visibleColumns.map((col) => ( - {filteredData.length === 0 ? ( - ) : ( filteredData.map((row, i) => ( - + {visibleColumns.map((col) => ( - ))} diff --git a/src/app/portfolio/[slug]/(portfolio)/live-projects/page.tsx b/src/app/portfolio/[slug]/(portfolio)/live-projects/page.tsx index 7eb6ded..cc135b1 100644 --- a/src/app/portfolio/[slug]/(portfolio)/live-projects/page.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/live-projects/page.tsx @@ -19,36 +19,55 @@ export default async function Demo(props: { const { slug: portfolioId } = await props.params; - // Fetch the single company + // 🏒 Fetch the company const [company] = await surveyDB .select() .from(hubspotCompanyData) .where(eq(hubspotCompanyData.groupId, portfolioId)); if (!company) { - console.log("No company found for this portfolioId"); return ( -
- No information to show. -
+
+
+ No information to show. +
+
); } - // Fetch deals related to that company + // πŸ’Ό Fetch deals for that company const deals = await surveyDB .select() .from(hubspotDealData) .where(eq(hubspotDealData.companyId, company.companyId)); - console.log("Deals:", deals); - if (!deals || deals.length === 0) { return ( -
- No information to show. -
+
+
+ No information to show. +
+
); } - return ; + return ( +
+ {/* 🌊 Domna-inspired layered background */} +
+ + {/* ✨ Subtle translucent grid texture */} +
+ + {/* πŸ’‘ Optional soft light glow at top */} +
+ + {/* Main content */} +
+
+ +
+
+
+ ); } diff --git a/src/app/portfolio/[slug]/components/BookSurveyModal.tsx b/src/app/portfolio/[slug]/components/BookSurveyModal.tsx index 39f6651..a3a7b52 100644 --- a/src/app/portfolio/[slug]/components/BookSurveyModal.tsx +++ b/src/app/portfolio/[slug]/components/BookSurveyModal.tsx @@ -39,7 +39,7 @@ export default function BookSurveyModal({ body: JSON.stringify({ dealName: address, pipelineId: "2400089278", - dealStageId: "3288115388", + dealStageId: "3660660975", propertyId: propertyId.toString(), portfolioId: portfolioId, }), @@ -68,8 +68,10 @@ export default function BookSurveyModal({ return ( - - Confirm Booking a Survey + + + Confirm and we’ll be in touch! +
@@ -79,14 +81,13 @@ export default function BookSurveyModal({ className="w-full" disabled={bookSurveyMutation.isPending} > - {bookSurveyMutation.isPending ? "Creating..." : "Submit"} + {bookSurveyMutation.isPending ? "Creating..." : "Confirm"}
- - ); + ); } diff --git a/tailwind.config.js b/tailwind.config.js index 30f1628..fdc4b23 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -26,7 +26,9 @@ module.exports = { "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", "gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", - }, + "domna-gradient": + "linear-gradient(135deg, #14163d 0%, #2d348f 45%, #3943b7 70%, #eff6fc 100%)", + }, colors: { tremor: { brand: {
-
- - {columnLabels?.[col] || col} - +
+
+ {columnLabels?.[col] || col} - setSearchTerms((prev) => ({ - ...prev, - [col]: e.target.value, - })) + setSearchTerms((prev) => ({ ...prev, [col]: e.target.value })) } />
@@ -54,18 +48,18 @@ export default function TableViewer({ data, columns, columnLabels }: TableViewer
+ No results found
+ {String(row[col] ?? "")}