mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-08 11:37:25 +00:00
save current progress
This commit is contained in:
parent
16c918a095
commit
8ecc159bf3
5 changed files with 130 additions and 77 deletions
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
|
||||
|
||||
/* 🌞 Light Theme (raw HSL values) */
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
|
|
|
|||
|
|
@ -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 }>;
|
||||
|
|
|
|||
|
|
@ -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<string, string> = {
|
|||
"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<string, string> = {
|
|||
"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<string, number> = {};
|
||||
|
||||
// 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 (
|
||||
<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 className="flex flex-col gap-3"> {/* Reduced gap */}
|
||||
{/* Main Progress Chart */}
|
||||
<Card className="bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-200 p-4">
|
||||
<div className="flex flex-col items-center mb-2">
|
||||
<Title className="text-gray-800 text-base font-semibold text-center">
|
||||
Project Progress by Stage
|
||||
</Title>
|
||||
<p className="text-xs text-gray-500 text-center mt-0.5">
|
||||
Click a bar to view related properties
|
||||
</p>
|
||||
<p className="text-xs text-gray-700 font-medium mt-1">
|
||||
Total: {total.toLocaleString()} properties
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* ✅ Total count */}
|
||||
<p className="text-sm text-gray-700 font-medium mt-2">
|
||||
Total: {total.toLocaleString()} properties
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<BarList
|
||||
data={data}
|
||||
data={normalStages}
|
||||
color="blue"
|
||||
sortOrder="none"
|
||||
className="cursor-pointer"
|
||||
onValueChange={handleBarClick}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Card>
|
||||
|
||||
{/* Exception Chart */}
|
||||
<Card className="bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-200 p-4">
|
||||
<div className="flex flex-col items-center mb-2">
|
||||
<Title className="text-gray-800 text-base font-semibold text-center">
|
||||
Needs HA Support & Not Viable
|
||||
</Title>
|
||||
<p className="text-xs text-gray-500 text-center mt-0.5">
|
||||
Click to explore exceptions
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<BarList
|
||||
data={exceptionStages}
|
||||
color="red"
|
||||
sortOrder="none"
|
||||
className="cursor-pointer"
|
||||
onValueChange={handleBarClick}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<string, any>[];
|
||||
}
|
||||
|
||||
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<string, string>;
|
||||
} | 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<string, string>
|
||||
) => {
|
||||
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 */}
|
||||
<StatCard
|
||||
icon={AlertTriangle}
|
||||
title="Major Condition Issues"
|
||||
value={`${majorIssues} `}
|
||||
title="Awaab's Law Reporting"
|
||||
value={`${majorIssues}`}
|
||||
subtitle={`(${majorPercent}%)`}
|
||||
onClick={() =>
|
||||
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) {
|
|||
<div className="flex-1 overflow-auto">
|
||||
<TableViewer
|
||||
data={openTable.data}
|
||||
columns={[
|
||||
"dealname",
|
||||
"landlordPropertyId",
|
||||
"outcome",
|
||||
"outcomeNotes",
|
||||
]}
|
||||
columnLabels={{
|
||||
dealname: "Address Ref.",
|
||||
landlordPropertyId: "Property Ref.",
|
||||
outcome: "Outcome",
|
||||
outcomeNotes: "Notes from Surveyor",
|
||||
}}
|
||||
columns={openTable.columns}
|
||||
columnLabels={openTable.columnLabels}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue