@burdenoff/microfe-housingvista 2026.709.2 → 2026.709.3
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/HousingVistaRoutes.d.ts.map +1 -1
- package/dist/housingvista/HousingVistaRoutes.js +136 -37
- package/dist/housingvista/HousingVistaRoutes.js.map +1 -1
- package/dist/housingvista/hooks/useHousingVistaAnalytics.d.ts +1 -1
- package/dist/housingvista/hooks/useHousingVistaAnalytics.d.ts.map +1 -1
- package/dist/housingvista/hooks/useHousingVistaAnalytics.js +4 -4
- package/dist/housingvista/hooks/useHousingVistaAnalytics.js.map +1 -1
- package/dist/housingvista/pages/amc/AMCManagementPage.d.ts.map +1 -1
- package/dist/housingvista/pages/amc/AMCManagementPage.js +62 -60
- package/dist/housingvista/pages/amc/AMCManagementPage.js.map +1 -1
- package/dist/housingvista/pages/assets/AssetDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/assets/AssetDetailPage.js +141 -138
- package/dist/housingvista/pages/assets/AssetDetailPage.js.map +1 -1
- package/dist/housingvista/pages/assets/AssetManagementPage.d.ts.map +1 -1
- package/dist/housingvista/pages/assets/AssetManagementPage.js +193 -191
- package/dist/housingvista/pages/assets/AssetManagementPage.js.map +1 -1
- package/dist/housingvista/pages/chat/ChatPage.d.ts.map +1 -1
- package/dist/housingvista/pages/chat/ChatPage.js +115 -113
- package/dist/housingvista/pages/chat/ChatPage.js.map +1 -1
- package/dist/housingvista/pages/finance/HomeLoanDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/finance/HomeLoanDetailPage.js +152 -149
- package/dist/housingvista/pages/finance/HomeLoanDetailPage.js.map +1 -1
- package/dist/housingvista/pages/finance/HomeLoansPage.d.ts.map +1 -1
- package/dist/housingvista/pages/finance/HomeLoansPage.js +141 -139
- package/dist/housingvista/pages/finance/HomeLoansPage.js.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentDetailPage.js +290 -287
- package/dist/housingvista/pages/investments/InvestmentDetailPage.js.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentsPage.d.ts.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentsPage.js +64 -62
- package/dist/housingvista/pages/investments/InvestmentsPage.js.map +1 -1
- package/dist/housingvista/pages/legal/LegalPage.d.ts.map +1 -1
- package/dist/housingvista/pages/legal/LegalPage.js +95 -93
- package/dist/housingvista/pages/legal/LegalPage.js.map +1 -1
- package/dist/housingvista/pages/legal/LegalServiceDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/legal/LegalServiceDetailPage.js +129 -126
- package/dist/housingvista/pages/legal/LegalServiceDetailPage.js.map +1 -1
- package/dist/housingvista/pages/market/AreaInsightsPage.d.ts.map +1 -1
- package/dist/housingvista/pages/market/AreaInsightsPage.js +99 -97
- package/dist/housingvista/pages/market/AreaInsightsPage.js.map +1 -1
- package/dist/housingvista/pages/market/MarketAnalyticsPage.d.ts.map +1 -1
- package/dist/housingvista/pages/market/MarketAnalyticsPage.js +138 -136
- package/dist/housingvista/pages/market/MarketAnalyticsPage.js.map +1 -1
- package/dist/housingvista/pages/marketplace/MarketplacePage.d.ts.map +1 -1
- package/dist/housingvista/pages/marketplace/MarketplacePage.js +99 -97
- package/dist/housingvista/pages/marketplace/MarketplacePage.js.map +1 -1
- package/dist/housingvista/pages/marketplace/ServiceDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/marketplace/ServiceDetailPage.js +137 -134
- package/dist/housingvista/pages/marketplace/ServiceDetailPage.js.map +1 -1
- package/dist/housingvista/pages/offers/OffersPage.d.ts.map +1 -1
- package/dist/housingvista/pages/offers/OffersPage.js +94 -92
- package/dist/housingvista/pages/offers/OffersPage.js.map +1 -1
- package/dist/housingvista/pages/properties/AddPropertyPage.d.ts.map +1 -1
- package/dist/housingvista/pages/properties/AddPropertyPage.js +280 -279
- package/dist/housingvista/pages/properties/AddPropertyPage.js.map +1 -1
- package/dist/housingvista/pages/rental/RentalManagementPage.d.ts.map +1 -1
- package/dist/housingvista/pages/rental/RentalManagementPage.js +153 -151
- package/dist/housingvista/pages/rental/RentalManagementPage.js.map +1 -1
- package/dist/housingvista/pages/settings/ProfilePage.d.ts.map +1 -1
- package/dist/housingvista/pages/settings/ProfilePage.js +52 -50
- package/dist/housingvista/pages/settings/ProfilePage.js.map +1 -1
- package/dist/housingvista/pages/settings/SettingsPage.d.ts.map +1 -1
- package/dist/housingvista/pages/settings/SettingsPage.js +54 -52
- package/dist/housingvista/pages/settings/SettingsPage.js.map +1 -1
- package/dist/housingvista/pages/shortlist/ShortlistPage.d.ts.map +1 -1
- package/dist/housingvista/pages/shortlist/ShortlistPage.js +43 -41
- package/dist/housingvista/pages/shortlist/ShortlistPage.js.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionDetailPage.js +211 -208
- package/dist/housingvista/pages/transactions/TransactionDetailPage.js.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionsPage.d.ts.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionsPage.js +85 -83
- package/dist/housingvista/pages/transactions/TransactionsPage.js.map +1 -1
- package/dist/housingvista/pages/verification/VerificationDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/verification/VerificationDetailPage.js +25 -22
- package/dist/housingvista/pages/verification/VerificationDetailPage.js.map +1 -1
- package/dist/housingvista/pages/verification/VerificationPage.d.ts.map +1 -1
- package/dist/housingvista/pages/verification/VerificationPage.js +123 -121
- package/dist/housingvista/pages/verification/VerificationPage.js.map +1 -1
- package/dist/housingvista/pages/viewings/ViewingsPage.d.ts.map +1 -1
- package/dist/housingvista/pages/viewings/ViewingsPage.js +128 -126
- package/dist/housingvista/pages/viewings/ViewingsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,15 +3,16 @@ import { PageLayout as n } from "../../components/PageLayout.js";
|
|
|
3
3
|
import { DataTable as r } from "../../components/DataTable.js";
|
|
4
4
|
import { StatusBadge as i } from "../../components/StatusBadge.js";
|
|
5
5
|
import { FilterBar as a } from "../../components/FilterBar.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { useTrackPageView as o } from "../../hooks/useHousingVistaAnalytics.js";
|
|
7
|
+
import { useApproveVerification as s, useRejectVerification as c, useVerifications as l } from "../../hooks/index.js";
|
|
8
|
+
import { notify as u } from "../../lib/toast.js";
|
|
8
9
|
import "../../components/index.js";
|
|
9
|
-
import { useServerPagination as
|
|
10
|
-
import { useEffect as
|
|
11
|
-
import { Check as
|
|
12
|
-
import { useNavigate as
|
|
13
|
-
import { Fragment as
|
|
14
|
-
import { Button as
|
|
10
|
+
import { useServerPagination as ee } from "../../hooks/useServerPagination.js";
|
|
11
|
+
import { useEffect as te, useState as d } from "react";
|
|
12
|
+
import { Check as ne, CheckCircle as re, Clock as f, Eye as ie, Shield as p, X as m, XCircle as h } from "lucide-react";
|
|
13
|
+
import { useNavigate as g } from "react-router-dom";
|
|
14
|
+
import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
15
|
+
import { Button as b, EmphasisPanel as x, ResponsiveDialog as S, ResponsiveDialogContent as C, ResponsiveDialogDescription as w, ResponsiveDialogFooter as T, ResponsiveDialogHeader as ae, ResponsiveDialogTitle as oe } from "@burdenoff/fe-libs/ui";
|
|
15
16
|
//#region src/housingvista/pages/verification/VerificationPage.tsx
|
|
16
17
|
function se(e) {
|
|
17
18
|
switch (e) {
|
|
@@ -28,18 +29,18 @@ function ce(e) {
|
|
|
28
29
|
function le(e) {
|
|
29
30
|
return e.replace("_", " ").replace(/\b\w/g, (e) => e.toUpperCase());
|
|
30
31
|
}
|
|
31
|
-
function
|
|
32
|
+
function E(t) {
|
|
32
33
|
let n = {
|
|
33
34
|
high: "bg-status-error-bg-subtle text-status-error-text",
|
|
34
35
|
medium: "bg-status-warning-bg-subtle text-status-warning-text",
|
|
35
36
|
low: "bg-status-success-bg-subtle text-status-success-text"
|
|
36
37
|
};
|
|
37
|
-
return /* @__PURE__ */
|
|
38
|
+
return /* @__PURE__ */ v("span", {
|
|
38
39
|
className: e("inline-flex rounded-full px-2 py-0.5 text-xs font-medium capitalize", n[t] ?? n.low),
|
|
39
40
|
children: t
|
|
40
41
|
});
|
|
41
42
|
}
|
|
42
|
-
var
|
|
43
|
+
var D = [
|
|
43
44
|
{
|
|
44
45
|
value: "",
|
|
45
46
|
label: "All statuses"
|
|
@@ -60,7 +61,7 @@ var E = [
|
|
|
60
61
|
value: "rejected",
|
|
61
62
|
label: "Rejected"
|
|
62
63
|
}
|
|
63
|
-
],
|
|
64
|
+
], O = [
|
|
64
65
|
{
|
|
65
66
|
value: "",
|
|
66
67
|
label: "All priorities"
|
|
@@ -77,7 +78,7 @@ var E = [
|
|
|
77
78
|
value: "low",
|
|
78
79
|
label: "Low"
|
|
79
80
|
}
|
|
80
|
-
],
|
|
81
|
+
], k = [
|
|
81
82
|
{
|
|
82
83
|
value: "",
|
|
83
84
|
label: "All types"
|
|
@@ -102,8 +103,8 @@ var E = [
|
|
|
102
103
|
value: "bank_account",
|
|
103
104
|
label: "Bank Account"
|
|
104
105
|
}
|
|
105
|
-
],
|
|
106
|
-
function
|
|
106
|
+
], A = "h-9 rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]";
|
|
107
|
+
function j({ label: t, value: n, total: r, tone: i, icon: a }) {
|
|
107
108
|
let o = r > 0 ? Math.round(n / r * 100) : 0, s = {
|
|
108
109
|
success: "bg-status-success-text",
|
|
109
110
|
warning: "bg-status-warning-text",
|
|
@@ -115,26 +116,26 @@ function A({ label: t, value: n, total: r, tone: i, icon: a }) {
|
|
|
115
116
|
error: "bg-status-error-bg-subtle text-status-error-text",
|
|
116
117
|
info: "bg-status-info-bg-subtle text-status-info-text"
|
|
117
118
|
}[i];
|
|
118
|
-
return /* @__PURE__ */
|
|
119
|
+
return /* @__PURE__ */ y("div", {
|
|
119
120
|
className: "rounded-card border border-border-seam bg-bg-surface p-4 shadow-elevation-1",
|
|
120
121
|
children: [
|
|
121
|
-
/* @__PURE__ */
|
|
122
|
+
/* @__PURE__ */ y("div", {
|
|
122
123
|
className: "flex items-center justify-between",
|
|
123
|
-
children: [/* @__PURE__ */
|
|
124
|
+
children: [/* @__PURE__ */ v("span", {
|
|
124
125
|
className: "text-2xl font-bold tabular-nums text-text-primary",
|
|
125
126
|
children: n
|
|
126
|
-
}), /* @__PURE__ */
|
|
127
|
+
}), /* @__PURE__ */ v("span", {
|
|
127
128
|
className: e("flex h-8 w-8 items-center justify-center rounded-lg", c),
|
|
128
129
|
children: a
|
|
129
130
|
})]
|
|
130
131
|
}),
|
|
131
|
-
/* @__PURE__ */
|
|
132
|
+
/* @__PURE__ */ v("p", {
|
|
132
133
|
className: "mt-0.5 text-sm font-medium text-text-secondary",
|
|
133
134
|
children: t
|
|
134
135
|
}),
|
|
135
|
-
/* @__PURE__ */
|
|
136
|
+
/* @__PURE__ */ v("div", {
|
|
136
137
|
className: "mt-3 h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken",
|
|
137
|
-
children: /* @__PURE__ */
|
|
138
|
+
children: /* @__PURE__ */ v("div", {
|
|
138
139
|
className: e("h-full rounded-full transition-all", s),
|
|
139
140
|
style: { width: `${o}%` }
|
|
140
141
|
})
|
|
@@ -142,46 +143,47 @@ function A({ label: t, value: n, total: r, tone: i, icon: a }) {
|
|
|
142
143
|
]
|
|
143
144
|
});
|
|
144
145
|
}
|
|
145
|
-
function
|
|
146
|
-
|
|
147
|
-
d(()
|
|
146
|
+
function M() {
|
|
147
|
+
o("housingvista.verification.viewed");
|
|
148
|
+
let e = g(), M = ee(10), [N, P] = d(""), [F, I] = d(""), [L, R] = d(""), [z, B] = d(""), [V, H] = d(""), { setPage: U } = M;
|
|
149
|
+
te(() => {
|
|
148
150
|
let e = setTimeout(() => {
|
|
149
|
-
|
|
151
|
+
I(N.trim()), U(1);
|
|
150
152
|
}, 300);
|
|
151
153
|
return () => clearTimeout(e);
|
|
152
|
-
}, [
|
|
153
|
-
let { data:
|
|
154
|
+
}, [N, U]);
|
|
155
|
+
let { data: W, loading: ue } = l({
|
|
154
156
|
filter: {
|
|
155
|
-
search:
|
|
156
|
-
status:
|
|
157
|
-
priority:
|
|
158
|
-
entityType:
|
|
157
|
+
search: F || void 0,
|
|
158
|
+
status: L || void 0,
|
|
159
|
+
priority: z || void 0,
|
|
160
|
+
entityType: V || void 0
|
|
159
161
|
},
|
|
160
|
-
pagination:
|
|
161
|
-
}), { approveVerification: de, loading: fe } =
|
|
162
|
-
|
|
162
|
+
pagination: M.pagination
|
|
163
|
+
}), { approveVerification: de, loading: fe } = s(), { rejectVerification: pe, loading: me } = c(), [G, K] = d(null), [q, J] = d(""), Y = fe || me, X = (e, t) => {
|
|
164
|
+
K({
|
|
163
165
|
item: e,
|
|
164
166
|
mode: t
|
|
165
|
-
}),
|
|
166
|
-
},
|
|
167
|
-
|
|
167
|
+
}), J("");
|
|
168
|
+
}, Z = () => {
|
|
169
|
+
Y || (K(null), J(""));
|
|
168
170
|
}, he = async (e) => {
|
|
169
|
-
if (e.preventDefault(), !
|
|
170
|
-
let t =
|
|
171
|
+
if (e.preventDefault(), !G) return;
|
|
172
|
+
let t = q.trim() || void 0;
|
|
171
173
|
try {
|
|
172
|
-
|
|
174
|
+
G.mode === "approve" ? (await de(G.item.id, t), u.success("Verification approved", G.item.entityLabel)) : (await pe(G.item.id, t), u.success("Verification rejected", G.item.entityLabel)), K(null), J("");
|
|
173
175
|
} catch (e) {
|
|
174
176
|
let t = e instanceof Error ? e.message : "Please try again.";
|
|
175
|
-
|
|
177
|
+
u.error(G.mode === "approve" ? "Could not approve verification" : "Could not reject verification", t);
|
|
176
178
|
}
|
|
177
|
-
}, ge =
|
|
179
|
+
}, ge = W.filter((e) => e.status === "verified").length, _e = W.filter((e) => e.status === "pending").length, ve = W.filter((e) => e.status === "in_progress").length, ye = W.filter((e) => e.status === "rejected").length, Q = W.length, be = [
|
|
178
180
|
{
|
|
179
181
|
key: "entityLabel",
|
|
180
182
|
header: "Item",
|
|
181
|
-
render: (e) => /* @__PURE__ */
|
|
183
|
+
render: (e) => /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
|
|
182
184
|
className: "font-medium text-text-primary",
|
|
183
185
|
children: e.entityLabel
|
|
184
|
-
}), e.notes && /* @__PURE__ */
|
|
186
|
+
}), e.notes && /* @__PURE__ */ v("p", {
|
|
185
187
|
className: "mt-0.5 text-xs text-text-secondary line-clamp-1",
|
|
186
188
|
children: e.notes
|
|
187
189
|
})] })
|
|
@@ -189,7 +191,7 @@ function j() {
|
|
|
189
191
|
{
|
|
190
192
|
key: "entityType",
|
|
191
193
|
header: "Type",
|
|
192
|
-
render: (e) => /* @__PURE__ */
|
|
194
|
+
render: (e) => /* @__PURE__ */ v("span", {
|
|
193
195
|
className: "inline-flex items-center gap-1 text-xs text-text-secondary capitalize",
|
|
194
196
|
children: le(e.entityType)
|
|
195
197
|
})
|
|
@@ -197,12 +199,12 @@ function j() {
|
|
|
197
199
|
{
|
|
198
200
|
key: "priority",
|
|
199
201
|
header: "Priority",
|
|
200
|
-
render: (e) =>
|
|
202
|
+
render: (e) => E(e.priority)
|
|
201
203
|
},
|
|
202
204
|
{
|
|
203
205
|
key: "submittedDate",
|
|
204
206
|
header: "Submitted",
|
|
205
|
-
render: (e) => /* @__PURE__ */
|
|
207
|
+
render: (e) => /* @__PURE__ */ v("span", {
|
|
206
208
|
className: "text-sm text-text-secondary",
|
|
207
209
|
children: t(e.submittedDate)
|
|
208
210
|
})
|
|
@@ -210,7 +212,7 @@ function j() {
|
|
|
210
212
|
{
|
|
211
213
|
key: "status",
|
|
212
214
|
header: "Status",
|
|
213
|
-
render: (e) => /* @__PURE__ */
|
|
215
|
+
render: (e) => /* @__PURE__ */ v(i, {
|
|
214
216
|
label: ce(e.status),
|
|
215
217
|
variant: se(e.status),
|
|
216
218
|
dot: !0
|
|
@@ -222,178 +224,178 @@ function j() {
|
|
|
222
224
|
sortable: !1,
|
|
223
225
|
render: (t) => {
|
|
224
226
|
let n = t.status === "pending" || t.status === "in_progress";
|
|
225
|
-
return /* @__PURE__ */
|
|
227
|
+
return /* @__PURE__ */ y("div", {
|
|
226
228
|
className: "flex items-center gap-1.5",
|
|
227
|
-
children: [/* @__PURE__ */
|
|
229
|
+
children: [/* @__PURE__ */ y("button", {
|
|
228
230
|
onClick: (n) => {
|
|
229
231
|
n.stopPropagation(), e(`../verification/${t.id}`);
|
|
230
232
|
},
|
|
231
233
|
className: "inline-flex items-center gap-1 rounded-md border border-border-subtle px-2.5 py-1 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken",
|
|
232
|
-
children: [/* @__PURE__ */
|
|
233
|
-
}), n && /* @__PURE__ */
|
|
234
|
+
children: [/* @__PURE__ */ v(ie, { className: "h-3 w-3" }), "View"]
|
|
235
|
+
}), n && /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ y("button", {
|
|
234
236
|
onClick: (e) => {
|
|
235
|
-
e.stopPropagation(),
|
|
237
|
+
e.stopPropagation(), X(t, "approve");
|
|
236
238
|
},
|
|
237
239
|
className: "inline-flex items-center gap-1 rounded-md border border-status-success-border bg-status-success-bg-subtle px-2.5 py-1 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle/70",
|
|
238
|
-
children: [/* @__PURE__ */
|
|
239
|
-
}), /* @__PURE__ */
|
|
240
|
+
children: [/* @__PURE__ */ v(ne, { className: "h-3 w-3" }), "Approve"]
|
|
241
|
+
}), /* @__PURE__ */ y("button", {
|
|
240
242
|
onClick: (e) => {
|
|
241
|
-
e.stopPropagation(),
|
|
243
|
+
e.stopPropagation(), X(t, "reject");
|
|
242
244
|
},
|
|
243
245
|
className: "inline-flex items-center gap-1 rounded-md border border-status-error-border bg-status-error-bg-subtle px-2.5 py-1 text-xs font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle/70",
|
|
244
|
-
children: [/* @__PURE__ */
|
|
246
|
+
children: [/* @__PURE__ */ v(m, { className: "h-3 w-3" }), "Reject"]
|
|
245
247
|
})] })]
|
|
246
248
|
});
|
|
247
249
|
}
|
|
248
250
|
}
|
|
249
|
-
], $ =
|
|
250
|
-
return /* @__PURE__ */
|
|
251
|
+
], $ = G?.mode === "approve";
|
|
252
|
+
return /* @__PURE__ */ y(n, {
|
|
251
253
|
title: "Verification Center",
|
|
252
254
|
description: "Track and manage all your verification requests",
|
|
253
255
|
purpose: "Submit and monitor identity, ownership, and document checks that build trust in your listings and deals. Use this page to see what is verified, what is still pending, and what was rejected so you can act before it blocks a transaction.",
|
|
254
|
-
actions: /* @__PURE__ */
|
|
256
|
+
actions: /* @__PURE__ */ y("span", {
|
|
255
257
|
className: "inline-flex items-center gap-1.5 rounded-full bg-status-success-bg-subtle px-3 py-1.5 text-xs font-medium text-status-success-text",
|
|
256
|
-
children: [/* @__PURE__ */
|
|
258
|
+
children: [/* @__PURE__ */ v(p, { className: "h-3.5 w-3.5" }), "Trust & Safety"]
|
|
257
259
|
}),
|
|
258
260
|
children: [
|
|
259
|
-
/* @__PURE__ */
|
|
261
|
+
/* @__PURE__ */ v(x, {
|
|
260
262
|
className: "p-4",
|
|
261
|
-
children: /* @__PURE__ */
|
|
263
|
+
children: /* @__PURE__ */ y("div", {
|
|
262
264
|
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
263
265
|
children: [
|
|
264
|
-
/* @__PURE__ */
|
|
266
|
+
/* @__PURE__ */ v(j, {
|
|
265
267
|
label: "Verified",
|
|
266
268
|
value: ge,
|
|
267
269
|
total: Q,
|
|
268
270
|
tone: "success",
|
|
269
|
-
icon: /* @__PURE__ */
|
|
271
|
+
icon: /* @__PURE__ */ v(re, { className: "h-4 w-4" })
|
|
270
272
|
}),
|
|
271
|
-
/* @__PURE__ */
|
|
273
|
+
/* @__PURE__ */ v(j, {
|
|
272
274
|
label: "Pending",
|
|
273
275
|
value: _e + ve,
|
|
274
276
|
total: Q,
|
|
275
277
|
tone: "warning",
|
|
276
|
-
icon: /* @__PURE__ */
|
|
278
|
+
icon: /* @__PURE__ */ v(f, { className: "h-4 w-4" })
|
|
277
279
|
}),
|
|
278
|
-
/* @__PURE__ */
|
|
280
|
+
/* @__PURE__ */ v(j, {
|
|
279
281
|
label: "Rejected",
|
|
280
|
-
value:
|
|
282
|
+
value: ye,
|
|
281
283
|
total: Q,
|
|
282
284
|
tone: "error",
|
|
283
|
-
icon: /* @__PURE__ */
|
|
285
|
+
icon: /* @__PURE__ */ v(h, { className: "h-4 w-4" })
|
|
284
286
|
}),
|
|
285
|
-
/* @__PURE__ */
|
|
287
|
+
/* @__PURE__ */ v(j, {
|
|
286
288
|
label: "Total",
|
|
287
289
|
value: Q,
|
|
288
290
|
total: Q,
|
|
289
291
|
tone: "info",
|
|
290
|
-
icon: /* @__PURE__ */
|
|
292
|
+
icon: /* @__PURE__ */ v(p, { className: "h-4 w-4" })
|
|
291
293
|
})
|
|
292
294
|
]
|
|
293
295
|
})
|
|
294
296
|
}),
|
|
295
|
-
/* @__PURE__ */
|
|
296
|
-
/* @__PURE__ */
|
|
297
|
+
/* @__PURE__ */ y("div", { children: [
|
|
298
|
+
/* @__PURE__ */ v("h2", {
|
|
297
299
|
className: "mb-3 text-lg font-semibold text-text-primary",
|
|
298
300
|
children: "Verification Items"
|
|
299
301
|
}),
|
|
300
|
-
/* @__PURE__ */
|
|
302
|
+
/* @__PURE__ */ v(a, {
|
|
301
303
|
className: "mb-4",
|
|
302
|
-
searchValue:
|
|
303
|
-
onSearchChange:
|
|
304
|
+
searchValue: N,
|
|
305
|
+
onSearchChange: P,
|
|
304
306
|
searchPlaceholder: "Search verification items...",
|
|
305
|
-
filters: /* @__PURE__ */
|
|
306
|
-
/* @__PURE__ */
|
|
307
|
+
filters: /* @__PURE__ */ y(_, { children: [
|
|
308
|
+
/* @__PURE__ */ v("select", {
|
|
307
309
|
"aria-label": "Filter by status",
|
|
308
|
-
value:
|
|
310
|
+
value: L,
|
|
309
311
|
onChange: (e) => {
|
|
310
|
-
|
|
312
|
+
R(e.target.value), U(1);
|
|
311
313
|
},
|
|
312
|
-
className:
|
|
313
|
-
children:
|
|
314
|
+
className: A,
|
|
315
|
+
children: D.map((e) => /* @__PURE__ */ v("option", {
|
|
314
316
|
value: e.value,
|
|
315
317
|
children: e.label
|
|
316
318
|
}, e.value))
|
|
317
319
|
}),
|
|
318
|
-
/* @__PURE__ */
|
|
320
|
+
/* @__PURE__ */ v("select", {
|
|
319
321
|
"aria-label": "Filter by priority",
|
|
320
|
-
value:
|
|
322
|
+
value: z,
|
|
321
323
|
onChange: (e) => {
|
|
322
|
-
|
|
324
|
+
B(e.target.value), U(1);
|
|
323
325
|
},
|
|
324
|
-
className:
|
|
325
|
-
children:
|
|
326
|
+
className: A,
|
|
327
|
+
children: O.map((e) => /* @__PURE__ */ v("option", {
|
|
326
328
|
value: e.value,
|
|
327
329
|
children: e.label
|
|
328
330
|
}, e.value))
|
|
329
331
|
}),
|
|
330
|
-
/* @__PURE__ */
|
|
332
|
+
/* @__PURE__ */ v("select", {
|
|
331
333
|
"aria-label": "Filter by type",
|
|
332
|
-
value:
|
|
334
|
+
value: V,
|
|
333
335
|
onChange: (e) => {
|
|
334
|
-
|
|
336
|
+
H(e.target.value), U(1);
|
|
335
337
|
},
|
|
336
|
-
className:
|
|
337
|
-
children:
|
|
338
|
+
className: A,
|
|
339
|
+
children: k.map((e) => /* @__PURE__ */ v("option", {
|
|
338
340
|
value: e.value,
|
|
339
341
|
children: e.label
|
|
340
342
|
}, e.value))
|
|
341
343
|
})
|
|
342
344
|
] })
|
|
343
345
|
}),
|
|
344
|
-
/* @__PURE__ */
|
|
345
|
-
columns:
|
|
346
|
-
data:
|
|
346
|
+
/* @__PURE__ */ v(r, {
|
|
347
|
+
columns: be,
|
|
348
|
+
data: W,
|
|
347
349
|
keyField: "id",
|
|
348
350
|
emptyMessage: "No verification items found",
|
|
349
351
|
emptyIllustration: "empty-search",
|
|
350
352
|
emptyDescription: "Verification requests you submit will appear here so you can track their progress.",
|
|
351
353
|
pageSize: 10,
|
|
352
354
|
serverPagination: {
|
|
353
|
-
page:
|
|
354
|
-
pageSize:
|
|
355
|
-
hasNextPage:
|
|
356
|
-
loading:
|
|
357
|
-
onPageChange:
|
|
358
|
-
onPageSizeChange:
|
|
355
|
+
page: M.page,
|
|
356
|
+
pageSize: M.pageSize,
|
|
357
|
+
hasNextPage: W.length >= M.pageSize,
|
|
358
|
+
loading: ue,
|
|
359
|
+
onPageChange: M.setPage,
|
|
360
|
+
onPageSizeChange: M.setPageSize
|
|
359
361
|
}
|
|
360
362
|
})
|
|
361
363
|
] }),
|
|
362
|
-
/* @__PURE__ */ S
|
|
363
|
-
open:
|
|
364
|
-
onOpenChange: (e) => !e &&
|
|
365
|
-
children: /* @__PURE__ */
|
|
364
|
+
/* @__PURE__ */ v(S, {
|
|
365
|
+
open: G !== null,
|
|
366
|
+
onOpenChange: (e) => !e && Z(),
|
|
367
|
+
children: /* @__PURE__ */ v(C, {
|
|
366
368
|
className: "sm:max-w-md",
|
|
367
|
-
children: /* @__PURE__ */
|
|
369
|
+
children: /* @__PURE__ */ y("form", {
|
|
368
370
|
onSubmit: he,
|
|
369
371
|
children: [
|
|
370
|
-
/* @__PURE__ */
|
|
371
|
-
/* @__PURE__ */
|
|
372
|
+
/* @__PURE__ */ y(ae, { children: [/* @__PURE__ */ v(oe, { children: $ ? "Approve verification" : "Reject verification" }), /* @__PURE__ */ v(w, { children: $ ? `Mark "${G?.item.entityLabel ?? ""}" as verified. You can add an optional note for the record.` : `Reject "${G?.item.entityLabel ?? ""}". Add an optional note explaining why so the submitter can respond.` })] }),
|
|
373
|
+
/* @__PURE__ */ y("div", {
|
|
372
374
|
className: "space-y-3 py-2",
|
|
373
|
-
children: [/* @__PURE__ */
|
|
375
|
+
children: [/* @__PURE__ */ v("label", {
|
|
374
376
|
className: "block text-sm font-medium text-text-primary",
|
|
375
377
|
htmlFor: "verification-notes",
|
|
376
378
|
children: "Notes (optional)"
|
|
377
|
-
}), /* @__PURE__ */
|
|
379
|
+
}), /* @__PURE__ */ v("textarea", {
|
|
378
380
|
id: "verification-notes",
|
|
379
|
-
value:
|
|
380
|
-
onChange: (e) =>
|
|
381
|
+
value: q,
|
|
382
|
+
onChange: (e) => J(e.target.value),
|
|
381
383
|
rows: 3,
|
|
382
384
|
placeholder: $ ? "Anything worth noting about this approval…" : "Reason for rejection…",
|
|
383
385
|
className: "w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
|
|
384
386
|
})]
|
|
385
387
|
}),
|
|
386
|
-
/* @__PURE__ */
|
|
388
|
+
/* @__PURE__ */ y(T, { children: [/* @__PURE__ */ v(b, {
|
|
387
389
|
type: "button",
|
|
388
390
|
variant: "outline",
|
|
389
|
-
onClick:
|
|
390
|
-
disabled:
|
|
391
|
+
onClick: Z,
|
|
392
|
+
disabled: Y,
|
|
391
393
|
children: "Cancel"
|
|
392
|
-
}), /* @__PURE__ */
|
|
394
|
+
}), /* @__PURE__ */ v(b, {
|
|
393
395
|
type: "submit",
|
|
394
396
|
variant: $ ? "default" : "destructive",
|
|
395
|
-
disabled:
|
|
396
|
-
children:
|
|
397
|
+
disabled: Y,
|
|
398
|
+
children: Y ? "Submitting…" : $ ? "Approve" : "Reject"
|
|
397
399
|
})] })
|
|
398
400
|
]
|
|
399
401
|
})
|
|
@@ -403,6 +405,6 @@ function j() {
|
|
|
403
405
|
});
|
|
404
406
|
}
|
|
405
407
|
//#endregion
|
|
406
|
-
export {
|
|
408
|
+
export { M as VerificationPage };
|
|
407
409
|
|
|
408
410
|
//# sourceMappingURL=VerificationPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VerificationPage.js","names":[],"sources":["../../../../src/housingvista/pages/verification/VerificationPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Shield, CheckCircle, Clock, XCircle, Eye, Check, X } from 'lucide-react';\nimport {\n Button,\n EmphasisPanel,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout, DataTable, StatusBadge, FilterBar } from '../../components';\nimport type { Column } from '../../components';\nimport { useVerifications, useApproveVerification, useRejectVerification } from '../../hooks';\nimport { useServerPagination } from '../../hooks/useServerPagination';\nimport { notify } from '../../lib/toast';\nimport { cn, formatDate } from '../../utils/helpers';\nimport type { VerificationItem } from '../../types';\n\nfunction getStatusVariant(status: string): 'success' | 'warning' | 'error' | 'info' {\n switch (status) {\n case 'verified':\n return 'success';\n case 'in_progress':\n return 'info';\n case 'pending':\n return 'warning';\n case 'rejected':\n return 'error';\n default:\n return 'info';\n }\n}\n\nfunction getStatusLabel(status: string): string {\n return status.replace('_', ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\nfunction getEntityTypeLabel(type: string): string {\n return type.replace('_', ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\nfunction getPriorityBadge(priority: string) {\n const styles: Record<string, string> = {\n high: 'bg-status-error-bg-subtle text-status-error-text',\n medium: 'bg-status-warning-bg-subtle text-status-warning-text',\n low: 'bg-status-success-bg-subtle text-status-success-text',\n };\n return (\n <span\n className={cn(\n 'inline-flex rounded-full px-2 py-0.5 text-xs font-medium capitalize',\n styles[priority] ?? styles.low\n )}\n >\n {priority}\n </span>\n );\n}\n\ntype DecisionMode = 'approve' | 'reject';\n\nconst STATUS_OPTIONS: { value: string; label: string }[] = [\n { value: '', label: 'All statuses' },\n { value: 'pending', label: 'Pending' },\n { value: 'in_progress', label: 'In Progress' },\n { value: 'verified', label: 'Verified' },\n { value: 'rejected', label: 'Rejected' },\n];\n\nconst PRIORITY_OPTIONS: { value: string; label: string }[] = [\n { value: '', label: 'All priorities' },\n { value: 'high', label: 'High' },\n { value: 'medium', label: 'Medium' },\n { value: 'low', label: 'Low' },\n];\n\nconst ENTITY_TYPE_OPTIONS: { value: string; label: string }[] = [\n { value: '', label: 'All types' },\n { value: 'property', label: 'Property' },\n { value: 'user', label: 'User' },\n { value: 'document', label: 'Document' },\n { value: 'income', label: 'Income' },\n { value: 'bank_account', label: 'Bank Account' },\n];\n\nconst SELECT_CLASS =\n 'h-9 rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\n/** Trust stat card with a proportion bar (share of the total on this page). */\nfunction VerifStat({\n label,\n value,\n total,\n tone,\n icon,\n}: {\n label: string;\n value: number;\n total: number;\n tone: 'success' | 'warning' | 'error' | 'info';\n icon: React.ReactNode;\n}) {\n const pct = total > 0 ? Math.round((value / total) * 100) : 0;\n const barColor = {\n success: 'bg-status-success-text',\n warning: 'bg-status-warning-text',\n error: 'bg-status-error-text',\n info: 'bg-status-info-text',\n }[tone];\n const iconBg = {\n success: 'bg-status-success-bg-subtle text-status-success-text',\n warning: 'bg-status-warning-bg-subtle text-status-warning-text',\n error: 'bg-status-error-bg-subtle text-status-error-text',\n info: 'bg-status-info-bg-subtle text-status-info-text',\n }[tone];\n return (\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4 shadow-elevation-1\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-2xl font-bold tabular-nums text-text-primary\">{value}</span>\n <span className={cn('flex h-8 w-8 items-center justify-center rounded-lg', iconBg)}>{icon}</span>\n </div>\n <p className=\"mt-0.5 text-sm font-medium text-text-secondary\">{label}</p>\n <div className=\"mt-3 h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div className={cn('h-full rounded-full transition-all', barColor)} style={{ width: `${pct}%` }} />\n </div>\n </div>\n );\n}\n\nexport function VerificationPage() {\n const navigate = useNavigate();\n const pagination = useServerPagination(10);\n\n const [search, setSearch] = useState('');\n const [debouncedSearch, setDebouncedSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState('');\n const [priorityFilter, setPriorityFilter] = useState('');\n const [entityTypeFilter, setEntityTypeFilter] = useState('');\n\n const { setPage } = pagination;\n\n // Debounce the free-text search into the server filter; reset to page 1 on change.\n useEffect(() => {\n const handle = setTimeout(() => {\n setDebouncedSearch(search.trim());\n setPage(1);\n }, 300);\n return () => clearTimeout(handle);\n }, [search, setPage]);\n\n const { data: verifications, loading } = useVerifications({\n filter: {\n search: debouncedSearch || undefined,\n status: statusFilter || undefined,\n priority: priorityFilter || undefined,\n entityType: entityTypeFilter || undefined,\n },\n pagination: pagination.pagination,\n });\n const { approveVerification, loading: approving } = useApproveVerification();\n const { rejectVerification, loading: rejecting } = useRejectVerification();\n\n const [decision, setDecision] = useState<{ item: VerificationItem; mode: DecisionMode } | null>(null);\n const [notes, setNotes] = useState('');\n\n const submitting = approving || rejecting;\n\n const openDecision = (item: VerificationItem, mode: DecisionMode) => {\n setDecision({ item, mode });\n setNotes('');\n };\n\n const closeDecision = () => {\n if (submitting) return;\n setDecision(null);\n setNotes('');\n };\n\n const handleConfirm = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!decision) return;\n const trimmedNotes = notes.trim() || undefined;\n try {\n if (decision.mode === 'approve') {\n await approveVerification(decision.item.id, trimmedNotes);\n notify.success('Verification approved', decision.item.entityLabel);\n } else {\n await rejectVerification(decision.item.id, trimmedNotes);\n notify.success('Verification rejected', decision.item.entityLabel);\n }\n setDecision(null);\n setNotes('');\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Please try again.';\n notify.error(\n decision.mode === 'approve' ? 'Could not approve verification' : 'Could not reject verification',\n message\n );\n }\n };\n\n const verified = verifications.filter((v) => v.status === 'verified').length;\n const pending = verifications.filter((v) => v.status === 'pending').length;\n const inProgress = verifications.filter((v) => v.status === 'in_progress').length;\n const rejected = verifications.filter((v) => v.status === 'rejected').length;\n const total = verifications.length;\n\n const columns: Column<VerificationItem>[] = [\n {\n key: 'entityLabel',\n header: 'Item',\n render: (item) => (\n <div>\n <p className=\"font-medium text-text-primary\">{item.entityLabel}</p>\n {item.notes && <p className=\"mt-0.5 text-xs text-text-secondary line-clamp-1\">{item.notes}</p>}\n </div>\n ),\n },\n {\n key: 'entityType',\n header: 'Type',\n render: (item) => (\n <span className=\"inline-flex items-center gap-1 text-xs text-text-secondary capitalize\">\n {getEntityTypeLabel(item.entityType)}\n </span>\n ),\n },\n {\n key: 'priority',\n header: 'Priority',\n render: (item) => getPriorityBadge(item.priority),\n },\n {\n key: 'submittedDate',\n header: 'Submitted',\n render: (item) => <span className=\"text-sm text-text-secondary\">{formatDate(item.submittedDate)}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (item) => <StatusBadge label={getStatusLabel(item.status)} variant={getStatusVariant(item.status)} dot />,\n },\n {\n key: 'actions',\n header: 'Action',\n sortable: false,\n render: (item) => {\n const decidable = item.status === 'pending' || item.status === 'in_progress';\n return (\n <div className=\"flex items-center gap-1.5\">\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(`../verification/${item.id}`);\n }}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-2.5 py-1 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Eye className=\"h-3 w-3\" />\n View\n </button>\n {decidable && (\n <>\n <button\n onClick={(e) => {\n e.stopPropagation();\n openDecision(item, 'approve');\n }}\n className=\"inline-flex items-center gap-1 rounded-md border border-status-success-border bg-status-success-bg-subtle px-2.5 py-1 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle/70\"\n >\n <Check className=\"h-3 w-3\" />\n Approve\n </button>\n <button\n onClick={(e) => {\n e.stopPropagation();\n openDecision(item, 'reject');\n }}\n className=\"inline-flex items-center gap-1 rounded-md border border-status-error-border bg-status-error-bg-subtle px-2.5 py-1 text-xs font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle/70\"\n >\n <X className=\"h-3 w-3\" />\n Reject\n </button>\n </>\n )}\n </div>\n );\n },\n },\n ];\n\n const isApprove = decision?.mode === 'approve';\n\n return (\n <PageLayout\n title=\"Verification Center\"\n description=\"Track and manage all your verification requests\"\n purpose=\"Submit and monitor identity, ownership, and document checks that build trust in your listings and deals. Use this page to see what is verified, what is still pending, and what was rejected so you can act before it blocks a transaction.\"\n actions={\n <span className=\"inline-flex items-center gap-1.5 rounded-full bg-status-success-bg-subtle px-3 py-1.5 text-xs font-medium text-status-success-text\">\n <Shield className=\"h-3.5 w-3.5\" />\n Trust & Safety\n </span>\n }\n >\n {/* Stats — primary insight zone (one EmphasisPanel per page) */}\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <VerifStat\n label=\"Verified\"\n value={verified}\n total={total}\n tone=\"success\"\n icon={<CheckCircle className=\"h-4 w-4\" />}\n />\n <VerifStat\n label=\"Pending\"\n value={pending + inProgress}\n total={total}\n tone=\"warning\"\n icon={<Clock className=\"h-4 w-4\" />}\n />\n <VerifStat\n label=\"Rejected\"\n value={rejected}\n total={total}\n tone=\"error\"\n icon={<XCircle className=\"h-4 w-4\" />}\n />\n <VerifStat label=\"Total\" value={total} total={total} tone=\"info\" icon={<Shield className=\"h-4 w-4\" />} />\n </div>\n </EmphasisPanel>\n\n {/* Verification Table */}\n <div>\n <h2 className=\"mb-3 text-lg font-semibold text-text-primary\">Verification Items</h2>\n <FilterBar\n className=\"mb-4\"\n searchValue={search}\n onSearchChange={setSearch}\n searchPlaceholder=\"Search verification items...\"\n filters={\n <>\n <select\n aria-label=\"Filter by status\"\n value={statusFilter}\n onChange={(e) => {\n setStatusFilter(e.target.value);\n setPage(1);\n }}\n className={SELECT_CLASS}\n >\n {STATUS_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n <select\n aria-label=\"Filter by priority\"\n value={priorityFilter}\n onChange={(e) => {\n setPriorityFilter(e.target.value);\n setPage(1);\n }}\n className={SELECT_CLASS}\n >\n {PRIORITY_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n <select\n aria-label=\"Filter by type\"\n value={entityTypeFilter}\n onChange={(e) => {\n setEntityTypeFilter(e.target.value);\n setPage(1);\n }}\n className={SELECT_CLASS}\n >\n {ENTITY_TYPE_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n </>\n }\n />\n <DataTable<VerificationItem>\n columns={columns}\n data={verifications}\n keyField=\"id\"\n emptyMessage=\"No verification items found\"\n emptyIllustration=\"empty-search\"\n emptyDescription=\"Verification requests you submit will appear here so you can track their progress.\"\n pageSize={10}\n serverPagination={{\n page: pagination.page,\n pageSize: pagination.pageSize,\n hasNextPage: verifications.length >= pagination.pageSize,\n loading,\n onPageChange: pagination.setPage,\n onPageSizeChange: pagination.setPageSize,\n }}\n />\n </div>\n\n {/* Approve / Reject decision dialog */}\n <ResponsiveDialog open={decision !== null} onOpenChange={(open) => !open && closeDecision()}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleConfirm}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {isApprove ? 'Approve verification' : 'Reject verification'}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {isApprove\n ? `Mark \"${decision?.item.entityLabel ?? ''}\" as verified. You can add an optional note for the record.`\n : `Reject \"${decision?.item.entityLabel ?? ''}\". Add an optional note explaining why so the submitter can respond.`}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <label className=\"block text-sm font-medium text-text-primary\" htmlFor=\"verification-notes\">\n Notes (optional)\n </label>\n <textarea\n id=\"verification-notes\"\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n rows={3}\n placeholder={isApprove ? 'Anything worth noting about this approval…' : 'Reason for rejection…'}\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={closeDecision} disabled={submitting}>\n Cancel\n </Button>\n <Button type=\"submit\" variant={isApprove ? 'default' : 'destructive'} disabled={submitting}>\n {submitting ? 'Submitting…' : isApprove ? 'Approve' : 'Reject'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VerificationPage.js","names":[],"sources":["../../../../src/housingvista/pages/verification/VerificationPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Shield, CheckCircle, Clock, XCircle, Eye, Check, X } from 'lucide-react';\nimport {\n Button,\n EmphasisPanel,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout, DataTable, StatusBadge, FilterBar } from '../../components';\nimport type { Column } from '../../components';\nimport { useVerifications, useApproveVerification, useRejectVerification } from '../../hooks';\nimport { useServerPagination } from '../../hooks/useServerPagination';\nimport { notify } from '../../lib/toast';\nimport { cn, formatDate } from '../../utils/helpers';\nimport type { VerificationItem } from '../../types';\nimport { useTrackPageView } from '../../hooks/useHousingVistaAnalytics';\n\nfunction getStatusVariant(status: string): 'success' | 'warning' | 'error' | 'info' {\n switch (status) {\n case 'verified':\n return 'success';\n case 'in_progress':\n return 'info';\n case 'pending':\n return 'warning';\n case 'rejected':\n return 'error';\n default:\n return 'info';\n }\n}\n\nfunction getStatusLabel(status: string): string {\n return status.replace('_', ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\nfunction getEntityTypeLabel(type: string): string {\n return type.replace('_', ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\nfunction getPriorityBadge(priority: string) {\n const styles: Record<string, string> = {\n high: 'bg-status-error-bg-subtle text-status-error-text',\n medium: 'bg-status-warning-bg-subtle text-status-warning-text',\n low: 'bg-status-success-bg-subtle text-status-success-text',\n };\n return (\n <span\n className={cn(\n 'inline-flex rounded-full px-2 py-0.5 text-xs font-medium capitalize',\n styles[priority] ?? styles.low\n )}\n >\n {priority}\n </span>\n );\n}\n\ntype DecisionMode = 'approve' | 'reject';\n\nconst STATUS_OPTIONS: { value: string; label: string }[] = [\n { value: '', label: 'All statuses' },\n { value: 'pending', label: 'Pending' },\n { value: 'in_progress', label: 'In Progress' },\n { value: 'verified', label: 'Verified' },\n { value: 'rejected', label: 'Rejected' },\n];\n\nconst PRIORITY_OPTIONS: { value: string; label: string }[] = [\n { value: '', label: 'All priorities' },\n { value: 'high', label: 'High' },\n { value: 'medium', label: 'Medium' },\n { value: 'low', label: 'Low' },\n];\n\nconst ENTITY_TYPE_OPTIONS: { value: string; label: string }[] = [\n { value: '', label: 'All types' },\n { value: 'property', label: 'Property' },\n { value: 'user', label: 'User' },\n { value: 'document', label: 'Document' },\n { value: 'income', label: 'Income' },\n { value: 'bank_account', label: 'Bank Account' },\n];\n\nconst SELECT_CLASS =\n 'h-9 rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\n/** Trust stat card with a proportion bar (share of the total on this page). */\nfunction VerifStat({\n label,\n value,\n total,\n tone,\n icon,\n}: {\n label: string;\n value: number;\n total: number;\n tone: 'success' | 'warning' | 'error' | 'info';\n icon: React.ReactNode;\n}) {\n const pct = total > 0 ? Math.round((value / total) * 100) : 0;\n const barColor = {\n success: 'bg-status-success-text',\n warning: 'bg-status-warning-text',\n error: 'bg-status-error-text',\n info: 'bg-status-info-text',\n }[tone];\n const iconBg = {\n success: 'bg-status-success-bg-subtle text-status-success-text',\n warning: 'bg-status-warning-bg-subtle text-status-warning-text',\n error: 'bg-status-error-bg-subtle text-status-error-text',\n info: 'bg-status-info-bg-subtle text-status-info-text',\n }[tone];\n return (\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4 shadow-elevation-1\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-2xl font-bold tabular-nums text-text-primary\">{value}</span>\n <span className={cn('flex h-8 w-8 items-center justify-center rounded-lg', iconBg)}>{icon}</span>\n </div>\n <p className=\"mt-0.5 text-sm font-medium text-text-secondary\">{label}</p>\n <div className=\"mt-3 h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div className={cn('h-full rounded-full transition-all', barColor)} style={{ width: `${pct}%` }} />\n </div>\n </div>\n );\n}\n\nexport function VerificationPage() {\n useTrackPageView('housingvista.verification.viewed');\n const navigate = useNavigate();\n const pagination = useServerPagination(10);\n\n const [search, setSearch] = useState('');\n const [debouncedSearch, setDebouncedSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState('');\n const [priorityFilter, setPriorityFilter] = useState('');\n const [entityTypeFilter, setEntityTypeFilter] = useState('');\n\n const { setPage } = pagination;\n\n // Debounce the free-text search into the server filter; reset to page 1 on change.\n useEffect(() => {\n const handle = setTimeout(() => {\n setDebouncedSearch(search.trim());\n setPage(1);\n }, 300);\n return () => clearTimeout(handle);\n }, [search, setPage]);\n\n const { data: verifications, loading } = useVerifications({\n filter: {\n search: debouncedSearch || undefined,\n status: statusFilter || undefined,\n priority: priorityFilter || undefined,\n entityType: entityTypeFilter || undefined,\n },\n pagination: pagination.pagination,\n });\n const { approveVerification, loading: approving } = useApproveVerification();\n const { rejectVerification, loading: rejecting } = useRejectVerification();\n\n const [decision, setDecision] = useState<{ item: VerificationItem; mode: DecisionMode } | null>(null);\n const [notes, setNotes] = useState('');\n\n const submitting = approving || rejecting;\n\n const openDecision = (item: VerificationItem, mode: DecisionMode) => {\n setDecision({ item, mode });\n setNotes('');\n };\n\n const closeDecision = () => {\n if (submitting) return;\n setDecision(null);\n setNotes('');\n };\n\n const handleConfirm = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!decision) return;\n const trimmedNotes = notes.trim() || undefined;\n try {\n if (decision.mode === 'approve') {\n await approveVerification(decision.item.id, trimmedNotes);\n notify.success('Verification approved', decision.item.entityLabel);\n } else {\n await rejectVerification(decision.item.id, trimmedNotes);\n notify.success('Verification rejected', decision.item.entityLabel);\n }\n setDecision(null);\n setNotes('');\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Please try again.';\n notify.error(\n decision.mode === 'approve' ? 'Could not approve verification' : 'Could not reject verification',\n message\n );\n }\n };\n\n const verified = verifications.filter((v) => v.status === 'verified').length;\n const pending = verifications.filter((v) => v.status === 'pending').length;\n const inProgress = verifications.filter((v) => v.status === 'in_progress').length;\n const rejected = verifications.filter((v) => v.status === 'rejected').length;\n const total = verifications.length;\n\n const columns: Column<VerificationItem>[] = [\n {\n key: 'entityLabel',\n header: 'Item',\n render: (item) => (\n <div>\n <p className=\"font-medium text-text-primary\">{item.entityLabel}</p>\n {item.notes && <p className=\"mt-0.5 text-xs text-text-secondary line-clamp-1\">{item.notes}</p>}\n </div>\n ),\n },\n {\n key: 'entityType',\n header: 'Type',\n render: (item) => (\n <span className=\"inline-flex items-center gap-1 text-xs text-text-secondary capitalize\">\n {getEntityTypeLabel(item.entityType)}\n </span>\n ),\n },\n {\n key: 'priority',\n header: 'Priority',\n render: (item) => getPriorityBadge(item.priority),\n },\n {\n key: 'submittedDate',\n header: 'Submitted',\n render: (item) => <span className=\"text-sm text-text-secondary\">{formatDate(item.submittedDate)}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (item) => <StatusBadge label={getStatusLabel(item.status)} variant={getStatusVariant(item.status)} dot />,\n },\n {\n key: 'actions',\n header: 'Action',\n sortable: false,\n render: (item) => {\n const decidable = item.status === 'pending' || item.status === 'in_progress';\n return (\n <div className=\"flex items-center gap-1.5\">\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(`../verification/${item.id}`);\n }}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-2.5 py-1 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Eye className=\"h-3 w-3\" />\n View\n </button>\n {decidable && (\n <>\n <button\n onClick={(e) => {\n e.stopPropagation();\n openDecision(item, 'approve');\n }}\n className=\"inline-flex items-center gap-1 rounded-md border border-status-success-border bg-status-success-bg-subtle px-2.5 py-1 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle/70\"\n >\n <Check className=\"h-3 w-3\" />\n Approve\n </button>\n <button\n onClick={(e) => {\n e.stopPropagation();\n openDecision(item, 'reject');\n }}\n className=\"inline-flex items-center gap-1 rounded-md border border-status-error-border bg-status-error-bg-subtle px-2.5 py-1 text-xs font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle/70\"\n >\n <X className=\"h-3 w-3\" />\n Reject\n </button>\n </>\n )}\n </div>\n );\n },\n },\n ];\n\n const isApprove = decision?.mode === 'approve';\n\n return (\n <PageLayout\n title=\"Verification Center\"\n description=\"Track and manage all your verification requests\"\n purpose=\"Submit and monitor identity, ownership, and document checks that build trust in your listings and deals. Use this page to see what is verified, what is still pending, and what was rejected so you can act before it blocks a transaction.\"\n actions={\n <span className=\"inline-flex items-center gap-1.5 rounded-full bg-status-success-bg-subtle px-3 py-1.5 text-xs font-medium text-status-success-text\">\n <Shield className=\"h-3.5 w-3.5\" />\n Trust & Safety\n </span>\n }\n >\n {/* Stats — primary insight zone (one EmphasisPanel per page) */}\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <VerifStat\n label=\"Verified\"\n value={verified}\n total={total}\n tone=\"success\"\n icon={<CheckCircle className=\"h-4 w-4\" />}\n />\n <VerifStat\n label=\"Pending\"\n value={pending + inProgress}\n total={total}\n tone=\"warning\"\n icon={<Clock className=\"h-4 w-4\" />}\n />\n <VerifStat\n label=\"Rejected\"\n value={rejected}\n total={total}\n tone=\"error\"\n icon={<XCircle className=\"h-4 w-4\" />}\n />\n <VerifStat label=\"Total\" value={total} total={total} tone=\"info\" icon={<Shield className=\"h-4 w-4\" />} />\n </div>\n </EmphasisPanel>\n\n {/* Verification Table */}\n <div>\n <h2 className=\"mb-3 text-lg font-semibold text-text-primary\">Verification Items</h2>\n <FilterBar\n className=\"mb-4\"\n searchValue={search}\n onSearchChange={setSearch}\n searchPlaceholder=\"Search verification items...\"\n filters={\n <>\n <select\n aria-label=\"Filter by status\"\n value={statusFilter}\n onChange={(e) => {\n setStatusFilter(e.target.value);\n setPage(1);\n }}\n className={SELECT_CLASS}\n >\n {STATUS_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n <select\n aria-label=\"Filter by priority\"\n value={priorityFilter}\n onChange={(e) => {\n setPriorityFilter(e.target.value);\n setPage(1);\n }}\n className={SELECT_CLASS}\n >\n {PRIORITY_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n <select\n aria-label=\"Filter by type\"\n value={entityTypeFilter}\n onChange={(e) => {\n setEntityTypeFilter(e.target.value);\n setPage(1);\n }}\n className={SELECT_CLASS}\n >\n {ENTITY_TYPE_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n </>\n }\n />\n <DataTable<VerificationItem>\n columns={columns}\n data={verifications}\n keyField=\"id\"\n emptyMessage=\"No verification items found\"\n emptyIllustration=\"empty-search\"\n emptyDescription=\"Verification requests you submit will appear here so you can track their progress.\"\n pageSize={10}\n serverPagination={{\n page: pagination.page,\n pageSize: pagination.pageSize,\n hasNextPage: verifications.length >= pagination.pageSize,\n loading,\n onPageChange: pagination.setPage,\n onPageSizeChange: pagination.setPageSize,\n }}\n />\n </div>\n\n {/* Approve / Reject decision dialog */}\n <ResponsiveDialog open={decision !== null} onOpenChange={(open) => !open && closeDecision()}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleConfirm}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {isApprove ? 'Approve verification' : 'Reject verification'}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {isApprove\n ? `Mark \"${decision?.item.entityLabel ?? ''}\" as verified. You can add an optional note for the record.`\n : `Reject \"${decision?.item.entityLabel ?? ''}\". Add an optional note explaining why so the submitter can respond.`}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <label className=\"block text-sm font-medium text-text-primary\" htmlFor=\"verification-notes\">\n Notes (optional)\n </label>\n <textarea\n id=\"verification-notes\"\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n rows={3}\n placeholder={isApprove ? 'Anything worth noting about this approval…' : 'Reason for rejection…'}\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={closeDecision} disabled={submitting}>\n Cancel\n </Button>\n <Button type=\"submit\" variant={isApprove ? 'default' : 'destructive'} disabled={submitting}>\n {submitting ? 'Submitting…' : isApprove ? 'Approve' : 'Reject'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ViewingsPage.d.ts","sourceRoot":"","sources":["../../../../src/housingvista/pages/viewings/ViewingsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"ViewingsPage.d.ts","sourceRoot":"","sources":["../../../../src/housingvista/pages/viewings/ViewingsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAsEH,wBAAgB,YAAY,gCAwT3B"}
|