diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/condition/components/RenewalsTimeline.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/condition/components/RenewalsTimeline.tsx
index e69de29b..9a9e1c28 100644
--- a/src/app/portfolio/[slug]/building-passport/[propertyId]/condition/components/RenewalsTimeline.tsx
+++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/condition/components/RenewalsTimeline.tsx
@@ -0,0 +1,117 @@
+"use client";
+
+import React from "react";
+import {
+ ScatterChart,
+ Scatter,
+ XAxis,
+ YAxis,
+ CartesianGrid,
+ Tooltip,
+ ResponsiveContainer,
+ ReferenceLine,
+ Label,
+} from "recharts";
+import { Renewal } from "../viewModels/renewal";
+
+type RenewalsTimelineProps = {
+ renewals: Renewal[];
+};
+
+export default function RenewalsTimeline({ renewals }: RenewalsTimelineProps) {
+ const today = new Date().getFullYear();
+
+ const elementTypes = Array.from(
+ new Set(renewals.map(r => (r.elementType ?? "").trim()))
+ );
+ const elementTypeToY = Object.fromEntries(elementTypes.map((type, i) => [type, i]));
+
+ const data = renewals.map(r => {
+ let color = "#10b981";
+ if (r.renewalYear < today) color = "#ef4444";
+ else if (r.renewalYear - today <= 3) color = "#f59e0b";
+
+ return {
+ id: r.id,
+ elementType: r.elementType,
+ x: r.renewalYear,
+ y: elementTypeToY[r.elementType ?? ""],
+ comments: r.comments ?? "",
+ color,
+ };
+ });
+
+ // 3️⃣ Click handler
+ const handleClick = (payload: any) => {
+ if (!payload) return;
+ alert(`Element Type: ${payload.elementType}\nYear: ${payload.x}\nComments: ${payload.comments}`);
+ };
+
+ return (
+
+
+
+
+ tick.toString()}
+ />
+ i)}
+ tickFormatter={index => elementTypes[index]}
+ interval={0} // force all element labels to render
+ />
+ {
+ if (active && payload && payload.length) {
+ const data = payload[0].payload;
+ return (
+
+
{data.elementType}
+
Renewal year: {data.x}
+
+ );
+ }
+ return null;
+ }}
+ />
+
+
+
+
+ {
+ const { cx, cy, payload } = props;
+ return (
+ handleClick(payload)}
+ style={{ cursor: "pointer" }}
+ />
+ );
+ }}
+ />
+
+
+
+ );
+}
diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/condition/page.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/condition/page.tsx
index 87059775..ed66cb5e 100644
--- a/src/app/portfolio/[slug]/building-passport/[propertyId]/condition/page.tsx
+++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/condition/page.tsx
@@ -5,6 +5,7 @@ import { Survey } from "./types/survey";
import { Renewal } from "./viewModels/renewal";
import ElementTable, { ElementGroup } from "./components/ElementTable";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/app/shadcn_components/ui/tabs";
+import RenewalsTimeline from "./components/RenewalsTimeline";
type ConditionPageProps = {
params: {
@@ -50,7 +51,6 @@ function buildRenewals(elements: Element[]): Renewal[] {
renewalYear: aspect.renewalYear!,
elementType: element.elementType,
aspectType: aspect.aspectType,
- label: `${element.elementType} ${element.elementInstance} – ${aspect.aspectType}`,
comments: aspect.comments,
}))
);
@@ -101,7 +101,7 @@ export default async function Condition({ params }: ConditionPageProps) {
- Coming soon...
+
Coming soon...
diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/condition/viewModels/renewal.ts b/src/app/portfolio/[slug]/building-passport/[propertyId]/condition/viewModels/renewal.ts
index 1ea24cfb..71d8371b 100644
--- a/src/app/portfolio/[slug]/building-passport/[propertyId]/condition/viewModels/renewal.ts
+++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/condition/viewModels/renewal.ts
@@ -2,7 +2,6 @@
id: string; // unique for timeline
elementId: string;
aspectId: string;
- label: string; // what user sees
renewalYear: number;
elementType: string;
aspectType: string;