@burdenoff/microfe-housingvista 2026.626.2 → 2026.703.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/housingvista/HousingVistaRoot.js +60 -59
- package/dist/housingvista/HousingVistaRoot.js.map +1 -1
- package/dist/housingvista/HousingVistaRoutes.js +25 -21
- package/dist/housingvista/HousingVistaRoutes.js.map +1 -1
- package/dist/housingvista/components/DataTable.js +1 -1
- package/dist/housingvista/components/DataTable.js.map +1 -1
- package/dist/housingvista/components/EmptyState.js.map +1 -1
- package/dist/housingvista/components/ErrorBoundary.js +3 -3
- package/dist/housingvista/components/ErrorBoundary.js.map +1 -1
- package/dist/housingvista/components/LoadingSkeleton.js +76 -1
- package/dist/housingvista/components/LoadingSkeleton.js.map +1 -0
- package/dist/housingvista/components/PageLayout.js.map +1 -1
- package/dist/housingvista/components/PropertyCard.js +49 -50
- package/dist/housingvista/components/PropertyCard.js.map +1 -1
- package/dist/housingvista/components/StatCard.js +3 -3
- package/dist/housingvista/components/StatCard.js.map +1 -1
- package/dist/housingvista/components/StatusBadge.js +8 -8
- package/dist/housingvista/components/StatusBadge.js.map +1 -1
- package/dist/housingvista/components/index.js +0 -2
- package/dist/housingvista/hooks/index.js +670 -198
- package/dist/housingvista/hooks/index.js.map +1 -1
- package/dist/housingvista/lib/toast.js +36 -0
- package/dist/housingvista/lib/toast.js.map +1 -0
- package/dist/housingvista/pages/HomePage.js +8 -8
- package/dist/housingvista/pages/HomePage.js.map +1 -1
- package/dist/housingvista/pages/amc/AMCManagementPage.js +254 -78
- package/dist/housingvista/pages/amc/AMCManagementPage.js.map +1 -1
- package/dist/housingvista/pages/analytics/AnalyticsPage.js +192 -161
- package/dist/housingvista/pages/analytics/AnalyticsPage.js.map +1 -1
- package/dist/housingvista/pages/assets/AssetManagementPage.js +4 -4
- package/dist/housingvista/pages/assets/AssetManagementPage.js.map +1 -1
- package/dist/housingvista/pages/chat/ChatPage.js +17 -209
- package/dist/housingvista/pages/chat/ChatPage.js.map +1 -1
- package/dist/housingvista/pages/finance/HomeLoansPage.js +11 -11
- package/dist/housingvista/pages/finance/HomeLoansPage.js.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentDetailPage.js +243 -205
- package/dist/housingvista/pages/investments/InvestmentDetailPage.js.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentsPage.js +348 -114
- package/dist/housingvista/pages/investments/InvestmentsPage.js.map +1 -1
- package/dist/housingvista/pages/legal/LegalPage.js +8 -8
- package/dist/housingvista/pages/legal/LegalPage.js.map +1 -1
- package/dist/housingvista/pages/market/AreaInsightsPage.js +81 -84
- package/dist/housingvista/pages/market/AreaInsightsPage.js.map +1 -1
- package/dist/housingvista/pages/market/MarketAnalyticsPage.js +83 -85
- package/dist/housingvista/pages/market/MarketAnalyticsPage.js.map +1 -1
- package/dist/housingvista/pages/marketplace/MarketplacePage.js +6 -6
- package/dist/housingvista/pages/marketplace/MarketplacePage.js.map +1 -1
- package/dist/housingvista/pages/notifications/NotificationsPage.js +19 -133
- package/dist/housingvista/pages/notifications/NotificationsPage.js.map +1 -1
- package/dist/housingvista/pages/properties/AddPropertyPage.js +279 -243
- package/dist/housingvista/pages/properties/AddPropertyPage.js.map +1 -1
- package/dist/housingvista/pages/properties/PropertiesPage.js +216 -165
- package/dist/housingvista/pages/properties/PropertiesPage.js.map +1 -1
- package/dist/housingvista/pages/properties/PropertyDetailPage.js +619 -424
- package/dist/housingvista/pages/properties/PropertyDetailPage.js.map +1 -1
- package/dist/housingvista/pages/rental/RentalManagementPage.js +493 -130
- package/dist/housingvista/pages/rental/RentalManagementPage.js.map +1 -1
- package/dist/housingvista/pages/settings/ProfilePage.js +102 -102
- package/dist/housingvista/pages/settings/ProfilePage.js.map +1 -1
- package/dist/housingvista/pages/settings/SettingsPage.js +99 -139
- package/dist/housingvista/pages/settings/SettingsPage.js.map +1 -1
- package/dist/housingvista/pages/shortlist/ShortlistPage.js +63 -0
- package/dist/housingvista/pages/shortlist/ShortlistPage.js.map +1 -0
- package/dist/housingvista/pages/transactions/TransactionDetailPage.js +140 -185
- package/dist/housingvista/pages/transactions/TransactionDetailPage.js.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionsPage.js +269 -110
- package/dist/housingvista/pages/transactions/TransactionsPage.js.map +1 -1
- package/dist/housingvista/pages/verification/VerificationPage.js +195 -116
- package/dist/housingvista/pages/verification/VerificationPage.js.map +1 -1
- package/dist/node_modules/sonner/dist/index.js +163 -0
- package/dist/node_modules/sonner/dist/index.js.map +1 -0
- package/package.json +3 -3
- package/dist/housingvista/components/Modal.js +0 -3
- package/dist/housingvista/components/Toast.js +0 -3
|
@@ -6,13 +6,18 @@ import { StatusBadge as o } from "../../components/StatusBadge.js";
|
|
|
6
6
|
import { EmptyState as s } from "../../components/EmptyState.js";
|
|
7
7
|
import { FilterBar as c } from "../../components/FilterBar.js";
|
|
8
8
|
import "../../components/index.js";
|
|
9
|
-
import { useAMCContracts as l } from "../../hooks/index.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
9
|
+
import { useAMCContracts as l, useCreateAmcContract as u } from "../../hooks/index.js";
|
|
10
|
+
import { notify as d } from "../../lib/toast.js";
|
|
11
|
+
import { useMemo as f, useState as p } from "react";
|
|
12
|
+
import { AlertTriangle as m, Calendar as ee, Clock as te, DollarSign as ne, Download as re, Plus as h, RotateCcw as g, Shield as _, Wrench as ie } from "lucide-react";
|
|
13
|
+
import { useNavigate as ae } from "react-router-dom";
|
|
14
|
+
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
15
|
+
import { Button as b, EmphasisPanel as oe, NextSteps as se, ResponsiveDialog as ce, ResponsiveDialogContent as le, ResponsiveDialogDescription as ue, ResponsiveDialogFooter as x, ResponsiveDialogHeader as S, ResponsiveDialogTitle as C } from "@burdenoff/fe-libs/ui";
|
|
15
16
|
//#region src/housingvista/pages/amc/AMCManagementPage.tsx
|
|
17
|
+
var w = "h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]";
|
|
18
|
+
function T(e) {
|
|
19
|
+
return e ? new Date(e).toISOString() : "";
|
|
20
|
+
}
|
|
16
21
|
function E(e) {
|
|
17
22
|
switch (e) {
|
|
18
23
|
case "active": return "success";
|
|
@@ -23,11 +28,41 @@ function E(e) {
|
|
|
23
28
|
}
|
|
24
29
|
}
|
|
25
30
|
function D() {
|
|
26
|
-
let D =
|
|
31
|
+
let D = ae(), { data: O } = l(), [k, A] = p(""), { createAmcContract: de, loading: j } = u(), [fe, M] = p(!1), [N, P] = p(""), [F, I] = p(""), [L, R] = p(""), [z, B] = p(""), [V, H] = p(""), [U, W] = p(""), [G, K] = p("active"), [q, J] = p(""), Y = () => {
|
|
32
|
+
P(""), I(""), R(""), B(""), H(""), W(""), K("active"), J("");
|
|
33
|
+
}, X = () => {
|
|
34
|
+
Y(), M(!0);
|
|
35
|
+
}, pe = async (e) => {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
let t = Number(z);
|
|
38
|
+
if (!N.trim() || !F.trim() || !L.trim()) {
|
|
39
|
+
d.warning("Missing details", "Property, package and provider are required.");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!Number.isFinite(t) || t <= 0) {
|
|
43
|
+
d.warning("Enter a valid monthly cost");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
let n = q.split(",").map((e) => e.trim()).filter((e) => e.length > 0);
|
|
47
|
+
try {
|
|
48
|
+
await de({
|
|
49
|
+
propertyTitle: N.trim(),
|
|
50
|
+
packageName: F.trim(),
|
|
51
|
+
providerName: L.trim(),
|
|
52
|
+
monthlyCost: t,
|
|
53
|
+
startDate: T(V),
|
|
54
|
+
endDate: T(U),
|
|
55
|
+
status: G,
|
|
56
|
+
servicesIncluded: n.length > 0 ? n : void 0
|
|
57
|
+
}), d.success("Contract created", `${F.trim()} added for ${N.trim()}.`), M(!1), Y();
|
|
58
|
+
} catch (e) {
|
|
59
|
+
d.error("Could not create contract", e.message);
|
|
60
|
+
}
|
|
61
|
+
}, Z = f(() => {
|
|
27
62
|
if (!k) return O;
|
|
28
63
|
let e = k.toLowerCase();
|
|
29
64
|
return O.filter((t) => t.propertyTitle.toLowerCase().includes(e) || t.providerName.toLowerCase().includes(e) || t.packageName.toLowerCase().includes(e));
|
|
30
|
-
}, [O, k]),
|
|
65
|
+
}, [O, k]), Q = f(() => ({
|
|
31
66
|
activeContracts: O.filter((e) => e.status === "active" || e.status === "expiring_soon").length,
|
|
32
67
|
pendingRenewals: O.filter((e) => e.status === "expiring_soon").length,
|
|
33
68
|
totalMonthlySpent: O.filter((e) => e.status === "active" || e.status === "expiring_soon").reduce((e, t) => e + t.monthlyCost, 0),
|
|
@@ -36,54 +71,56 @@ function D() {
|
|
|
36
71
|
let t = new Date(e.nextServiceDate), n = /* @__PURE__ */ new Date(), r = (t.getTime() - n.getTime()) / (1e3 * 60 * 60 * 24);
|
|
37
72
|
return r >= 0 && r <= 30;
|
|
38
73
|
}).length
|
|
39
|
-
}), [O]),
|
|
40
|
-
return /* @__PURE__ */
|
|
74
|
+
}), [O]), $ = f(() => O.filter((e) => e.nextServiceDate && (e.status === "active" || e.status === "expiring_soon")).sort((e, t) => new Date(e.nextServiceDate).getTime() - new Date(t.nextServiceDate).getTime()), [O]);
|
|
75
|
+
return /* @__PURE__ */ y(r, {
|
|
41
76
|
title: "AMC Management",
|
|
42
77
|
description: "Manage Annual Maintenance Contracts and service packages",
|
|
43
78
|
purpose: "Keep your properties maintained without surprises. Track every Annual Maintenance Contract, see what each covers and costs, get ahead of renewals, and know which services are due in the next 30 days so nothing slips.",
|
|
44
|
-
actions: /* @__PURE__ */
|
|
79
|
+
actions: /* @__PURE__ */ y("div", {
|
|
45
80
|
className: "flex items-center gap-2",
|
|
46
|
-
children: [/* @__PURE__ */
|
|
81
|
+
children: [/* @__PURE__ */ y("button", {
|
|
47
82
|
className: "inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
|
|
48
|
-
children: [/* @__PURE__ */
|
|
49
|
-
}), /* @__PURE__ */
|
|
50
|
-
|
|
51
|
-
|
|
83
|
+
children: [/* @__PURE__ */ v(re, { className: "h-4 w-4" }), "Export"]
|
|
84
|
+
}), /* @__PURE__ */ y(b, {
|
|
85
|
+
type: "button",
|
|
86
|
+
onClick: X,
|
|
87
|
+
className: "gap-2",
|
|
88
|
+
children: [/* @__PURE__ */ v(h, { className: "h-4 w-4" }), "New Contract"]
|
|
52
89
|
})]
|
|
53
90
|
}),
|
|
54
91
|
children: [
|
|
55
|
-
/* @__PURE__ */
|
|
92
|
+
/* @__PURE__ */ v(oe, {
|
|
56
93
|
className: "p-4",
|
|
57
|
-
children: /* @__PURE__ */
|
|
94
|
+
children: /* @__PURE__ */ y("div", {
|
|
58
95
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
59
96
|
children: [
|
|
60
|
-
/* @__PURE__ */
|
|
97
|
+
/* @__PURE__ */ v(i, {
|
|
61
98
|
label: "Active Contracts",
|
|
62
|
-
value:
|
|
63
|
-
icon: /* @__PURE__ */
|
|
99
|
+
value: Q.activeContracts,
|
|
100
|
+
icon: /* @__PURE__ */ v(_, { className: "h-4 w-4" })
|
|
64
101
|
}),
|
|
65
|
-
/* @__PURE__ */
|
|
102
|
+
/* @__PURE__ */ v(i, {
|
|
66
103
|
label: "Pending Renewals",
|
|
67
|
-
value:
|
|
68
|
-
icon: /* @__PURE__ */
|
|
69
|
-
trend:
|
|
70
|
-
change:
|
|
104
|
+
value: Q.pendingRenewals,
|
|
105
|
+
icon: /* @__PURE__ */ v(g, { className: "h-4 w-4" }),
|
|
106
|
+
trend: Q.pendingRenewals > 0 ? "up" : "stable",
|
|
107
|
+
change: Q.pendingRenewals > 0 ? Q.pendingRenewals : 0
|
|
71
108
|
}),
|
|
72
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ v(i, {
|
|
73
110
|
label: "Total Monthly Spent",
|
|
74
|
-
value:
|
|
111
|
+
value: Q.totalMonthlySpent,
|
|
75
112
|
prefix: "$",
|
|
76
|
-
icon: /* @__PURE__ */
|
|
113
|
+
icon: /* @__PURE__ */ v(ne, { className: "h-4 w-4" })
|
|
77
114
|
}),
|
|
78
|
-
/* @__PURE__ */
|
|
115
|
+
/* @__PURE__ */ v(i, {
|
|
79
116
|
label: "Upcoming Services",
|
|
80
|
-
value:
|
|
81
|
-
icon: /* @__PURE__ */
|
|
117
|
+
value: Q.upcomingServices,
|
|
118
|
+
icon: /* @__PURE__ */ v(ee, { className: "h-4 w-4" })
|
|
82
119
|
})
|
|
83
120
|
]
|
|
84
121
|
})
|
|
85
122
|
}),
|
|
86
|
-
/* @__PURE__ */
|
|
123
|
+
/* @__PURE__ */ v(se, {
|
|
87
124
|
storageKey: "housingvista-amc",
|
|
88
125
|
steps: [
|
|
89
126
|
{
|
|
@@ -109,33 +146,33 @@ function D() {
|
|
|
109
146
|
}
|
|
110
147
|
]
|
|
111
148
|
}),
|
|
112
|
-
/* @__PURE__ */
|
|
149
|
+
/* @__PURE__ */ y("div", {
|
|
113
150
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-5 shadow-sm",
|
|
114
|
-
children: [/* @__PURE__ */
|
|
151
|
+
children: [/* @__PURE__ */ v("h3", {
|
|
115
152
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
116
153
|
children: "Upcoming Service Schedule"
|
|
117
|
-
}),
|
|
154
|
+
}), $.length === 0 ? /* @__PURE__ */ v(s, {
|
|
118
155
|
illustration: "empty-generic",
|
|
119
156
|
title: "No services scheduled",
|
|
120
157
|
description: "Once your active contracts have upcoming service dates, they'll be listed here in order of priority.",
|
|
121
158
|
className: "border-0 bg-transparent p-6"
|
|
122
|
-
}) : /* @__PURE__ */
|
|
159
|
+
}) : /* @__PURE__ */ v("div", {
|
|
123
160
|
className: "space-y-3",
|
|
124
|
-
children:
|
|
161
|
+
children: $.map((t) => {
|
|
125
162
|
let r = new Date(t.nextServiceDate), i = /* @__PURE__ */ new Date(), a = Math.ceil((r.getTime() - i.getTime()) / (1e3 * 60 * 60 * 24)), s = a <= 7, c = a < 0;
|
|
126
|
-
return /* @__PURE__ */
|
|
163
|
+
return /* @__PURE__ */ y("div", {
|
|
127
164
|
className: "flex flex-col gap-3 rounded-lg bg-bg-sunken/50 p-4 sm:flex-row sm:items-center sm:justify-between",
|
|
128
|
-
children: [/* @__PURE__ */
|
|
165
|
+
children: [/* @__PURE__ */ y("div", {
|
|
129
166
|
className: "flex items-start gap-3",
|
|
130
|
-
children: [/* @__PURE__ */
|
|
131
|
-
className: e("rounded-lg p-2", c ? "bg-
|
|
132
|
-
children: c ? /* @__PURE__ */
|
|
133
|
-
}), /* @__PURE__ */
|
|
134
|
-
/* @__PURE__ */
|
|
167
|
+
children: [/* @__PURE__ */ v("div", {
|
|
168
|
+
className: e("rounded-lg p-2", c ? "bg-status-error-bg-subtle" : s ? "bg-status-warning-bg-subtle" : "bg-status-info-bg-subtle"),
|
|
169
|
+
children: c ? /* @__PURE__ */ v(m, { className: "h-4 w-4 text-status-error-text" }) : s ? /* @__PURE__ */ v(te, { className: "h-4 w-4 text-status-warning-text" }) : /* @__PURE__ */ v(ie, { className: "h-4 w-4 text-status-info-text" })
|
|
170
|
+
}), /* @__PURE__ */ y("div", { children: [
|
|
171
|
+
/* @__PURE__ */ v("p", {
|
|
135
172
|
className: "font-medium text-text-primary",
|
|
136
173
|
children: t.propertyTitle
|
|
137
174
|
}),
|
|
138
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ y("p", {
|
|
139
176
|
className: "text-sm text-text-secondary",
|
|
140
177
|
children: [
|
|
141
178
|
t.packageName,
|
|
@@ -143,12 +180,12 @@ function D() {
|
|
|
143
180
|
t.providerName
|
|
144
181
|
]
|
|
145
182
|
}),
|
|
146
|
-
/* @__PURE__ */
|
|
183
|
+
/* @__PURE__ */ y("div", {
|
|
147
184
|
className: "mt-1 flex flex-wrap gap-1",
|
|
148
|
-
children: [t.servicesIncluded.slice(0, 3).map((e, t) => /* @__PURE__ */
|
|
185
|
+
children: [t.servicesIncluded.slice(0, 3).map((e, t) => /* @__PURE__ */ v("span", {
|
|
149
186
|
className: "inline-block rounded-md bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
|
|
150
187
|
children: e
|
|
151
|
-
}, t)), t.servicesIncluded.length > 3 && /* @__PURE__ */
|
|
188
|
+
}, t)), t.servicesIncluded.length > 3 && /* @__PURE__ */ y("span", {
|
|
152
189
|
className: "inline-block rounded-md bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
|
|
153
190
|
children: [
|
|
154
191
|
"+",
|
|
@@ -158,18 +195,18 @@ function D() {
|
|
|
158
195
|
})]
|
|
159
196
|
})
|
|
160
197
|
] })]
|
|
161
|
-
}), /* @__PURE__ */
|
|
198
|
+
}), /* @__PURE__ */ y("div", {
|
|
162
199
|
className: "flex items-center gap-3",
|
|
163
|
-
children: [/* @__PURE__ */
|
|
200
|
+
children: [/* @__PURE__ */ y("div", {
|
|
164
201
|
className: "text-right",
|
|
165
|
-
children: [/* @__PURE__ */
|
|
202
|
+
children: [/* @__PURE__ */ v("p", {
|
|
166
203
|
className: "text-sm font-medium text-text-primary",
|
|
167
204
|
children: n(t.nextServiceDate)
|
|
168
|
-
}), /* @__PURE__ */
|
|
169
|
-
className: e("text-xs", c ? "text-
|
|
205
|
+
}), /* @__PURE__ */ v("p", {
|
|
206
|
+
className: e("text-xs", c ? "text-status-error-text" : s ? "text-status-warning-text" : "text-text-secondary"),
|
|
170
207
|
children: c ? `${Math.abs(a)} days overdue` : a === 0 ? "Today" : `In ${a} days`
|
|
171
208
|
})]
|
|
172
|
-
}), /* @__PURE__ */
|
|
209
|
+
}), /* @__PURE__ */ v(o, {
|
|
173
210
|
label: t.status.replace("_", " "),
|
|
174
211
|
variant: E(t.status)
|
|
175
212
|
})]
|
|
@@ -178,27 +215,27 @@ function D() {
|
|
|
178
215
|
})
|
|
179
216
|
})]
|
|
180
217
|
}),
|
|
181
|
-
/* @__PURE__ */
|
|
182
|
-
/* @__PURE__ */
|
|
218
|
+
/* @__PURE__ */ y("div", { children: [
|
|
219
|
+
/* @__PURE__ */ v("h2", {
|
|
183
220
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
184
221
|
children: "All Contracts"
|
|
185
222
|
}),
|
|
186
|
-
/* @__PURE__ */
|
|
223
|
+
/* @__PURE__ */ v(c, {
|
|
187
224
|
searchValue: k,
|
|
188
225
|
onSearchChange: A,
|
|
189
226
|
searchPlaceholder: "Search contracts, providers, properties..."
|
|
190
227
|
}),
|
|
191
|
-
/* @__PURE__ */
|
|
228
|
+
/* @__PURE__ */ v("div", {
|
|
192
229
|
className: "mt-4",
|
|
193
|
-
children:
|
|
230
|
+
children: Z.length > 0 ? /* @__PURE__ */ v(a, {
|
|
194
231
|
columns: [
|
|
195
232
|
{
|
|
196
233
|
key: "packageName",
|
|
197
234
|
header: "Contract",
|
|
198
|
-
render: (e) => /* @__PURE__ */
|
|
235
|
+
render: (e) => /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("div", {
|
|
199
236
|
className: "font-medium text-text-primary",
|
|
200
237
|
children: e.packageName
|
|
201
|
-
}), /* @__PURE__ */
|
|
238
|
+
}), /* @__PURE__ */ v("div", {
|
|
202
239
|
className: "text-xs text-text-secondary",
|
|
203
240
|
children: e.propertyTitle
|
|
204
241
|
})] })
|
|
@@ -206,7 +243,7 @@ function D() {
|
|
|
206
243
|
{
|
|
207
244
|
key: "providerName",
|
|
208
245
|
header: "Provider",
|
|
209
|
-
render: (e) => /* @__PURE__ */
|
|
246
|
+
render: (e) => /* @__PURE__ */ v("span", {
|
|
210
247
|
className: "text-sm text-text-primary",
|
|
211
248
|
children: e.providerName
|
|
212
249
|
})
|
|
@@ -214,12 +251,12 @@ function D() {
|
|
|
214
251
|
{
|
|
215
252
|
key: "servicesIncluded",
|
|
216
253
|
header: "Coverage",
|
|
217
|
-
render: (e) => /* @__PURE__ */
|
|
254
|
+
render: (e) => /* @__PURE__ */ y("div", {
|
|
218
255
|
className: "max-w-[200px]",
|
|
219
|
-
children: [/* @__PURE__ */
|
|
256
|
+
children: [/* @__PURE__ */ y("span", {
|
|
220
257
|
className: "text-sm text-text-primary",
|
|
221
258
|
children: [e.servicesIncluded.length, " services"]
|
|
222
|
-
}), /* @__PURE__ */
|
|
259
|
+
}), /* @__PURE__ */ y("p", {
|
|
223
260
|
className: "truncate text-xs text-text-secondary",
|
|
224
261
|
children: [e.servicesIncluded.slice(0, 2).join(", "), e.servicesIncluded.length > 2 && ` +${e.servicesIncluded.length - 2} more`]
|
|
225
262
|
})]
|
|
@@ -228,10 +265,10 @@ function D() {
|
|
|
228
265
|
{
|
|
229
266
|
key: "monthlyCost",
|
|
230
267
|
header: "Cost",
|
|
231
|
-
render: (e) => /* @__PURE__ */
|
|
268
|
+
render: (e) => /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("span", {
|
|
232
269
|
className: "font-medium text-text-primary",
|
|
233
270
|
children: t(e.monthlyCost)
|
|
234
|
-
}), /* @__PURE__ */
|
|
271
|
+
}), /* @__PURE__ */ v("span", {
|
|
235
272
|
className: "text-xs text-text-secondary",
|
|
236
273
|
children: "/mo"
|
|
237
274
|
})] })
|
|
@@ -239,7 +276,7 @@ function D() {
|
|
|
239
276
|
{
|
|
240
277
|
key: "startDate",
|
|
241
278
|
header: "Start",
|
|
242
|
-
render: (e) => /* @__PURE__ */
|
|
279
|
+
render: (e) => /* @__PURE__ */ v("span", {
|
|
243
280
|
className: "text-sm text-text-primary",
|
|
244
281
|
children: n(e.startDate)
|
|
245
282
|
})
|
|
@@ -247,7 +284,7 @@ function D() {
|
|
|
247
284
|
{
|
|
248
285
|
key: "endDate",
|
|
249
286
|
header: "End",
|
|
250
|
-
render: (e) => /* @__PURE__ */
|
|
287
|
+
render: (e) => /* @__PURE__ */ v("span", {
|
|
251
288
|
className: "text-sm text-text-primary",
|
|
252
289
|
children: n(e.endDate)
|
|
253
290
|
})
|
|
@@ -255,28 +292,167 @@ function D() {
|
|
|
255
292
|
{
|
|
256
293
|
key: "status",
|
|
257
294
|
header: "Status",
|
|
258
|
-
render: (e) => /* @__PURE__ */
|
|
295
|
+
render: (e) => /* @__PURE__ */ v(o, {
|
|
259
296
|
label: e.status.replace("_", " "),
|
|
260
297
|
variant: E(e.status),
|
|
261
298
|
dot: !0
|
|
262
299
|
})
|
|
263
300
|
}
|
|
264
301
|
],
|
|
265
|
-
data:
|
|
302
|
+
data: Z,
|
|
266
303
|
keyField: "id",
|
|
267
304
|
pageSize: 10
|
|
268
|
-
}) : /* @__PURE__ */
|
|
305
|
+
}) : /* @__PURE__ */ v(s, {
|
|
269
306
|
illustration: "empty-search",
|
|
270
|
-
icon: /* @__PURE__ */
|
|
307
|
+
icon: /* @__PURE__ */ v(_, { className: "h-12 w-12" }),
|
|
271
308
|
title: "No contracts found",
|
|
272
309
|
description: "Try adjusting your search criteria or create a new contract.",
|
|
273
|
-
action: /* @__PURE__ */
|
|
274
|
-
|
|
275
|
-
|
|
310
|
+
action: /* @__PURE__ */ y(b, {
|
|
311
|
+
type: "button",
|
|
312
|
+
onClick: X,
|
|
313
|
+
className: "gap-2",
|
|
314
|
+
children: [/* @__PURE__ */ v(h, { className: "h-4 w-4" }), "New Contract"]
|
|
276
315
|
})
|
|
277
316
|
})
|
|
278
317
|
})
|
|
279
|
-
] })
|
|
318
|
+
] }),
|
|
319
|
+
/* @__PURE__ */ v(ce, {
|
|
320
|
+
open: fe,
|
|
321
|
+
onOpenChange: M,
|
|
322
|
+
children: /* @__PURE__ */ v(le, {
|
|
323
|
+
className: "sm:max-w-lg",
|
|
324
|
+
children: /* @__PURE__ */ y("form", {
|
|
325
|
+
onSubmit: pe,
|
|
326
|
+
children: [
|
|
327
|
+
/* @__PURE__ */ y(S, { children: [/* @__PURE__ */ v(C, { children: "New maintenance contract" }), /* @__PURE__ */ v(ue, { children: "Set up an Annual Maintenance Contract to track coverage, cost and renewal dates." })] }),
|
|
328
|
+
/* @__PURE__ */ y("div", {
|
|
329
|
+
className: "space-y-3 py-2",
|
|
330
|
+
children: [
|
|
331
|
+
/* @__PURE__ */ y("div", {
|
|
332
|
+
className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
|
|
333
|
+
children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
|
|
334
|
+
className: "mb-1.5 block text-sm font-medium text-text-primary",
|
|
335
|
+
children: "Property *"
|
|
336
|
+
}), /* @__PURE__ */ v("input", {
|
|
337
|
+
type: "text",
|
|
338
|
+
required: !0,
|
|
339
|
+
placeholder: "e.g. Maple Court",
|
|
340
|
+
value: N,
|
|
341
|
+
onChange: (e) => P(e.target.value),
|
|
342
|
+
className: w
|
|
343
|
+
})] }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
|
|
344
|
+
className: "mb-1.5 block text-sm font-medium text-text-primary",
|
|
345
|
+
children: "Package *"
|
|
346
|
+
}), /* @__PURE__ */ v("input", {
|
|
347
|
+
type: "text",
|
|
348
|
+
required: !0,
|
|
349
|
+
placeholder: "e.g. Premium Care",
|
|
350
|
+
value: F,
|
|
351
|
+
onChange: (e) => I(e.target.value),
|
|
352
|
+
className: w
|
|
353
|
+
})] })]
|
|
354
|
+
}),
|
|
355
|
+
/* @__PURE__ */ y("div", {
|
|
356
|
+
className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
|
|
357
|
+
children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
|
|
358
|
+
className: "mb-1.5 block text-sm font-medium text-text-primary",
|
|
359
|
+
children: "Provider *"
|
|
360
|
+
}), /* @__PURE__ */ v("input", {
|
|
361
|
+
type: "text",
|
|
362
|
+
required: !0,
|
|
363
|
+
placeholder: "Service company",
|
|
364
|
+
value: L,
|
|
365
|
+
onChange: (e) => R(e.target.value),
|
|
366
|
+
className: w
|
|
367
|
+
})] }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
|
|
368
|
+
className: "mb-1.5 block text-sm font-medium text-text-primary",
|
|
369
|
+
children: "Monthly cost *"
|
|
370
|
+
}), /* @__PURE__ */ v("input", {
|
|
371
|
+
type: "number",
|
|
372
|
+
required: !0,
|
|
373
|
+
min: 0,
|
|
374
|
+
step: "0.01",
|
|
375
|
+
placeholder: "0.00",
|
|
376
|
+
value: z,
|
|
377
|
+
onChange: (e) => B(e.target.value),
|
|
378
|
+
className: w
|
|
379
|
+
})] })]
|
|
380
|
+
}),
|
|
381
|
+
/* @__PURE__ */ y("div", {
|
|
382
|
+
className: "grid grid-cols-1 gap-3 sm:grid-cols-3",
|
|
383
|
+
children: [
|
|
384
|
+
/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
|
|
385
|
+
className: "mb-1.5 block text-sm font-medium text-text-primary",
|
|
386
|
+
children: "Start date"
|
|
387
|
+
}), /* @__PURE__ */ v("input", {
|
|
388
|
+
type: "date",
|
|
389
|
+
value: V,
|
|
390
|
+
onChange: (e) => H(e.target.value),
|
|
391
|
+
className: w
|
|
392
|
+
})] }),
|
|
393
|
+
/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
|
|
394
|
+
className: "mb-1.5 block text-sm font-medium text-text-primary",
|
|
395
|
+
children: "End date"
|
|
396
|
+
}), /* @__PURE__ */ v("input", {
|
|
397
|
+
type: "date",
|
|
398
|
+
value: U,
|
|
399
|
+
onChange: (e) => W(e.target.value),
|
|
400
|
+
className: w
|
|
401
|
+
})] }),
|
|
402
|
+
/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
|
|
403
|
+
className: "mb-1.5 block text-sm font-medium text-text-primary",
|
|
404
|
+
children: "Status"
|
|
405
|
+
}), /* @__PURE__ */ y("select", {
|
|
406
|
+
value: G,
|
|
407
|
+
onChange: (e) => K(e.target.value),
|
|
408
|
+
className: w,
|
|
409
|
+
children: [
|
|
410
|
+
/* @__PURE__ */ v("option", {
|
|
411
|
+
value: "active",
|
|
412
|
+
children: "Active"
|
|
413
|
+
}),
|
|
414
|
+
/* @__PURE__ */ v("option", {
|
|
415
|
+
value: "expiring_soon",
|
|
416
|
+
children: "Expiring soon"
|
|
417
|
+
}),
|
|
418
|
+
/* @__PURE__ */ v("option", {
|
|
419
|
+
value: "expired",
|
|
420
|
+
children: "Expired"
|
|
421
|
+
}),
|
|
422
|
+
/* @__PURE__ */ v("option", {
|
|
423
|
+
value: "cancelled",
|
|
424
|
+
children: "Cancelled"
|
|
425
|
+
})
|
|
426
|
+
]
|
|
427
|
+
})] })
|
|
428
|
+
]
|
|
429
|
+
}),
|
|
430
|
+
/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
|
|
431
|
+
className: "mb-1.5 block text-sm font-medium text-text-primary",
|
|
432
|
+
children: "Services included"
|
|
433
|
+
}), /* @__PURE__ */ v("input", {
|
|
434
|
+
type: "text",
|
|
435
|
+
placeholder: "Comma-separated, e.g. Plumbing, Electrical, HVAC",
|
|
436
|
+
value: q,
|
|
437
|
+
onChange: (e) => J(e.target.value),
|
|
438
|
+
className: w
|
|
439
|
+
})] })
|
|
440
|
+
]
|
|
441
|
+
}),
|
|
442
|
+
/* @__PURE__ */ y(x, { children: [/* @__PURE__ */ v(b, {
|
|
443
|
+
type: "button",
|
|
444
|
+
variant: "outline",
|
|
445
|
+
onClick: () => M(!1),
|
|
446
|
+
children: "Cancel"
|
|
447
|
+
}), /* @__PURE__ */ v(b, {
|
|
448
|
+
type: "submit",
|
|
449
|
+
disabled: j,
|
|
450
|
+
children: j ? "Saving…" : "Create contract"
|
|
451
|
+
})] })
|
|
452
|
+
]
|
|
453
|
+
})
|
|
454
|
+
})
|
|
455
|
+
})
|
|
280
456
|
]
|
|
281
457
|
});
|
|
282
458
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AMCManagementPage.js","names":[],"sources":["../../../../src/housingvista/pages/amc/AMCManagementPage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Shield,\n FileText,\n DollarSign,\n Calendar,\n Plus,\n Download,\n CheckCircle,\n Clock,\n AlertTriangle,\n Wrench,\n RotateCcw,\n Eye,\n} from 'lucide-react';\nimport { EmphasisPanel, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components';\nimport { StatCard } from '../../components';\nimport { DataTable, type Column } from '../../components';\nimport { StatusBadge } from '../../components';\nimport { FilterBar } from '../../components';\nimport { EmptyState } from '../../components';\nimport { useAMCContracts } from '../../hooks';\nimport { cn, formatCurrency, formatDate } from '../../utils/helpers';\nimport type { AMCContract } from '../../types';\n\nfunction getStatusVariant(status: AMCContract['status']): 'success' | 'warning' | 'error' | 'muted' {\n switch (status) {\n case 'active':\n return 'success';\n case 'expiring_soon':\n return 'warning';\n case 'expired':\n return 'error';\n case 'cancelled':\n return 'muted';\n default:\n return 'muted';\n }\n}\n\nexport function AMCManagementPage() {\n const navigate = useNavigate();\n const { data: amcContracts } = useAMCContracts();\n const [search, setSearch] = useState('');\n\n const filtered = useMemo(() => {\n if (!search) return amcContracts;\n const q = search.toLowerCase();\n return amcContracts.filter(\n (c) =>\n c.propertyTitle.toLowerCase().includes(q) ||\n c.providerName.toLowerCase().includes(q) ||\n c.packageName.toLowerCase().includes(q)\n );\n }, [amcContracts, search]);\n\n const stats = useMemo(() => {\n const activeContracts = amcContracts.filter((c) => c.status === 'active' || c.status === 'expiring_soon').length;\n const pendingRenewals = amcContracts.filter((c) => c.status === 'expiring_soon').length;\n const totalMonthlySpent = amcContracts\n .filter((c) => c.status === 'active' || c.status === 'expiring_soon')\n .reduce((sum, c) => sum + c.monthlyCost, 0);\n const upcomingServices = amcContracts.filter((c) => {\n if (!c.nextServiceDate) return false;\n const serviceDate = new Date(c.nextServiceDate);\n const now = new Date();\n const daysUntil = (serviceDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24);\n return daysUntil >= 0 && daysUntil <= 30;\n }).length;\n return { activeContracts, pendingRenewals, totalMonthlySpent, upcomingServices };\n }, [amcContracts]);\n\n const scheduledServices = useMemo(\n () =>\n amcContracts\n .filter((c) => c.nextServiceDate && (c.status === 'active' || c.status === 'expiring_soon'))\n .sort((a, b) => new Date(a.nextServiceDate).getTime() - new Date(b.nextServiceDate).getTime()),\n [amcContracts]\n );\n\n const contractColumns: Column<AMCContract>[] = [\n {\n key: 'packageName',\n header: 'Contract',\n render: (item) => (\n <div>\n <div className=\"font-medium text-text-primary\">{item.packageName}</div>\n <div className=\"text-xs text-text-secondary\">{item.propertyTitle}</div>\n </div>\n ),\n },\n {\n key: 'providerName',\n header: 'Provider',\n render: (item) => <span className=\"text-sm text-text-primary\">{item.providerName}</span>,\n },\n {\n key: 'servicesIncluded',\n header: 'Coverage',\n render: (item) => (\n <div className=\"max-w-[200px]\">\n <span className=\"text-sm text-text-primary\">{item.servicesIncluded.length} services</span>\n <p className=\"truncate text-xs text-text-secondary\">\n {item.servicesIncluded.slice(0, 2).join(', ')}\n {item.servicesIncluded.length > 2 && ` +${item.servicesIncluded.length - 2} more`}\n </p>\n </div>\n ),\n },\n {\n key: 'monthlyCost',\n header: 'Cost',\n render: (item) => (\n <div>\n <span className=\"font-medium text-text-primary\">{formatCurrency(item.monthlyCost)}</span>\n <span className=\"text-xs text-text-secondary\">/mo</span>\n </div>\n ),\n },\n {\n key: 'startDate',\n header: 'Start',\n render: (item) => <span className=\"text-sm text-text-primary\">{formatDate(item.startDate)}</span>,\n },\n {\n key: 'endDate',\n header: 'End',\n render: (item) => <span className=\"text-sm text-text-primary\">{formatDate(item.endDate)}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (item) => (\n <StatusBadge label={item.status.replace('_', ' ')} variant={getStatusVariant(item.status)} dot />\n ),\n },\n ];\n\n return (\n <PageLayout\n title=\"AMC Management\"\n description=\"Manage Annual Maintenance Contracts and service packages\"\n purpose=\"Keep your properties maintained without surprises. Track every Annual Maintenance Contract, see what each covers and costs, get ahead of renewals, and know which services are due in the next 30 days so nothing slips.\"\n actions={\n <div className=\"flex items-center gap-2\">\n <button className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\">\n <Download className=\"h-4 w-4\" />\n Export\n </button>\n <button className=\"inline-flex items-center gap-2 rounded-md bg-foreground px-4 py-2 text-sm font-medium text-background transition-colors hover:bg-foreground/90\">\n <Plus className=\"h-4 w-4\" />\n New Contract\n </button>\n </div>\n }\n >\n {/* Stats — primary insight zone (one EmphasisPanel per page) */}\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard label=\"Active Contracts\" value={stats.activeContracts} icon={<Shield className=\"h-4 w-4\" />} />\n <StatCard\n label=\"Pending Renewals\"\n value={stats.pendingRenewals}\n icon={<RotateCcw className=\"h-4 w-4\" />}\n trend={stats.pendingRenewals > 0 ? 'up' : 'stable'}\n change={stats.pendingRenewals > 0 ? stats.pendingRenewals : 0}\n />\n <StatCard\n label=\"Total Monthly Spent\"\n value={stats.totalMonthlySpent}\n prefix=\"$\"\n icon={<DollarSign className=\"h-4 w-4\" />}\n />\n <StatCard label=\"Upcoming Services\" value={stats.upcomingServices} icon={<Calendar className=\"h-4 w-4\" />} />\n </div>\n </EmphasisPanel>\n\n <NextSteps\n storageKey=\"housingvista-amc\"\n steps={[\n {\n id: 'pick-property',\n label: 'Add coverage for a property',\n description: 'Choose a property to set up a new maintenance contract.',\n onClick: () => navigate('/properties'),\n },\n {\n id: 'compare-providers',\n label: 'Compare your providers',\n description: 'Search by provider name to weigh coverage and monthly cost.',\n onClick: () => {\n const firstProvider = amcContracts[0]?.providerName;\n if (firstProvider) setSearch(firstProvider);\n },\n },\n {\n id: 'check-legal',\n label: 'Review contract paperwork',\n description: 'Open the legal hub to manage the underlying documents.',\n onClick: () => navigate('/legal'),\n },\n ]}\n />\n\n {/* Service Schedule Section */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5 shadow-sm\">\n <h3 className=\"mb-4 text-lg font-semibold text-text-primary\">Upcoming Service Schedule</h3>\n {scheduledServices.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No services scheduled\"\n description=\"Once your active contracts have upcoming service dates, they'll be listed here in order of priority.\"\n className=\"border-0 bg-transparent p-6\"\n />\n ) : (\n <div className=\"space-y-3\">\n {scheduledServices\n .map((contract) => {\n const serviceDate = new Date(contract.nextServiceDate);\n const now = new Date();\n const daysUntil = Math.ceil((serviceDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));\n const isUrgent = daysUntil <= 7;\n const isPast = daysUntil < 0;\n\n return (\n <div\n key={contract.id}\n className=\"flex flex-col gap-3 rounded-lg bg-bg-sunken/50 p-4 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"flex items-start gap-3\">\n <div\n className={cn(\n 'rounded-lg p-2',\n isPast\n ? 'bg-red-100 dark:bg-red-900/30'\n : isUrgent\n ? 'bg-amber-100 dark:bg-amber-900/30'\n : 'bg-blue-100 dark:bg-blue-900/30'\n )}\n >\n {isPast ? (\n <AlertTriangle className=\"h-4 w-4 text-red-600 dark:text-red-400\" />\n ) : isUrgent ? (\n <Clock className=\"h-4 w-4 text-amber-600 dark:text-amber-400\" />\n ) : (\n <Wrench className=\"h-4 w-4 text-blue-600 dark:text-blue-400\" />\n )}\n </div>\n <div>\n <p className=\"font-medium text-text-primary\">{contract.propertyTitle}</p>\n <p className=\"text-sm text-text-secondary\">\n {contract.packageName} · {contract.providerName}\n </p>\n <div className=\"mt-1 flex flex-wrap gap-1\">\n {contract.servicesIncluded.slice(0, 3).map((service, idx) => (\n <span\n key={idx}\n className=\"inline-block rounded-md bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\"\n >\n {service}\n </span>\n ))}\n {contract.servicesIncluded.length > 3 && (\n <span className=\"inline-block rounded-md bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\">\n +{contract.servicesIncluded.length - 3} more\n </span>\n )}\n </div>\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n <div className=\"text-right\">\n <p className=\"text-sm font-medium text-text-primary\">{formatDate(contract.nextServiceDate)}</p>\n <p\n className={cn(\n 'text-xs',\n isPast\n ? 'text-red-600 dark:text-red-400'\n : isUrgent\n ? 'text-amber-600 dark:text-amber-400'\n : 'text-text-secondary'\n )}\n >\n {isPast\n ? `${Math.abs(daysUntil)} days overdue`\n : daysUntil === 0\n ? 'Today'\n : `In ${daysUntil} days`}\n </p>\n </div>\n <StatusBadge\n label={contract.status.replace('_', ' ')}\n variant={getStatusVariant(contract.status)}\n />\n </div>\n </div>\n );\n })}\n </div>\n )}\n </div>\n\n {/* Contracts Table */}\n <div>\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">All Contracts</h2>\n <FilterBar\n searchValue={search}\n onSearchChange={setSearch}\n searchPlaceholder=\"Search contracts, providers, properties...\"\n />\n <div className=\"mt-4\">\n {filtered.length > 0 ? (\n <DataTable columns={contractColumns} data={filtered} keyField=\"id\" pageSize={10} />\n ) : (\n <EmptyState\n illustration=\"empty-search\"\n icon={<Shield className=\"h-12 w-12\" />}\n title=\"No contracts found\"\n description=\"Try adjusting your search criteria or create a new contract.\"\n action={\n <button className=\"inline-flex items-center gap-2 rounded-md bg-foreground px-4 py-2 text-sm font-medium text-background transition-colors hover:bg-foreground/90\">\n <Plus className=\"h-4 w-4\" />\n New Contract\n </button>\n }\n />\n )}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AMCManagementPage.js","names":[],"sources":["../../../../src/housingvista/pages/amc/AMCManagementPage.tsx"],"sourcesContent":["import { useState, useMemo, type FormEvent } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Shield, DollarSign, Calendar, Plus, Download, Clock, AlertTriangle, Wrench, RotateCcw } from 'lucide-react';\nimport {\n Button,\n EmphasisPanel,\n NextSteps,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components';\nimport { StatCard } from '../../components';\nimport { DataTable, type Column } from '../../components';\nimport { StatusBadge } from '../../components';\nimport { FilterBar } from '../../components';\nimport { EmptyState } from '../../components';\nimport { useAMCContracts, useCreateAmcContract } from '../../hooks';\nimport { cn, formatCurrency, formatDate } from '../../utils/helpers';\nimport { notify } from '../../lib/toast';\nimport type { AMCContract } from '../../types';\n\nconst inputClass =\n 'h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\ntype AmcStatus = 'active' | 'expiring_soon' | 'expired' | 'cancelled';\n\nfunction toIso(value: string): string {\n return value ? new Date(value).toISOString() : '';\n}\n\nfunction getStatusVariant(status: AMCContract['status']): 'success' | 'warning' | 'error' | 'muted' {\n switch (status) {\n case 'active':\n return 'success';\n case 'expiring_soon':\n return 'warning';\n case 'expired':\n return 'error';\n case 'cancelled':\n return 'muted';\n default:\n return 'muted';\n }\n}\n\nexport function AMCManagementPage() {\n const navigate = useNavigate();\n const { data: amcContracts } = useAMCContracts();\n const [search, setSearch] = useState('');\n\n const { createAmcContract, loading: creating } = useCreateAmcContract();\n\n const [createOpen, setCreateOpen] = useState(false);\n const [propertyTitle, setPropertyTitle] = useState('');\n const [packageName, setPackageName] = useState('');\n const [providerName, setProviderName] = useState('');\n const [monthlyCost, setMonthlyCost] = useState('');\n const [startDate, setStartDate] = useState('');\n const [endDate, setEndDate] = useState('');\n const [status, setStatus] = useState<AmcStatus>('active');\n const [servicesIncluded, setServicesIncluded] = useState('');\n\n const resetForm = () => {\n setPropertyTitle('');\n setPackageName('');\n setProviderName('');\n setMonthlyCost('');\n setStartDate('');\n setEndDate('');\n setStatus('active');\n setServicesIncluded('');\n };\n\n const openCreate = () => {\n resetForm();\n setCreateOpen(true);\n };\n\n const handleCreate = async (e: FormEvent) => {\n e.preventDefault();\n const cost = Number(monthlyCost);\n if (!propertyTitle.trim() || !packageName.trim() || !providerName.trim()) {\n notify.warning('Missing details', 'Property, package and provider are required.');\n return;\n }\n if (!Number.isFinite(cost) || cost <= 0) {\n notify.warning('Enter a valid monthly cost');\n return;\n }\n const services = servicesIncluded\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n try {\n await createAmcContract({\n propertyTitle: propertyTitle.trim(),\n packageName: packageName.trim(),\n providerName: providerName.trim(),\n monthlyCost: cost,\n startDate: toIso(startDate),\n endDate: toIso(endDate),\n status,\n servicesIncluded: services.length > 0 ? services : undefined,\n });\n notify.success('Contract created', `${packageName.trim()} added for ${propertyTitle.trim()}.`);\n setCreateOpen(false);\n resetForm();\n } catch (err) {\n notify.error('Could not create contract', (err as Error).message);\n }\n };\n\n const filtered = useMemo(() => {\n if (!search) return amcContracts;\n const q = search.toLowerCase();\n return amcContracts.filter(\n (c) =>\n c.propertyTitle.toLowerCase().includes(q) ||\n c.providerName.toLowerCase().includes(q) ||\n c.packageName.toLowerCase().includes(q)\n );\n }, [amcContracts, search]);\n\n const stats = useMemo(() => {\n const activeContracts = amcContracts.filter((c) => c.status === 'active' || c.status === 'expiring_soon').length;\n const pendingRenewals = amcContracts.filter((c) => c.status === 'expiring_soon').length;\n const totalMonthlySpent = amcContracts\n .filter((c) => c.status === 'active' || c.status === 'expiring_soon')\n .reduce((sum, c) => sum + c.monthlyCost, 0);\n const upcomingServices = amcContracts.filter((c) => {\n if (!c.nextServiceDate) return false;\n const serviceDate = new Date(c.nextServiceDate);\n const now = new Date();\n const daysUntil = (serviceDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24);\n return daysUntil >= 0 && daysUntil <= 30;\n }).length;\n return { activeContracts, pendingRenewals, totalMonthlySpent, upcomingServices };\n }, [amcContracts]);\n\n const scheduledServices = useMemo(\n () =>\n amcContracts\n .filter((c) => c.nextServiceDate && (c.status === 'active' || c.status === 'expiring_soon'))\n .sort((a, b) => new Date(a.nextServiceDate).getTime() - new Date(b.nextServiceDate).getTime()),\n [amcContracts]\n );\n\n const contractColumns: Column<AMCContract>[] = [\n {\n key: 'packageName',\n header: 'Contract',\n render: (item) => (\n <div>\n <div className=\"font-medium text-text-primary\">{item.packageName}</div>\n <div className=\"text-xs text-text-secondary\">{item.propertyTitle}</div>\n </div>\n ),\n },\n {\n key: 'providerName',\n header: 'Provider',\n render: (item) => <span className=\"text-sm text-text-primary\">{item.providerName}</span>,\n },\n {\n key: 'servicesIncluded',\n header: 'Coverage',\n render: (item) => (\n <div className=\"max-w-[200px]\">\n <span className=\"text-sm text-text-primary\">{item.servicesIncluded.length} services</span>\n <p className=\"truncate text-xs text-text-secondary\">\n {item.servicesIncluded.slice(0, 2).join(', ')}\n {item.servicesIncluded.length > 2 && ` +${item.servicesIncluded.length - 2} more`}\n </p>\n </div>\n ),\n },\n {\n key: 'monthlyCost',\n header: 'Cost',\n render: (item) => (\n <div>\n <span className=\"font-medium text-text-primary\">{formatCurrency(item.monthlyCost)}</span>\n <span className=\"text-xs text-text-secondary\">/mo</span>\n </div>\n ),\n },\n {\n key: 'startDate',\n header: 'Start',\n render: (item) => <span className=\"text-sm text-text-primary\">{formatDate(item.startDate)}</span>,\n },\n {\n key: 'endDate',\n header: 'End',\n render: (item) => <span className=\"text-sm text-text-primary\">{formatDate(item.endDate)}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (item) => (\n <StatusBadge label={item.status.replace('_', ' ')} variant={getStatusVariant(item.status)} dot />\n ),\n },\n ];\n\n return (\n <PageLayout\n title=\"AMC Management\"\n description=\"Manage Annual Maintenance Contracts and service packages\"\n purpose=\"Keep your properties maintained without surprises. Track every Annual Maintenance Contract, see what each covers and costs, get ahead of renewals, and know which services are due in the next 30 days so nothing slips.\"\n actions={\n <div className=\"flex items-center gap-2\">\n <button className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\">\n <Download className=\"h-4 w-4\" />\n Export\n </button>\n <Button type=\"button\" onClick={openCreate} className=\"gap-2\">\n <Plus className=\"h-4 w-4\" />\n New Contract\n </Button>\n </div>\n }\n >\n {/* Stats — primary insight zone (one EmphasisPanel per page) */}\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard label=\"Active Contracts\" value={stats.activeContracts} icon={<Shield className=\"h-4 w-4\" />} />\n <StatCard\n label=\"Pending Renewals\"\n value={stats.pendingRenewals}\n icon={<RotateCcw className=\"h-4 w-4\" />}\n trend={stats.pendingRenewals > 0 ? 'up' : 'stable'}\n change={stats.pendingRenewals > 0 ? stats.pendingRenewals : 0}\n />\n <StatCard\n label=\"Total Monthly Spent\"\n value={stats.totalMonthlySpent}\n prefix=\"$\"\n icon={<DollarSign className=\"h-4 w-4\" />}\n />\n <StatCard label=\"Upcoming Services\" value={stats.upcomingServices} icon={<Calendar className=\"h-4 w-4\" />} />\n </div>\n </EmphasisPanel>\n\n <NextSteps\n storageKey=\"housingvista-amc\"\n steps={[\n {\n id: 'pick-property',\n label: 'Add coverage for a property',\n description: 'Choose a property to set up a new maintenance contract.',\n onClick: () => navigate('/properties'),\n },\n {\n id: 'compare-providers',\n label: 'Compare your providers',\n description: 'Search by provider name to weigh coverage and monthly cost.',\n onClick: () => {\n const firstProvider = amcContracts[0]?.providerName;\n if (firstProvider) setSearch(firstProvider);\n },\n },\n {\n id: 'check-legal',\n label: 'Review contract paperwork',\n description: 'Open the legal hub to manage the underlying documents.',\n onClick: () => navigate('/legal'),\n },\n ]}\n />\n\n {/* Service Schedule Section */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5 shadow-sm\">\n <h3 className=\"mb-4 text-lg font-semibold text-text-primary\">Upcoming Service Schedule</h3>\n {scheduledServices.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No services scheduled\"\n description=\"Once your active contracts have upcoming service dates, they'll be listed here in order of priority.\"\n className=\"border-0 bg-transparent p-6\"\n />\n ) : (\n <div className=\"space-y-3\">\n {scheduledServices.map((contract) => {\n const serviceDate = new Date(contract.nextServiceDate);\n const now = new Date();\n const daysUntil = Math.ceil((serviceDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));\n const isUrgent = daysUntil <= 7;\n const isPast = daysUntil < 0;\n\n return (\n <div\n key={contract.id}\n className=\"flex flex-col gap-3 rounded-lg bg-bg-sunken/50 p-4 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"flex items-start gap-3\">\n <div\n className={cn(\n 'rounded-lg p-2',\n isPast\n ? 'bg-status-error-bg-subtle'\n : isUrgent\n ? 'bg-status-warning-bg-subtle'\n : 'bg-status-info-bg-subtle'\n )}\n >\n {isPast ? (\n <AlertTriangle className=\"h-4 w-4 text-status-error-text\" />\n ) : isUrgent ? (\n <Clock className=\"h-4 w-4 text-status-warning-text\" />\n ) : (\n <Wrench className=\"h-4 w-4 text-status-info-text\" />\n )}\n </div>\n <div>\n <p className=\"font-medium text-text-primary\">{contract.propertyTitle}</p>\n <p className=\"text-sm text-text-secondary\">\n {contract.packageName} · {contract.providerName}\n </p>\n <div className=\"mt-1 flex flex-wrap gap-1\">\n {contract.servicesIncluded.slice(0, 3).map((service, idx) => (\n <span\n key={idx}\n className=\"inline-block rounded-md bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\"\n >\n {service}\n </span>\n ))}\n {contract.servicesIncluded.length > 3 && (\n <span className=\"inline-block rounded-md bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\">\n +{contract.servicesIncluded.length - 3} more\n </span>\n )}\n </div>\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n <div className=\"text-right\">\n <p className=\"text-sm font-medium text-text-primary\">{formatDate(contract.nextServiceDate)}</p>\n <p\n className={cn(\n 'text-xs',\n isPast\n ? 'text-status-error-text'\n : isUrgent\n ? 'text-status-warning-text'\n : 'text-text-secondary'\n )}\n >\n {isPast\n ? `${Math.abs(daysUntil)} days overdue`\n : daysUntil === 0\n ? 'Today'\n : `In ${daysUntil} days`}\n </p>\n </div>\n <StatusBadge\n label={contract.status.replace('_', ' ')}\n variant={getStatusVariant(contract.status)}\n />\n </div>\n </div>\n );\n })}\n </div>\n )}\n </div>\n\n {/* Contracts Table */}\n <div>\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">All Contracts</h2>\n <FilterBar\n searchValue={search}\n onSearchChange={setSearch}\n searchPlaceholder=\"Search contracts, providers, properties...\"\n />\n <div className=\"mt-4\">\n {filtered.length > 0 ? (\n <DataTable columns={contractColumns} data={filtered} keyField=\"id\" pageSize={10} />\n ) : (\n <EmptyState\n illustration=\"empty-search\"\n icon={<Shield className=\"h-12 w-12\" />}\n title=\"No contracts found\"\n description=\"Try adjusting your search criteria or create a new contract.\"\n action={\n <Button type=\"button\" onClick={openCreate} className=\"gap-2\">\n <Plus className=\"h-4 w-4\" />\n New Contract\n </Button>\n }\n />\n )}\n </div>\n </div>\n\n {/* New Contract dialog */}\n <ResponsiveDialog open={createOpen} onOpenChange={setCreateOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <form onSubmit={handleCreate}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New maintenance contract</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Set up an Annual Maintenance Contract to track coverage, cost and renewal dates.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Property *</label>\n <input\n type=\"text\"\n required\n placeholder=\"e.g. Maple Court\"\n value={propertyTitle}\n onChange={(e) => setPropertyTitle(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Package *</label>\n <input\n type=\"text\"\n required\n placeholder=\"e.g. Premium Care\"\n value={packageName}\n onChange={(e) => setPackageName(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Provider *</label>\n <input\n type=\"text\"\n required\n placeholder=\"Service company\"\n value={providerName}\n onChange={(e) => setProviderName(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Monthly cost *</label>\n <input\n type=\"number\"\n required\n min={0}\n step=\"0.01\"\n placeholder=\"0.00\"\n value={monthlyCost}\n onChange={(e) => setMonthlyCost(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Start date</label>\n <input\n type=\"date\"\n value={startDate}\n onChange={(e) => setStartDate(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">End date</label>\n <input\n type=\"date\"\n value={endDate}\n onChange={(e) => setEndDate(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Status</label>\n <select\n value={status}\n onChange={(e) => setStatus(e.target.value as AmcStatus)}\n className={inputClass}\n >\n <option value=\"active\">Active</option>\n <option value=\"expiring_soon\">Expiring soon</option>\n <option value=\"expired\">Expired</option>\n <option value=\"cancelled\">Cancelled</option>\n </select>\n </div>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Services included</label>\n <input\n type=\"text\"\n placeholder=\"Comma-separated, e.g. Plumbing, Electrical, HVAC\"\n value={servicesIncluded}\n onChange={(e) => setServicesIncluded(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setCreateOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={creating}>\n {creating ? 'Saving…' : 'Create contract'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|