mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-08 11:37:25 +00:00
added the surveyor button changes
This commit is contained in:
parent
0003102ca1
commit
b8caeca263
8 changed files with 155 additions and 193 deletions
|
|
@ -184,8 +184,8 @@ export function Toolbar({
|
|||
<BookingSuccessToast
|
||||
show={showToast}
|
||||
onClose={() => 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. 🎉"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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<string, string> = {
|
||||
|
|
@ -36,37 +35,18 @@ const STAGE_LABELS: Record<string, string> = {
|
|||
"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 (
|
||||
<Card className="max-w-lg mx-auto">
|
||||
<Title>Project Progress</Title>
|
||||
<BarList
|
||||
data={data}
|
||||
color="blue"
|
||||
sortOrder="none"
|
||||
onValueChange={handleBarClick}
|
||||
/>
|
||||
<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 mb-4">
|
||||
<Title className="text-gray-800 text-lg font-semibold tracking-tight text-center">
|
||||
Project Progress by Stage
|
||||
</Title>
|
||||
<p className="text-sm text-gray-500 text-center mt-1">
|
||||
Click a bar to view related properties
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<BarList
|
||||
data={data}
|
||||
color="blue"
|
||||
sortOrder="none"
|
||||
className="cursor-pointer"
|
||||
onValueChange={handleBarClick}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,32 +9,26 @@ interface ReportsProps {
|
|||
deals: Record<string, any>[];
|
||||
}
|
||||
|
||||
// 🟩 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 (
|
||||
<div className="p-6 text-center text-gray-500 border rounded-xl shadow-sm">
|
||||
<div className="p-6 text-center text-gray-500 border rounded-2xl shadow-sm bg-white">
|
||||
No deal data available.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 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<string, any[]>);
|
||||
|
||||
|
|
@ -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 (
|
||||
<div className="p-6 space-y-8">
|
||||
{/* 🔹 Global Overview Row */}
|
||||
<div className="border rounded-xl bg-gray-50 shadow-sm p-6 space-y-4">
|
||||
<h2 className="text-lg font-semibold text-gray-700 text-center uppercase tracking-wide">
|
||||
<div className="p-6 space-y-10">
|
||||
{/* 🌍 Global Portfolio Overview */}
|
||||
<div className="border rounded-2xl bg-gradient-to-br from-gray-50 to-white shadow-lg p-8 space-y-6">
|
||||
<h2 className="text-center text-xl font-semibold text-gray-800 tracking-tight">
|
||||
🌍 Global Portfolio Overview
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 items-center text-center">
|
||||
{/* Total Properties */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 text-center">
|
||||
{/* Total */}
|
||||
<button
|
||||
onClick={handleTotalClick}
|
||||
className="group transition rounded-lg border border-transparent hover:border-blue-500 hover:bg-blue-50 p-4 cursor-pointer"
|
||||
onClick={() => handleOpenTable("All Properties", deals)}
|
||||
className="group transition rounded-xl border bg-white shadow-sm hover:shadow-md hover:border-blue-400 p-5"
|
||||
>
|
||||
<p className="text-sm text-gray-500 uppercase tracking-wide">
|
||||
Total Properties
|
||||
</p>
|
||||
<p className="text-2xl font-semibold text-gray-800 group-hover:text-blue-600">
|
||||
<p className="text-sm text-gray-500 uppercase tracking-wide">Total Properties</p>
|
||||
<p className="text-3xl font-bold text-gray-800 group-hover:text-blue-600">
|
||||
{totalProperties}
|
||||
</p>
|
||||
</button>
|
||||
|
||||
{/* Major Condition Issues */}
|
||||
{/* Major Issues */}
|
||||
<button
|
||||
onClick={handleMajorClick}
|
||||
className="group transition rounded-lg border border-transparent hover:border-red-500 hover:bg-red-50 p-4 cursor-pointer"
|
||||
onClick={() => handleOpenTable("Major Condition Issues", majorConditionDeals)}
|
||||
className="group transition rounded-xl border bg-white shadow-sm hover:shadow-md hover:border-red-400 p-5"
|
||||
>
|
||||
<p className="text-sm text-gray-500 uppercase tracking-wide">
|
||||
Major Condition Issues
|
||||
</p>
|
||||
<p className="text-2xl font-semibold text-red-600 group-hover:text-red-700">
|
||||
<p className="text-sm text-gray-500 uppercase tracking-wide">Major Condition Issues</p>
|
||||
<p className="text-3xl font-bold text-red-600 group-hover:text-red-700">
|
||||
{majorIssues}{" "}
|
||||
<span className="text-gray-500 text-base font-normal">
|
||||
<span className="text-gray-500 text-base font-medium">
|
||||
({majorPercent}%)
|
||||
</span>
|
||||
</p>
|
||||
</button>
|
||||
|
||||
{/* Project Dropdown Selector */}
|
||||
<div>
|
||||
{/* Project Selector */}
|
||||
<div className="flex flex-col justify-center items-center">
|
||||
<label
|
||||
htmlFor="projectSelect"
|
||||
className="block text-sm font-medium text-gray-700 mb-1"
|
||||
className="block text-sm font-medium text-gray-700 mb-2"
|
||||
>
|
||||
Select Project
|
||||
</label>
|
||||
<div className="relative max-w-xs mx-auto">
|
||||
<div className="relative w-60">
|
||||
<select
|
||||
id="projectSelect"
|
||||
value={currentProjectCode}
|
||||
onChange={(e) => setCurrentProjectCode(e.target.value)}
|
||||
className="w-full appearance-none px-4 py-2 pr-10 border border-gray-300 rounded-lg bg-white text-gray-800 shadow-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||
className="w-full appearance-none px-4 py-2 pr-10 border rounded-lg shadow-sm bg-white text-gray-800 focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||
>
|
||||
{projectCodes.map((code) => (
|
||||
<option key={code} value={code}>
|
||||
|
|
@ -120,61 +96,43 @@ export default function LiveTracker({ deals }: ReportsProps) {
|
|||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
{/* Custom dropdown arrow */}
|
||||
<div className="pointer-events-none absolute inset-y-0 right-3 flex items-center text-gray-500">
|
||||
▼
|
||||
</div>
|
||||
<div className="absolute right-3 top-2.5 text-gray-500 pointer-events-none">▼</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 🔹 Project-Level Section */}
|
||||
<div className="border rounded-xl bg-gray-50 shadow-sm p-6 space-y-4">
|
||||
<h2 className="text-lg font-semibold text-gray-700 text-center uppercase tracking-wide">
|
||||
{/* 📊 Project Insights */}
|
||||
<div className="border rounded-2xl bg-gradient-to-br from-gray-50 to-white shadow-lg p-8 space-y-6">
|
||||
<h2 className="text-center text-xl font-semibold text-gray-800 tracking-tight">
|
||||
📊 Project-Level Insights
|
||||
</h2>
|
||||
<p className="text-center text-gray-500 text-sm -mt-2">
|
||||
Showing data for{" "}
|
||||
<span className="font-medium text-gray-700">
|
||||
{currentProjectCode}
|
||||
</span>
|
||||
<p className="text-center text-gray-500 text-sm">
|
||||
Showing data for <span className="font-medium text-gray-700">{currentProjectCode}</span>
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="border rounded-xl p-4 shadow-sm bg-white">
|
||||
<DealStageChart
|
||||
deals={currentDeals}
|
||||
onOpenTable={handleOpenTable}
|
||||
/>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div className="border rounded-xl p-5 shadow-md bg-white hover:shadow-lg transition">
|
||||
<DealStageChart deals={currentDeals} onOpenTable={handleOpenTable} />
|
||||
</div>
|
||||
<div className="border rounded-xl p-4 shadow-sm bg-white">
|
||||
<SurveyedPieChart
|
||||
deals={currentDeals}
|
||||
onOpenTable={handleOpenTable}
|
||||
/>
|
||||
<div className="border rounded-xl p-5 shadow-md bg-white hover:shadow-lg transition">
|
||||
<SurveyedPieChart deals={currentDeals} onOpenTable={handleOpenTable} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 🔹 Modal Table */}
|
||||
{/* 🔹 Modal */}
|
||||
{openTable && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
|
||||
<div className="bg-white rounded-xl shadow-2xl p-6 w-full max-w-5xl h-[90vh] flex flex-col">
|
||||
<h2 className="text-2xl font-semibold mb-4 text-center">
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm transition-opacity">
|
||||
<div className="bg-white rounded-2xl shadow-2xl p-6 w-full max-w-6xl h-[90vh] flex flex-col animate-fadeIn">
|
||||
<h2 className="text-2xl font-semibold mb-4 text-center text-gray-800">
|
||||
{openTable.stage} — {openTable.data.length} Properties
|
||||
</h2>
|
||||
|
||||
<div className="flex-1 overflow-auto">
|
||||
<TableViewer
|
||||
data={openTable.data}
|
||||
columns={[
|
||||
"dealname",
|
||||
"landlordPropertyId",
|
||||
"outcome",
|
||||
"outcomeNotes",
|
||||
]}
|
||||
columns={["dealname", "landlordPropertyId", "outcome", "outcomeNotes"]}
|
||||
columnLabels={{
|
||||
dealname: "Address Ref.",
|
||||
landlordPropertyId: "Property Ref.",
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
"use client";
|
||||
|
||||
import { DonutChart, Card, Title } from "@tremor/react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useMemo } from "react";
|
||||
|
||||
interface SurveyedPieChartProps {
|
||||
deals: Record<string, any>[];
|
||||
onOpenTable?: (outcome: string, filteredDeals: Record<string, any>[]) => void;
|
||||
}
|
||||
|
||||
export default function SurveyedPieChart({
|
||||
deals,
|
||||
onOpenTable,
|
||||
}: SurveyedPieChartProps) {
|
||||
const [selected, setSelected] = useState<string | null>(null);
|
||||
|
||||
const surveyorOutcomes = [
|
||||
"Surveyed",
|
||||
"Surveyed - Pending Upload",
|
||||
|
|
@ -27,14 +26,12 @@ export default function SurveyedPieChart({
|
|||
|
||||
const data = useMemo(() => {
|
||||
const outcomeCounts: Record<string, number> = {};
|
||||
|
||||
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 (
|
||||
<Card className="max-w-lg mx-auto">
|
||||
<div className="flex flex-col items-center space-y-6">
|
||||
<Title className="text-center text-lg font-semibold">
|
||||
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
|
||||
</Title>
|
||||
<p className="text-sm text-gray-500 text-center -mt-2">
|
||||
Click a segment to view filtered properties
|
||||
</p>
|
||||
|
||||
<DonutChart
|
||||
data={data}
|
||||
|
|
@ -61,17 +60,17 @@ export default function SurveyedPieChart({
|
|||
index="name"
|
||||
valueFormatter={(n) => `${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}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,13 +4,12 @@ import { useState, useMemo } from "react";
|
|||
|
||||
interface TableViewerProps {
|
||||
data: Record<string, any>[];
|
||||
columns?: string[]; // optional: which columns to show
|
||||
columnLabels?: Record<string, string>; // 👈 map data keys to display names
|
||||
columns?: string[];
|
||||
columnLabels?: Record<string, string>;
|
||||
}
|
||||
|
||||
export default function TableViewer({ data, columns, columnLabels }: TableViewerProps) {
|
||||
const [searchTerms, setSearchTerms] = useState<Record<string, string>>({});
|
||||
|
||||
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 (
|
||||
<div className="overflow-x-auto border rounded-xl shadow-md p-4">
|
||||
<div className="overflow-x-auto border rounded-xl shadow-lg bg-white">
|
||||
<table className="min-w-full text-sm border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<thead className="bg-gray-100 sticky top-0">
|
||||
<tr>
|
||||
{visibleColumns.map((col) => (
|
||||
<th key={col} className="border p-2 text-left">
|
||||
<div className="flex flex-col">
|
||||
<span className="font-semibold capitalize">
|
||||
{columnLabels?.[col] || col}
|
||||
</span>
|
||||
<th key={col} className="border-b p-3 text-left text-gray-700 font-semibold">
|
||||
<div className="flex flex-col gap-1">
|
||||
<span>{columnLabels?.[col] || col}</span>
|
||||
<input
|
||||
type="text"
|
||||
placeholder={`Search ${columnLabels?.[col] || col}`}
|
||||
className="mt-1 p-1 border rounded text-xs"
|
||||
placeholder="Search..."
|
||||
className="p-1 border border-gray-300 rounded text-xs focus:ring-1 focus:ring-blue-400 outline-none"
|
||||
onChange={(e) =>
|
||||
setSearchTerms((prev) => ({
|
||||
...prev,
|
||||
[col]: e.target.value,
|
||||
}))
|
||||
setSearchTerms((prev) => ({ ...prev, [col]: e.target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -54,18 +48,18 @@ export default function TableViewer({ data, columns, columnLabels }: TableViewer
|
|||
<tbody>
|
||||
{filteredData.length === 0 ? (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={visibleColumns.length}
|
||||
className="text-center p-4 text-gray-400"
|
||||
>
|
||||
<td colSpan={visibleColumns.length} className="text-center py-6 text-gray-400">
|
||||
No results found
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
filteredData.map((row, i) => (
|
||||
<tr key={i} className="odd:bg-white even:bg-gray-50">
|
||||
<tr
|
||||
key={i}
|
||||
className="odd:bg-white even:bg-gray-50 hover:bg-blue-50 transition"
|
||||
>
|
||||
{visibleColumns.map((col) => (
|
||||
<td key={col} className="border p-2">
|
||||
<td key={col} className="border-b p-3 text-gray-700">
|
||||
{String(row[col] ?? "")}
|
||||
</td>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="text-center text-gray-500 mt-8">
|
||||
No information to show.
|
||||
</div>
|
||||
<main className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#14163d] via-[#2d348f] to-[#3943b7] text-white">
|
||||
<div className="text-center bg-white/10 backdrop-blur-md text-gray-200 p-8 rounded-2xl shadow-2xl border border-white/10">
|
||||
No information to show.
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
// 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 (
|
||||
<div className="text-center text-gray-500 mt-8">
|
||||
No information to show.
|
||||
</div>
|
||||
<main className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#14163d] via-[#2d348f] to-[#3943b7] text-white">
|
||||
<div className="text-center bg-white/10 backdrop-blur-md text-gray-200 p-8 rounded-2xl shadow-2xl border border-white/10">
|
||||
No information to show.
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
return <LiveTracker deals={deals} />;
|
||||
return (
|
||||
<main className="relative min-h-screen overflow-hidden">
|
||||
{/* 🌊 Domna-inspired layered background */}
|
||||
<div className="absolute inset-0 -z-10 bg-[linear-gradient(135deg,#14163d_0%,#2d348f_45%,#3943b7_70%,#eff6fc_100%)]" />
|
||||
|
||||
{/* ✨ Subtle translucent grid texture */}
|
||||
<div className="absolute inset-0 -z-0 opacity-10 bg-[radial-gradient(circle_at_top_left,rgba(255,255,255,0.7)_1px,transparent_0)] bg-[length:40px_40px]" />
|
||||
|
||||
{/* 💡 Optional soft light glow at top */}
|
||||
<div className="absolute top-0 inset-x-0 h-[40vh] bg-gradient-to-b from-white/20 via-transparent to-transparent opacity-30" />
|
||||
|
||||
{/* Main content */}
|
||||
<div className="relative z-10 p-6 md:p-10">
|
||||
<div className="max-w-7xl mx-auto animate-fadeIn">
|
||||
<LiveTracker deals={deals} />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Confirm Booking a Survey</DialogTitle>
|
||||
<DialogHeader className="text-center">
|
||||
<DialogTitle className="text-center">
|
||||
Confirm and we’ll be in touch!
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
|
|
@ -79,14 +81,13 @@ export default function BookSurveyModal({
|
|||
className="w-full"
|
||||
disabled={bookSurveyMutation.isPending}
|
||||
>
|
||||
{bookSurveyMutation.isPending ? "Creating..." : "Submit"}
|
||||
{bookSurveyMutation.isPending ? "Creating..." : "Confirm"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue