decent homes mvp

This commit is contained in:
Khalim Conn-Kowlessar 2025-09-23 22:03:42 +00:00
parent 66a43a19b8
commit fa7636b340
2 changed files with 86 additions and 55 deletions

View file

@ -140,8 +140,6 @@ export default async function DecentHomesPage({
})
);
console.log("summaryResults", summaryResults);
return (
<div className="container mx-auto px-4">
<DecentHomesDashboard data={summaryResults} portfolioId={portfolioId} />

View file

@ -17,7 +17,14 @@ import {
TabsTrigger,
TabsContent,
} from "@/app/shadcn_components/ui/tabs";
import { Wrench } from "lucide-react";
import {
Wrench,
AlertTriangle,
Clock,
Hourglass,
CheckCircle,
} from "lucide-react";
const DISPLAY_NAMES: Record<string, string> = {
// Criterion A - HHSRS hazards
@ -175,7 +182,7 @@ function StatusBadge({ status }: { status: string }) {
// urgency badge
function UrgencyBadge({ label }: { label: string }) {
const colorMap: Record<string, string> = {
Overdue: "bg-red-600",
Overdue: "bg-red-700",
"06 months": "bg-orange-500",
"612 months": "bg-yellow-500",
">12 months": "bg-green-600",
@ -205,7 +212,7 @@ function CriterionContent({
return (
<Card className="h-96 flex flex-col relative overflow-hidden">
<CardHeader>
<CardTitle>{title}</CardTitle>
<CardTitle className="text-brandbrown">{title}</CardTitle>
</CardHeader>
<CardContent className="flex-1 overflow-y-scroll pr-2 scrollbar-thin scrollbar-thumb-gray-400 scrollbar-track-gray-100">
<ul className="space-y-2 pb-6">
@ -235,7 +242,12 @@ function ReplacementsContent({
const today = new Date();
const groups: Record<
string,
{ sub_variable: string; expiry: Date; remaining: string }[]
{
sub_variable: string;
expiry: Date;
remaining: string;
overdue: boolean;
}[]
> = {
Overdue: [],
"06 months": [],
@ -248,46 +260,37 @@ function ReplacementsContent({
const expiry = new Date(item.expiry_date);
const diffMs = expiry.getTime() - today.getTime();
const diffMonths = diffMs / (1000 * 60 * 60 * 24 * 30);
const years = Math.floor(diffMonths / 12);
const months = Math.floor(diffMonths % 12);
const remaining =
diffMs < 0
? "Expired"
: `${years > 0 ? `${years}y ` : ""}${months}m remaining`;
const years = Math.floor(Math.abs(diffMonths) / 12);
const months = Math.floor(Math.abs(diffMonths) % 12);
let remaining = "";
let overdue = false;
if (diffMs < 0) {
groups.Overdue.push({
sub_variable: SUB_ITEMS_TEXT[item.sub_variable],
expiry,
remaining,
});
} else if (diffMonths <= 6) {
groups["06 months"].push({
sub_variable: SUB_ITEMS_TEXT[item.sub_variable],
expiry,
remaining,
});
} else if (diffMonths <= 12) {
groups["612 months"].push({
sub_variable: SUB_ITEMS_TEXT[item.sub_variable],
expiry,
remaining,
});
overdue = true;
remaining = `Expired ${years > 0 ? `${years}y ` : ""}${months}m ago`;
} else {
groups[">12 months"].push({
sub_variable: SUB_ITEMS_TEXT[item.sub_variable],
expiry,
remaining,
});
remaining = `${years > 0 ? `${years}y ` : ""}${months}m remaining`;
}
const entry = {
sub_variable: SUB_ITEMS_TEXT[item.sub_variable] ?? item.sub_variable,
expiry,
remaining,
overdue,
};
if (diffMs < 0) groups.Overdue.push(entry);
else if (diffMonths <= 6) groups["06 months"].push(entry);
else if (diffMonths <= 12) groups["612 months"].push(entry);
else groups[">12 months"].push(entry);
});
// sort each group by expiry date ascending (soonest first)
Object.values(groups).forEach((comps) => {
comps.sort((a, b) => a.expiry.getTime() - b.expiry.getTime());
});
// sort within each group
Object.values(groups).forEach((comps) =>
comps.sort((a, b) => a.expiry.getTime() - b.expiry.getTime())
);
// order of groups
const groupOrder: (keyof typeof groups)[] = [
"Overdue",
"06 months",
@ -295,36 +298,66 @@ function ReplacementsContent({
">12 months",
];
// urgency → card highlight color + icon
const cardStyles: Record<string, { border: string; icon: JSX.Element }> = {
Overdue: {
border: "border-l-4 border-red-600",
icon: <AlertTriangle className="w-4 h-4 text-red-600" />,
},
"06 months": {
border: "border-l-4 border-orange-500",
icon: <Clock className="w-4 h-4 text-orange-500" />,
},
"612 months": {
border: "border-l-4 border-yellow-500",
icon: <Hourglass className="w-4 h-4 text-yellow-500" />,
},
">12 months": {
border: "border-l-4 border-green-600",
icon: <CheckCircle className="w-4 h-4 text-green-600" />,
},
};
return (
<Card className="h-96 flex flex-col relative overflow-hidden">
<Card className="h-[32rem] flex flex-col relative overflow-hidden">
<CardHeader>
<CardTitle>Upcoming Replacements</CardTitle>
<CardTitle className="text-lg font-medium text-brandbrown">
Upcoming Replacements
</CardTitle>
</CardHeader>
<CardContent className="flex-1 overflow-y-scroll pr-2 scrollbar-thin scrollbar-thumb-gray-400 scrollbar-track-gray-100">
{groupOrder.map((urgency) =>
groups[urgency].length > 0 ? (
<div key={urgency} className="mb-4">
<div className="flex items-center space-x-2 mb-2">
<div key={urgency} className="mb-6">
{/* group header */}
<div className="flex items-center space-x-2 mb-3">
<UrgencyBadge label={urgency} />
<span className="text-gray-700 font-medium">
{groups[urgency].length} items
{groups[urgency].length}{" "}
{groups[urgency].length > 1 ? "items" : "item"}
</span>
</div>
<ul className="space-y-1 ml-2">
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{groups[urgency].map((comp, idx) => (
<li
<div
key={idx}
className="flex justify-between border-b pb-1 last:border-0"
className={`px-4 py-2 bg-gray-50 rounded-md border hover:bg-gray-100 transition ${cardStyles[urgency].border}`}
>
<span>
{DISPLAY_NAMES[comp.sub_variable] ?? comp.sub_variable}
</span>
<span className="text-sm text-gray-600">
{comp.remaining}
</span>
</li>
<div className="flex items-center justify-between mb-2">
<span className="font-medium text-gray-800 flex items-center gap-2">
{cardStyles[urgency].icon}
{comp.sub_variable}
</span>
</div>
<div className="text-sm text-gray-600 flex justify-between">
<span>{comp.remaining}</span>
<span className={comp.overdue ? "text-red-600" : ""}>
{comp.expiry.toLocaleDateString("en-GB")}
</span>
</div>
</div>
))}
</ul>
</div>
</div>
) : null
)}