@burdenoff/microfe-housingvista 2026.925.1 → 2026.928.1
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.
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Viewings — track property viewings
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Viewings — track property viewings. Buyer side: reschedule (update) or cancel
|
|
3
|
+
* one you booked. Host side (agent/owner): confirm or decline incoming
|
|
4
|
+
* `requested` viewings for the listings you manage. Server-side status filter +
|
|
5
|
+
* pagination, plus an optional week strip that lets you jump to a day and see
|
|
6
|
+
* which days have viewings. This is where the "book a viewing" story's
|
|
7
|
+
* "track your viewings" and the host disposition step both land.
|
|
6
8
|
* @module housingvista
|
|
7
9
|
*/
|
|
8
10
|
export declare function ViewingsPage(): import("react").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ViewingsPage.d.ts","sourceRoot":"","sources":["../../../../src/housingvista/pages/viewings/ViewingsPage.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ViewingsPage.d.ts","sourceRoot":"","sources":["../../../../src/housingvista/pages/viewings/ViewingsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqFH,wBAAgB,YAAY,gCA2a3B"}
|
|
@@ -4,17 +4,17 @@ import { StatCard as r } from "../../components/StatCard.js";
|
|
|
4
4
|
import { StatusBadge as i } from "../../components/StatusBadge.js";
|
|
5
5
|
import { EmptyState as a } from "../../components/EmptyState.js";
|
|
6
6
|
import { useTrackPageView as o } from "../../hooks/useHousingVistaAnalytics.js";
|
|
7
|
-
import { useCancelViewing as
|
|
8
|
-
import { notify as
|
|
7
|
+
import { useCancelViewing as ee, useUpdateViewing as te, useViewings as ne } from "../../hooks/index.js";
|
|
8
|
+
import { notify as s } from "../../lib/toast.js";
|
|
9
9
|
import "../../components/index.js";
|
|
10
|
-
import { useServerPagination as
|
|
11
|
-
import { useMemo as
|
|
12
|
-
import { CalendarClock as
|
|
13
|
-
import { useNavigate as
|
|
14
|
-
import { jsx as
|
|
15
|
-
import { Button as
|
|
10
|
+
import { useServerPagination as c } from "../../hooks/useServerPagination.js";
|
|
11
|
+
import { useMemo as l, useState as u } from "react";
|
|
12
|
+
import { CalendarClock as d, CalendarDays as f, Check as re, ChevronLeft as ie, ChevronRight as ae, Home as p, MapPin as oe, Users as m, Video as h, X as g, XCircle as se } from "lucide-react";
|
|
13
|
+
import { useNavigate as ce } from "react-router";
|
|
14
|
+
import { Fragment as le, jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
15
|
+
import { Button as y, EmphasisPanel as ue, GlassCard as b, Pagination as de, ResponsiveDialog as x, ResponsiveDialogContent as S, ResponsiveDialogDescription as C, ResponsiveDialogFooter as w, ResponsiveDialogHeader as T, ResponsiveDialogTitle as E } from "@burdenoff/fe-libs/ui";
|
|
16
16
|
//#region src/housingvista/pages/viewings/ViewingsPage.tsx
|
|
17
|
-
var
|
|
17
|
+
var fe = [
|
|
18
18
|
{
|
|
19
19
|
value: "all",
|
|
20
20
|
label: "All"
|
|
@@ -36,7 +36,7 @@ var O = [
|
|
|
36
36
|
label: "Cancelled"
|
|
37
37
|
}
|
|
38
38
|
];
|
|
39
|
-
function
|
|
39
|
+
function pe(e) {
|
|
40
40
|
switch (e) {
|
|
41
41
|
case "confirmed": return "success";
|
|
42
42
|
case "requested": return "warning";
|
|
@@ -44,14 +44,14 @@ function k(e) {
|
|
|
44
44
|
default: return "muted";
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
var
|
|
48
|
-
function
|
|
47
|
+
var me = "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)]", D = 5e3;
|
|
48
|
+
function he(e) {
|
|
49
49
|
let t = new Date(e);
|
|
50
50
|
if (Number.isNaN(t.getTime())) return "";
|
|
51
51
|
let n = t.getTimezoneOffset();
|
|
52
52
|
return (/* @__PURE__ */ new Date(t.getTime() - n * 6e4)).toISOString().slice(0, 16);
|
|
53
53
|
}
|
|
54
|
-
var
|
|
54
|
+
var O = 1440 * 60 * 1e3, ge = [
|
|
55
55
|
"Sun",
|
|
56
56
|
"Mon",
|
|
57
57
|
"Tue",
|
|
@@ -60,298 +60,404 @@ var M = 1440 * 60 * 1e3, N = [
|
|
|
60
60
|
"Fri",
|
|
61
61
|
"Sat"
|
|
62
62
|
];
|
|
63
|
-
function
|
|
63
|
+
function _e(e) {
|
|
64
64
|
let t = new Date(e.getFullYear(), e.getMonth(), e.getDate());
|
|
65
65
|
return t.setDate(t.getDate() - t.getDay()), t;
|
|
66
66
|
}
|
|
67
|
-
function
|
|
67
|
+
function k(e, t) {
|
|
68
68
|
return e.getFullYear() === t.getFullYear() && e.getMonth() === t.getMonth() && e.getDate() === t.getDate();
|
|
69
69
|
}
|
|
70
|
-
function
|
|
70
|
+
function A() {
|
|
71
71
|
o("housingvista.viewings.viewed");
|
|
72
|
-
let
|
|
73
|
-
filter:
|
|
74
|
-
pagination:
|
|
75
|
-
}), { updateViewing:
|
|
72
|
+
let A = ce(), j = c(10), [M, N] = u("all"), { data: P, loading: F } = ne({
|
|
73
|
+
filter: M === "all" ? void 0 : { status: M },
|
|
74
|
+
pagination: j.pagination
|
|
75
|
+
}), { updateViewing: I, loading: L } = te(), { cancelViewing: ve, loading: ye } = ee(), R = L || ye, [z, B] = u(null), [V, H] = u(""), [U, W] = u(null), [G, K] = u(""), [q, J] = u(() => _e(/* @__PURE__ */ new Date())), [Y, X] = u(null), be = l(() => Array.from({ length: 7 }, (e, t) => new Date(q.getTime() + t * O)), [q]), xe = l(() => {
|
|
76
76
|
let e = /* @__PURE__ */ new Set();
|
|
77
|
-
for (let t of
|
|
77
|
+
for (let t of P) {
|
|
78
78
|
let n = new Date(t.scheduledAt);
|
|
79
79
|
Number.isNaN(n.getTime()) || e.add(`${n.getFullYear()}-${n.getMonth()}-${n.getDate()}`);
|
|
80
80
|
}
|
|
81
81
|
return e;
|
|
82
|
-
}, [
|
|
82
|
+
}, [P]), Z = l(() => Y ? P.filter((e) => k(new Date(e.scheduledAt), Y)) : P, [P, Y]), Se = q.toLocaleDateString("en-US", {
|
|
83
83
|
month: "long",
|
|
84
84
|
year: "numeric"
|
|
85
|
-
}),
|
|
86
|
-
|
|
87
|
-
},
|
|
88
|
-
if (e.preventDefault(),
|
|
89
|
-
if (!
|
|
90
|
-
|
|
85
|
+
}), Ce = (e) => {
|
|
86
|
+
B(e), H(he(e.scheduledAt));
|
|
87
|
+
}, we = async (e) => {
|
|
88
|
+
if (e.preventDefault(), z) {
|
|
89
|
+
if (!V) {
|
|
90
|
+
s.warning("Pick a new date and time");
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
93
|
try {
|
|
94
|
-
await
|
|
94
|
+
await I(z.id, { scheduledAt: new Date(V).toISOString() }), s.success("Viewing rescheduled"), B(null);
|
|
95
95
|
} catch (e) {
|
|
96
|
-
|
|
96
|
+
s.error("Could not reschedule", e.message);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
},
|
|
99
|
+
}, Te = async (e) => {
|
|
100
100
|
try {
|
|
101
|
-
await
|
|
101
|
+
await ve(e.id), s.success("Viewing cancelled");
|
|
102
102
|
} catch (e) {
|
|
103
|
-
|
|
103
|
+
s.error("Could not cancel viewing", e.message);
|
|
104
104
|
}
|
|
105
|
-
},
|
|
106
|
-
|
|
105
|
+
}, Ee = async (e) => {
|
|
106
|
+
try {
|
|
107
|
+
await I(e.id, { status: "confirmed" }), s.success("Viewing confirmed", e.property?.title ?? void 0);
|
|
108
|
+
} catch (e) {
|
|
109
|
+
s.error("Could not confirm viewing", e.message);
|
|
110
|
+
}
|
|
111
|
+
}, Q = () => {
|
|
112
|
+
R || B(null);
|
|
113
|
+
}, $ = () => {
|
|
114
|
+
R || (W(null), K(""));
|
|
115
|
+
}, De = async (e) => {
|
|
116
|
+
if (e.preventDefault(), !U) return;
|
|
117
|
+
let t = G.trim(), n = t ? `Declined: ${t}` : "Declined", r = U.notes?.trim() ?? "", i = r ? `${r}\n${n}` : n;
|
|
118
|
+
if (i.length > D) {
|
|
119
|
+
let e = D - n.length - 1, t = e > 0 ? r.slice(Math.max(r.length - e, 0)) : "";
|
|
120
|
+
i = t ? `${t}\n${n}` : n;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
await I(U.id, {
|
|
124
|
+
status: "cancelled",
|
|
125
|
+
notes: i
|
|
126
|
+
}), s.success("Viewing declined", U.property?.title ?? void 0), W(null), K("");
|
|
127
|
+
} catch (e) {
|
|
128
|
+
s.error("Could not decline viewing", e.message);
|
|
129
|
+
}
|
|
130
|
+
}, Oe = P.filter((e) => e.status === "requested" || e.status === "confirmed").length;
|
|
131
|
+
return /* @__PURE__ */ v(n, {
|
|
107
132
|
title: "Viewings",
|
|
108
|
-
description: "Track and manage
|
|
109
|
-
purpose: "See every viewing
|
|
110
|
-
actions: /* @__PURE__ */
|
|
133
|
+
description: "Track and manage property viewings — booked requests and the ones you host",
|
|
134
|
+
purpose: "See every viewing in play, in person or virtual, with its date and status. Confirm or decline incoming requests on listings you manage, reschedule when plans change, or cancel one you no longer need.",
|
|
135
|
+
actions: /* @__PURE__ */ v(y, {
|
|
111
136
|
variant: "outline",
|
|
112
|
-
onClick: () =>
|
|
113
|
-
children: [/* @__PURE__ */
|
|
137
|
+
onClick: () => A("../properties"),
|
|
138
|
+
children: [/* @__PURE__ */ _(p, { className: "h-4 w-4" }), /* @__PURE__ */ _("span", {
|
|
114
139
|
className: "ml-1",
|
|
115
140
|
children: "Book a viewing"
|
|
116
141
|
})]
|
|
117
142
|
}),
|
|
118
143
|
children: [
|
|
119
|
-
/* @__PURE__ */
|
|
144
|
+
/* @__PURE__ */ _(ue, {
|
|
120
145
|
className: "p-4",
|
|
121
|
-
children: /* @__PURE__ */
|
|
146
|
+
children: /* @__PURE__ */ v("div", {
|
|
122
147
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
|
|
123
148
|
children: [
|
|
124
|
-
/* @__PURE__ */
|
|
149
|
+
/* @__PURE__ */ _(r, {
|
|
125
150
|
label: "Total viewings",
|
|
126
|
-
value:
|
|
127
|
-
icon: /* @__PURE__ */
|
|
151
|
+
value: P.length,
|
|
152
|
+
icon: /* @__PURE__ */ _(f, { className: "h-4 w-4" })
|
|
128
153
|
}),
|
|
129
|
-
/* @__PURE__ */
|
|
154
|
+
/* @__PURE__ */ _(r, {
|
|
130
155
|
label: "Upcoming",
|
|
131
|
-
value:
|
|
132
|
-
icon: /* @__PURE__ */
|
|
156
|
+
value: Oe,
|
|
157
|
+
icon: /* @__PURE__ */ _(d, { className: "h-4 w-4" })
|
|
133
158
|
}),
|
|
134
|
-
/* @__PURE__ */
|
|
159
|
+
/* @__PURE__ */ _(r, {
|
|
135
160
|
label: "Virtual",
|
|
136
|
-
value:
|
|
137
|
-
icon: /* @__PURE__ */
|
|
161
|
+
value: P.filter((e) => e.type === "virtual").length,
|
|
162
|
+
icon: /* @__PURE__ */ _(h, { className: "h-4 w-4" })
|
|
138
163
|
})
|
|
139
164
|
]
|
|
140
165
|
})
|
|
141
166
|
}),
|
|
142
|
-
/* @__PURE__ */
|
|
167
|
+
/* @__PURE__ */ v(b, {
|
|
143
168
|
treatment: "plain",
|
|
144
169
|
className: "p-3",
|
|
145
|
-
children: [/* @__PURE__ */
|
|
170
|
+
children: [/* @__PURE__ */ v("div", {
|
|
146
171
|
className: "mb-2 flex items-center justify-between",
|
|
147
|
-
children: [/* @__PURE__ */
|
|
172
|
+
children: [/* @__PURE__ */ _("span", {
|
|
148
173
|
className: "text-sm font-semibold text-text-primary",
|
|
149
|
-
children:
|
|
150
|
-
}), /* @__PURE__ */
|
|
174
|
+
children: Se
|
|
175
|
+
}), /* @__PURE__ */ v("div", {
|
|
151
176
|
className: "flex items-center gap-1",
|
|
152
177
|
children: [
|
|
153
|
-
|
|
178
|
+
Y && /* @__PURE__ */ _("button", {
|
|
154
179
|
type: "button",
|
|
155
|
-
onClick: () =>
|
|
180
|
+
onClick: () => X(null),
|
|
156
181
|
className: "mr-1 rounded-full bg-bg-sunken px-2.5 py-1 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary",
|
|
157
182
|
children: "All days"
|
|
158
183
|
}),
|
|
159
|
-
/* @__PURE__ */
|
|
184
|
+
/* @__PURE__ */ _("button", {
|
|
160
185
|
type: "button",
|
|
161
186
|
"aria-label": "Previous week",
|
|
162
|
-
onClick: () =>
|
|
187
|
+
onClick: () => J(/* @__PURE__ */ new Date(q.getTime() - 7 * O)),
|
|
163
188
|
className: "rounded-md p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary",
|
|
164
|
-
children: /* @__PURE__ */
|
|
189
|
+
children: /* @__PURE__ */ _(ie, { className: "h-4 w-4" })
|
|
165
190
|
}),
|
|
166
|
-
/* @__PURE__ */
|
|
191
|
+
/* @__PURE__ */ _("button", {
|
|
167
192
|
type: "button",
|
|
168
193
|
"aria-label": "Next week",
|
|
169
|
-
onClick: () =>
|
|
194
|
+
onClick: () => J(new Date(q.getTime() + 7 * O)),
|
|
170
195
|
className: "rounded-md p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary",
|
|
171
|
-
children: /* @__PURE__ */
|
|
196
|
+
children: /* @__PURE__ */ _(ae, { className: "h-4 w-4" })
|
|
172
197
|
})
|
|
173
198
|
]
|
|
174
199
|
})]
|
|
175
|
-
}), /* @__PURE__ */
|
|
200
|
+
}), /* @__PURE__ */ _("div", {
|
|
176
201
|
className: "grid grid-cols-7 gap-1.5",
|
|
177
|
-
children:
|
|
178
|
-
let n = `${t.getFullYear()}-${t.getMonth()}-${t.getDate()}`, r =
|
|
179
|
-
return /* @__PURE__ */
|
|
202
|
+
children: be.map((t) => {
|
|
203
|
+
let n = `${t.getFullYear()}-${t.getMonth()}-${t.getDate()}`, r = k(t, /* @__PURE__ */ new Date()), i = Y ? k(t, Y) : !1, a = xe.has(n);
|
|
204
|
+
return /* @__PURE__ */ v("button", {
|
|
180
205
|
type: "button",
|
|
181
|
-
onClick: () =>
|
|
206
|
+
onClick: () => X(i ? null : t),
|
|
182
207
|
"aria-pressed": i,
|
|
183
208
|
className: e("flex flex-col items-center gap-0.5 rounded-lg py-2 text-center transition-colors", i ? "bg-action-primary-bg text-action-primary-text" : "text-text-secondary hover:bg-bg-sunken"),
|
|
184
209
|
children: [
|
|
185
|
-
/* @__PURE__ */
|
|
210
|
+
/* @__PURE__ */ _("span", {
|
|
186
211
|
className: "text-[0.65rem] font-medium uppercase",
|
|
187
|
-
children:
|
|
212
|
+
children: ge[t.getDay()]
|
|
188
213
|
}),
|
|
189
|
-
/* @__PURE__ */
|
|
214
|
+
/* @__PURE__ */ _("span", {
|
|
190
215
|
className: e("text-sm font-semibold tabular-nums", !i && r && "text-primary"),
|
|
191
216
|
children: t.getDate()
|
|
192
217
|
}),
|
|
193
|
-
/* @__PURE__ */
|
|
218
|
+
/* @__PURE__ */ _("span", { className: e("h-1 w-1 rounded-full", a ? i ? "bg-action-primary-text" : "bg-status-success" : "bg-transparent") })
|
|
194
219
|
]
|
|
195
220
|
}, n);
|
|
196
221
|
})
|
|
197
222
|
})]
|
|
198
223
|
}),
|
|
199
|
-
/* @__PURE__ */
|
|
224
|
+
/* @__PURE__ */ _("div", {
|
|
200
225
|
className: "-mx-1 flex flex-nowrap items-center gap-2 overflow-x-auto px-1 pb-1 sm:flex-wrap sm:overflow-visible",
|
|
201
|
-
children:
|
|
226
|
+
children: fe.map((t) => /* @__PURE__ */ _("button", {
|
|
202
227
|
onClick: () => {
|
|
203
|
-
|
|
228
|
+
N(t.value), j.setPage(1);
|
|
204
229
|
},
|
|
205
|
-
className: e("shrink-0 rounded-full px-3 py-1.5 text-sm font-medium transition-colors",
|
|
230
|
+
className: e("shrink-0 rounded-full px-3 py-1.5 text-sm font-medium transition-colors", M === t.value ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
|
|
206
231
|
children: t.label
|
|
207
232
|
}, t.value))
|
|
208
233
|
}),
|
|
209
|
-
|
|
234
|
+
P.length === 0 && !F ? /* @__PURE__ */ _(a, {
|
|
210
235
|
illustration: "empty-generic",
|
|
211
|
-
icon: /* @__PURE__ */
|
|
236
|
+
icon: /* @__PURE__ */ _(d, { className: "h-12 w-12" }),
|
|
212
237
|
title: "No viewings booked",
|
|
213
238
|
description: "Book a viewing from any property page and it will show up here to track.",
|
|
214
|
-
action: /* @__PURE__ */
|
|
215
|
-
onClick: () =>
|
|
239
|
+
action: /* @__PURE__ */ _(y, {
|
|
240
|
+
onClick: () => A("../properties"),
|
|
216
241
|
children: "Browse properties"
|
|
217
242
|
})
|
|
218
|
-
}) :
|
|
243
|
+
}) : Z.length === 0 ? /* @__PURE__ */ _(a, {
|
|
219
244
|
illustration: "empty-generic",
|
|
220
|
-
icon: /* @__PURE__ */
|
|
245
|
+
icon: /* @__PURE__ */ _(d, { className: "h-12 w-12" }),
|
|
221
246
|
title: "No viewings that day",
|
|
222
247
|
description: "There are no viewings scheduled for the selected day. Pick another day or clear the filter.",
|
|
223
|
-
action: /* @__PURE__ */
|
|
224
|
-
onClick: () =>
|
|
248
|
+
action: /* @__PURE__ */ _(y, {
|
|
249
|
+
onClick: () => X(null),
|
|
225
250
|
children: "Show all days"
|
|
226
251
|
})
|
|
227
|
-
}) : /* @__PURE__ */
|
|
252
|
+
}) : /* @__PURE__ */ v("div", {
|
|
228
253
|
className: "space-y-4",
|
|
229
|
-
children: [
|
|
254
|
+
children: [Z.map((e) => {
|
|
230
255
|
let n = e.property?.images.find((e) => e.isPrimary) ?? e.property?.images[0], r = e.status === "requested" || e.status === "confirmed", a = e.type === "virtual";
|
|
231
|
-
return /* @__PURE__ */
|
|
256
|
+
return /* @__PURE__ */ _(b, {
|
|
232
257
|
treatment: r ? "insight" : "plain",
|
|
233
258
|
className: "p-5",
|
|
234
|
-
children: /* @__PURE__ */
|
|
259
|
+
children: /* @__PURE__ */ v("div", {
|
|
235
260
|
className: "flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between",
|
|
236
|
-
children: [/* @__PURE__ */
|
|
261
|
+
children: [/* @__PURE__ */ v("button", {
|
|
237
262
|
type: "button",
|
|
238
|
-
onClick: () => e.property &&
|
|
263
|
+
onClick: () => e.property && A(`../properties/${e.property.id}`),
|
|
239
264
|
className: "flex flex-1 items-start gap-3 text-left",
|
|
240
|
-
children: [n ? /* @__PURE__ */
|
|
265
|
+
children: [n ? /* @__PURE__ */ _("img", {
|
|
241
266
|
src: n.url,
|
|
242
267
|
alt: "",
|
|
243
268
|
className: "h-16 w-16 rounded-lg object-cover"
|
|
244
|
-
}) : /* @__PURE__ */
|
|
269
|
+
}) : /* @__PURE__ */ _("div", {
|
|
245
270
|
className: "flex h-16 w-16 items-center justify-center rounded-lg bg-bg-sunken",
|
|
246
|
-
children: /* @__PURE__ */
|
|
247
|
-
}), /* @__PURE__ */
|
|
271
|
+
children: /* @__PURE__ */ _(p, { className: "h-6 w-6 text-text-secondary" })
|
|
272
|
+
}), /* @__PURE__ */ v("div", {
|
|
248
273
|
className: "min-w-0",
|
|
249
274
|
children: [
|
|
250
|
-
/* @__PURE__ */
|
|
275
|
+
/* @__PURE__ */ v("div", {
|
|
251
276
|
className: "mb-1 flex flex-wrap items-center gap-2",
|
|
252
277
|
children: [
|
|
253
|
-
/* @__PURE__ */
|
|
278
|
+
/* @__PURE__ */ _("h3", {
|
|
254
279
|
className: "font-semibold text-text-primary",
|
|
255
280
|
children: e.property?.title ?? "Property"
|
|
256
281
|
}),
|
|
257
|
-
/* @__PURE__ */
|
|
282
|
+
/* @__PURE__ */ _(i, {
|
|
258
283
|
label: e.status,
|
|
259
|
-
variant:
|
|
284
|
+
variant: pe(e.status),
|
|
260
285
|
dot: !0
|
|
261
286
|
}),
|
|
262
|
-
/* @__PURE__ */
|
|
287
|
+
/* @__PURE__ */ v("span", {
|
|
263
288
|
className: "inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary",
|
|
264
|
-
children: [
|
|
289
|
+
children: [_(a ? h : m, { className: "h-3 w-3" }), a ? "Virtual" : "In person"]
|
|
265
290
|
})
|
|
266
291
|
]
|
|
267
292
|
}),
|
|
268
|
-
e.property && /* @__PURE__ */
|
|
293
|
+
e.property && /* @__PURE__ */ v("p", {
|
|
269
294
|
className: "flex items-center gap-1 text-xs text-text-secondary",
|
|
270
295
|
children: [
|
|
271
|
-
/* @__PURE__ */
|
|
296
|
+
/* @__PURE__ */ _(oe, { className: "h-3 w-3" }),
|
|
272
297
|
e.property.location.city,
|
|
273
298
|
", ",
|
|
274
299
|
e.property.location.state
|
|
275
300
|
]
|
|
276
301
|
}),
|
|
277
|
-
/* @__PURE__ */
|
|
302
|
+
/* @__PURE__ */ v("div", {
|
|
278
303
|
className: "mt-2 flex flex-wrap gap-4 text-sm text-text-secondary",
|
|
279
|
-
children: [/* @__PURE__ */
|
|
304
|
+
children: [/* @__PURE__ */ v("span", {
|
|
280
305
|
className: "flex items-center gap-1 font-medium text-text-primary",
|
|
281
|
-
children: [/* @__PURE__ */
|
|
282
|
-
}), e.attendeeName && /* @__PURE__ */
|
|
306
|
+
children: [/* @__PURE__ */ _(d, { className: "h-3.5 w-3.5" }), t(e.scheduledAt)]
|
|
307
|
+
}), e.attendeeName && /* @__PURE__ */ v("span", {
|
|
283
308
|
className: "flex items-center gap-1",
|
|
284
|
-
children: [/* @__PURE__ */
|
|
309
|
+
children: [/* @__PURE__ */ _(m, { className: "h-3.5 w-3.5" }), e.attendeeName]
|
|
285
310
|
})]
|
|
311
|
+
}),
|
|
312
|
+
e.status === "cancelled" && e.notes && /* @__PURE__ */ v("p", {
|
|
313
|
+
className: "mt-2 whitespace-pre-line text-xs text-text-secondary",
|
|
314
|
+
children: [
|
|
315
|
+
/* @__PURE__ */ _("span", {
|
|
316
|
+
className: "font-medium",
|
|
317
|
+
children: "Note:"
|
|
318
|
+
}),
|
|
319
|
+
" ",
|
|
320
|
+
e.notes
|
|
321
|
+
]
|
|
286
322
|
})
|
|
287
323
|
]
|
|
288
324
|
})]
|
|
289
|
-
}), r && /* @__PURE__ */
|
|
325
|
+
}), r && /* @__PURE__ */ v("div", {
|
|
290
326
|
className: "flex shrink-0 flex-wrap gap-2",
|
|
291
|
-
children: [
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
className: "
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
327
|
+
children: [
|
|
328
|
+
e.status === "requested" && /* @__PURE__ */ v(le, { children: [/* @__PURE__ */ v(y, {
|
|
329
|
+
size: "sm",
|
|
330
|
+
disabled: R,
|
|
331
|
+
onClick: () => Ee(e),
|
|
332
|
+
children: [/* @__PURE__ */ _(re, { className: "h-4 w-4" }), /* @__PURE__ */ _("span", {
|
|
333
|
+
className: "ml-1",
|
|
334
|
+
children: L ? "Confirming…" : "Confirm"
|
|
335
|
+
})]
|
|
336
|
+
}), /* @__PURE__ */ v(y, {
|
|
337
|
+
variant: "outline",
|
|
338
|
+
size: "sm",
|
|
339
|
+
disabled: R,
|
|
340
|
+
onClick: () => {
|
|
341
|
+
W(e), K("");
|
|
342
|
+
},
|
|
343
|
+
children: [/* @__PURE__ */ _(se, { className: "h-4 w-4" }), /* @__PURE__ */ _("span", {
|
|
344
|
+
className: "ml-1",
|
|
345
|
+
children: "Decline"
|
|
346
|
+
})]
|
|
347
|
+
})] }),
|
|
348
|
+
/* @__PURE__ */ v(y, {
|
|
349
|
+
variant: "outline",
|
|
350
|
+
size: "sm",
|
|
351
|
+
disabled: R,
|
|
352
|
+
onClick: () => Ce(e),
|
|
353
|
+
children: [/* @__PURE__ */ _(d, { className: "h-4 w-4" }), /* @__PURE__ */ _("span", {
|
|
354
|
+
className: "ml-1",
|
|
355
|
+
children: "Reschedule"
|
|
356
|
+
})]
|
|
357
|
+
}),
|
|
358
|
+
/* @__PURE__ */ v(y, {
|
|
359
|
+
variant: "outline",
|
|
360
|
+
size: "sm",
|
|
361
|
+
disabled: R,
|
|
362
|
+
onClick: () => Te(e),
|
|
363
|
+
children: [/* @__PURE__ */ _(g, { className: "h-4 w-4" }), /* @__PURE__ */ _("span", {
|
|
364
|
+
className: "ml-1",
|
|
365
|
+
children: "Cancel"
|
|
366
|
+
})]
|
|
367
|
+
})
|
|
368
|
+
]
|
|
309
369
|
})]
|
|
310
370
|
})
|
|
311
371
|
}, e.id);
|
|
312
|
-
}), !
|
|
313
|
-
page:
|
|
314
|
-
pageSize:
|
|
315
|
-
hasNextPage:
|
|
316
|
-
hasPreviousPage:
|
|
317
|
-
onPageChange:
|
|
318
|
-
onPageSizeChange:
|
|
372
|
+
}), !Y && /* @__PURE__ */ _(de, {
|
|
373
|
+
page: j.page,
|
|
374
|
+
pageSize: j.pageSize,
|
|
375
|
+
hasNextPage: P.length >= j.pageSize,
|
|
376
|
+
hasPreviousPage: j.page > 1,
|
|
377
|
+
onPageChange: j.setPage,
|
|
378
|
+
onPageSizeChange: j.setPageSize
|
|
319
379
|
})]
|
|
320
380
|
}),
|
|
321
|
-
/* @__PURE__ */ x
|
|
322
|
-
open:
|
|
323
|
-
onOpenChange: (e) => !e &&
|
|
324
|
-
children: /* @__PURE__ */
|
|
381
|
+
/* @__PURE__ */ _(x, {
|
|
382
|
+
open: z !== null,
|
|
383
|
+
onOpenChange: (e) => !e && Q(),
|
|
384
|
+
children: /* @__PURE__ */ _(S, {
|
|
325
385
|
className: "sm:max-w-md",
|
|
326
|
-
children: /* @__PURE__ */
|
|
327
|
-
onSubmit:
|
|
386
|
+
children: /* @__PURE__ */ v("form", {
|
|
387
|
+
onSubmit: we,
|
|
328
388
|
children: [
|
|
329
|
-
/* @__PURE__ */
|
|
389
|
+
/* @__PURE__ */ v(T, { children: [/* @__PURE__ */ _(E, { children: "Reschedule viewing" }), /* @__PURE__ */ v(C, { children: [
|
|
330
390
|
"Pick a new date and time for ",
|
|
331
|
-
|
|
391
|
+
z?.property?.title ?? "this viewing",
|
|
332
392
|
"."
|
|
333
393
|
] })] }),
|
|
334
|
-
/* @__PURE__ */
|
|
394
|
+
/* @__PURE__ */ v("div", {
|
|
335
395
|
className: "py-2",
|
|
336
|
-
children: [/* @__PURE__ */
|
|
396
|
+
children: [/* @__PURE__ */ _("label", {
|
|
337
397
|
className: "mb-1.5 block text-sm font-medium text-text-primary",
|
|
338
398
|
children: "New date & time *"
|
|
339
|
-
}), /* @__PURE__ */
|
|
399
|
+
}), /* @__PURE__ */ _("input", {
|
|
340
400
|
type: "datetime-local",
|
|
341
|
-
value:
|
|
342
|
-
onChange: (e) =>
|
|
343
|
-
className:
|
|
401
|
+
value: V,
|
|
402
|
+
onChange: (e) => H(e.target.value),
|
|
403
|
+
className: me
|
|
344
404
|
})]
|
|
345
405
|
}),
|
|
346
|
-
/* @__PURE__ */
|
|
406
|
+
/* @__PURE__ */ v(w, { children: [/* @__PURE__ */ _(y, {
|
|
347
407
|
type: "button",
|
|
348
408
|
variant: "outline",
|
|
349
|
-
onClick:
|
|
409
|
+
onClick: Q,
|
|
410
|
+
disabled: R,
|
|
350
411
|
children: "Cancel"
|
|
351
|
-
}), /* @__PURE__ */
|
|
412
|
+
}), /* @__PURE__ */ _(y, {
|
|
413
|
+
type: "submit",
|
|
414
|
+
disabled: R,
|
|
415
|
+
children: L ? "Saving…" : "Reschedule"
|
|
416
|
+
})] })
|
|
417
|
+
]
|
|
418
|
+
})
|
|
419
|
+
})
|
|
420
|
+
}),
|
|
421
|
+
/* @__PURE__ */ _(x, {
|
|
422
|
+
open: U !== null,
|
|
423
|
+
onOpenChange: (e) => !e && $(),
|
|
424
|
+
children: /* @__PURE__ */ _(S, {
|
|
425
|
+
className: "sm:max-w-md",
|
|
426
|
+
children: /* @__PURE__ */ v("form", {
|
|
427
|
+
onSubmit: De,
|
|
428
|
+
children: [
|
|
429
|
+
/* @__PURE__ */ v(T, { children: [/* @__PURE__ */ _(E, { children: "Decline viewing request" }), /* @__PURE__ */ v(C, { children: [
|
|
430
|
+
"Decline the requested viewing for ",
|
|
431
|
+
U?.property?.title ?? "this property",
|
|
432
|
+
". The attendee sees the note you leave here."
|
|
433
|
+
] })] }),
|
|
434
|
+
/* @__PURE__ */ v("div", {
|
|
435
|
+
className: "py-2",
|
|
436
|
+
children: [/* @__PURE__ */ _("label", {
|
|
437
|
+
className: "mb-1.5 block text-sm font-medium text-text-primary",
|
|
438
|
+
htmlFor: "decline-reason",
|
|
439
|
+
children: "Reason (optional)"
|
|
440
|
+
}), /* @__PURE__ */ _("textarea", {
|
|
441
|
+
id: "decline-reason",
|
|
442
|
+
value: G,
|
|
443
|
+
onChange: (e) => K(e.target.value),
|
|
444
|
+
rows: 3,
|
|
445
|
+
maxLength: 480,
|
|
446
|
+
placeholder: "e.g. Agent unavailable that slot — suggest a new time",
|
|
447
|
+
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)]"
|
|
448
|
+
})]
|
|
449
|
+
}),
|
|
450
|
+
/* @__PURE__ */ v(w, { children: [/* @__PURE__ */ _(y, {
|
|
451
|
+
type: "button",
|
|
452
|
+
variant: "outline",
|
|
453
|
+
onClick: $,
|
|
454
|
+
disabled: R,
|
|
455
|
+
children: "Keep request"
|
|
456
|
+
}), /* @__PURE__ */ _(y, {
|
|
352
457
|
type: "submit",
|
|
353
|
-
|
|
354
|
-
|
|
458
|
+
variant: "destructive",
|
|
459
|
+
disabled: R,
|
|
460
|
+
children: L ? "Declining…" : "Decline viewing"
|
|
355
461
|
})] })
|
|
356
462
|
]
|
|
357
463
|
})
|
|
@@ -361,6 +467,6 @@ function I() {
|
|
|
361
467
|
});
|
|
362
468
|
}
|
|
363
469
|
//#endregion
|
|
364
|
-
export {
|
|
470
|
+
export { A as ViewingsPage };
|
|
365
471
|
|
|
366
472
|
//# sourceMappingURL=ViewingsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ViewingsPage.js","names":[],"sources":["../../../../src/housingvista/pages/viewings/ViewingsPage.tsx"],"sourcesContent":["/**\n * Viewings — track property viewings you have booked. Reschedule (update) or\n * cancel a viewing. Server-side status filter + pagination, plus an optional\n * week strip that lets you jump to a day and see which days have viewings. This\n * is where the \"book a viewing\" story's \"track your viewings\" lands.\n * @module housingvista\n */\n\nimport { useMemo, useState, type FormEvent } from 'react';\nimport { useNavigate } from 'react-router';\nimport { CalendarClock, MapPin, Video, Users, X, CalendarDays, ChevronLeft, ChevronRight, Home } from 'lucide-react';\nimport {\n Button,\n Pagination,\n EmphasisPanel,\n GlassCard,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout, StatCard, StatusBadge, EmptyState } from '../../components';\nimport { useViewings, useUpdateViewing, useCancelViewing, type ViewingRow } from '../../hooks';\nimport { useServerPagination } from '../../hooks/useServerPagination';\nimport { notify } from '../../lib/toast';\nimport { cn, formatDateTime } from '../../utils/helpers';\nimport { useTrackPageView } from '../../hooks/useHousingVistaAnalytics';\n\nconst statusTabs: { value: string; label: string }[] = [\n { value: 'all', label: 'All' },\n { value: 'requested', label: 'Requested' },\n { value: 'confirmed', label: 'Confirmed' },\n { value: 'completed', label: 'Completed' },\n { value: 'cancelled', label: 'Cancelled' },\n];\n\nfunction statusVariant(status: ViewingRow['status']): 'success' | 'warning' | 'info' | 'muted' {\n switch (status) {\n case 'confirmed':\n return 'success';\n case 'requested':\n return 'warning';\n case 'completed':\n return 'info';\n default:\n return 'muted';\n }\n}\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\n/** ISO string → value for `datetime-local` input (local time, no seconds). */\nfunction toLocalInput(iso: string): string {\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) return '';\n const offset = date.getTimezoneOffset();\n return new Date(date.getTime() - offset * 60000).toISOString().slice(0, 16);\n}\n\n// ---- Week strip helpers ----\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst WEEKDAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];\n\nfunction startOfWeek(date: Date): Date {\n const copy = new Date(date.getFullYear(), date.getMonth(), date.getDate());\n copy.setDate(copy.getDate() - copy.getDay());\n return copy;\n}\n\nfunction sameDay(a: Date, b: Date): boolean {\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();\n}\n\nexport function ViewingsPage() {\n useTrackPageView('housingvista.viewings.viewed');\n const navigate = useNavigate();\n const pagination = useServerPagination(10);\n const [activeTab, setActiveTab] = useState('all');\n const { data: viewings, loading } = useViewings({\n filter: activeTab === 'all' ? undefined : { status: activeTab },\n pagination: pagination.pagination,\n });\n const { updateViewing, loading: updating } = useUpdateViewing();\n const { cancelViewing, loading: cancelling } = useCancelViewing();\n\n const [rescheduleTarget, setRescheduleTarget] = useState<ViewingRow | null>(null);\n const [scheduledAt, setScheduledAt] = useState('');\n\n // Week strip state — a light day filter over the loaded viewings.\n const [weekStart, setWeekStart] = useState(() => startOfWeek(new Date()));\n const [selectedDay, setSelectedDay] = useState<Date | null>(null);\n\n const weekDays = useMemo(\n () => Array.from({ length: 7 }, (_, i) => new Date(weekStart.getTime() + i * DAY_MS)),\n [weekStart]\n );\n\n const daysWithViewings = useMemo(() => {\n const set = new Set<string>();\n for (const v of viewings) {\n const d = new Date(v.scheduledAt);\n if (!Number.isNaN(d.getTime())) set.add(`${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`);\n }\n return set;\n }, [viewings]);\n\n const shownViewings = useMemo(\n () => (selectedDay ? viewings.filter((v) => sameDay(new Date(v.scheduledAt), selectedDay)) : viewings),\n [viewings, selectedDay]\n );\n\n const monthLabel = weekStart.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });\n\n const openReschedule = (viewing: ViewingRow) => {\n setRescheduleTarget(viewing);\n setScheduledAt(toLocalInput(viewing.scheduledAt));\n };\n\n const handleReschedule = async (event: FormEvent) => {\n event.preventDefault();\n if (!rescheduleTarget) return;\n if (!scheduledAt) {\n notify.warning('Pick a new date and time');\n return;\n }\n try {\n await updateViewing(rescheduleTarget.id, { scheduledAt: new Date(scheduledAt).toISOString() });\n notify.success('Viewing rescheduled');\n setRescheduleTarget(null);\n } catch (err) {\n notify.error('Could not reschedule', (err as Error).message);\n }\n };\n\n const handleCancel = async (viewing: ViewingRow) => {\n try {\n await cancelViewing(viewing.id);\n notify.success('Viewing cancelled');\n } catch (err) {\n notify.error('Could not cancel viewing', (err as Error).message);\n }\n };\n\n const upcoming = viewings.filter((v) => v.status === 'requested' || v.status === 'confirmed').length;\n\n return (\n <PageLayout\n title=\"Viewings\"\n description=\"Track and manage the property viewings you have booked\"\n purpose=\"See every viewing you have booked, in person or virtual, with its date and status. Jump to a day, reschedule when your plans change, or cancel one you no longer need.\"\n actions={\n <Button variant=\"outline\" onClick={() => navigate('../properties')}>\n <Home className=\"h-4 w-4\" />\n <span className=\"ml-1\">Book a viewing</span>\n </Button>\n }\n >\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <StatCard label=\"Total viewings\" value={viewings.length} icon={<CalendarDays className=\"h-4 w-4\" />} />\n <StatCard label=\"Upcoming\" value={upcoming} icon={<CalendarClock className=\"h-4 w-4\" />} />\n <StatCard\n label=\"Virtual\"\n value={viewings.filter((v) => v.type === 'virtual').length}\n icon={<Video className=\"h-4 w-4\" />}\n />\n </div>\n </EmphasisPanel>\n\n {/* Week strip */}\n <GlassCard treatment=\"plain\" className=\"p-3\">\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-sm font-semibold text-text-primary\">{monthLabel}</span>\n <div className=\"flex items-center gap-1\">\n {selectedDay && (\n <button\n type=\"button\"\n onClick={() => setSelectedDay(null)}\n className=\"mr-1 rounded-full bg-bg-sunken px-2.5 py-1 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n All days\n </button>\n )}\n <button\n type=\"button\"\n aria-label=\"Previous week\"\n onClick={() => setWeekStart(new Date(weekStart.getTime() - 7 * DAY_MS))}\n className=\"rounded-md p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n </button>\n <button\n type=\"button\"\n aria-label=\"Next week\"\n onClick={() => setWeekStart(new Date(weekStart.getTime() + 7 * DAY_MS))}\n className=\"rounded-md p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n >\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n <div className=\"grid grid-cols-7 gap-1.5\">\n {weekDays.map((day) => {\n const key = `${day.getFullYear()}-${day.getMonth()}-${day.getDate()}`;\n const isToday = sameDay(day, new Date());\n const isSelected = selectedDay ? sameDay(day, selectedDay) : false;\n const hasViewings = daysWithViewings.has(key);\n return (\n <button\n key={key}\n type=\"button\"\n onClick={() => setSelectedDay(isSelected ? null : day)}\n aria-pressed={isSelected}\n className={cn(\n 'flex flex-col items-center gap-0.5 rounded-lg py-2 text-center transition-colors',\n isSelected\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:bg-bg-sunken'\n )}\n >\n <span className=\"text-[0.65rem] font-medium uppercase\">{WEEKDAYS[day.getDay()]}</span>\n <span className={cn('text-sm font-semibold tabular-nums', !isSelected && isToday && 'text-primary')}>\n {day.getDate()}\n </span>\n <span\n className={cn(\n 'h-1 w-1 rounded-full',\n hasViewings ? (isSelected ? 'bg-action-primary-text' : 'bg-status-success') : 'bg-transparent'\n )}\n />\n </button>\n );\n })}\n </div>\n </GlassCard>\n\n <div className=\"-mx-1 flex flex-nowrap items-center gap-2 overflow-x-auto px-1 pb-1 sm:flex-wrap sm:overflow-visible\">\n {statusTabs.map((tab) => (\n <button\n key={tab.value}\n onClick={() => {\n setActiveTab(tab.value);\n pagination.setPage(1);\n }}\n className={cn(\n 'shrink-0 rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n activeTab === tab.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {tab.label}\n </button>\n ))}\n </div>\n\n {viewings.length === 0 && !loading ? (\n <EmptyState\n illustration=\"empty-generic\"\n icon={<CalendarClock className=\"h-12 w-12\" />}\n title=\"No viewings booked\"\n description=\"Book a viewing from any property page and it will show up here to track.\"\n action={<Button onClick={() => navigate('../properties')}>Browse properties</Button>}\n />\n ) : shownViewings.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n icon={<CalendarClock className=\"h-12 w-12\" />}\n title=\"No viewings that day\"\n description=\"There are no viewings scheduled for the selected day. Pick another day or clear the filter.\"\n action={<Button onClick={() => setSelectedDay(null)}>Show all days</Button>}\n />\n ) : (\n <div className=\"space-y-4\">\n {shownViewings.map((viewing) => {\n const image = viewing.property?.images.find((img) => img.isPrimary) ?? viewing.property?.images[0];\n const canManage = viewing.status === 'requested' || viewing.status === 'confirmed';\n const isVirtual = viewing.type === 'virtual';\n return (\n <GlassCard key={viewing.id} treatment={canManage ? 'insight' : 'plain'} className=\"p-5\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n <button\n type=\"button\"\n onClick={() => viewing.property && navigate(`../properties/${viewing.property.id}`)}\n className=\"flex flex-1 items-start gap-3 text-left\"\n >\n {image ? (\n <img src={image.url} alt=\"\" className=\"h-16 w-16 rounded-lg object-cover\" />\n ) : (\n <div className=\"flex h-16 w-16 items-center justify-center rounded-lg bg-bg-sunken\">\n <Home className=\"h-6 w-6 text-text-secondary\" />\n </div>\n )}\n <div className=\"min-w-0\">\n <div className=\"mb-1 flex flex-wrap items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary\">{viewing.property?.title ?? 'Property'}</h3>\n <StatusBadge label={viewing.status} variant={statusVariant(viewing.status)} dot />\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\">\n {isVirtual ? <Video className=\"h-3 w-3\" /> : <Users className=\"h-3 w-3\" />}\n {isVirtual ? 'Virtual' : 'In person'}\n </span>\n </div>\n {viewing.property && (\n <p className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <MapPin className=\"h-3 w-3\" />\n {viewing.property.location.city}, {viewing.property.location.state}\n </p>\n )}\n <div className=\"mt-2 flex flex-wrap gap-4 text-sm text-text-secondary\">\n <span className=\"flex items-center gap-1 font-medium text-text-primary\">\n <CalendarClock className=\"h-3.5 w-3.5\" />\n {formatDateTime(viewing.scheduledAt)}\n </span>\n {viewing.attendeeName && (\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3.5 w-3.5\" />\n {viewing.attendeeName}\n </span>\n )}\n </div>\n </div>\n </button>\n\n {canManage && (\n <div className=\"flex shrink-0 flex-wrap gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => openReschedule(viewing)}>\n <CalendarClock className=\"h-4 w-4\" />\n <span className=\"ml-1\">Reschedule</span>\n </Button>\n <Button variant=\"outline\" size=\"sm\" disabled={cancelling} onClick={() => handleCancel(viewing)}>\n <X className=\"h-4 w-4\" />\n <span className=\"ml-1\">Cancel</span>\n </Button>\n </div>\n )}\n </div>\n </GlassCard>\n );\n })}\n\n {!selectedDay && (\n <Pagination\n page={pagination.page}\n pageSize={pagination.pageSize}\n hasNextPage={viewings.length >= pagination.pageSize}\n hasPreviousPage={pagination.page > 1}\n onPageChange={pagination.setPage}\n onPageSizeChange={pagination.setPageSize}\n />\n )}\n </div>\n )}\n\n {/* Reschedule */}\n <ResponsiveDialog open={rescheduleTarget !== null} onOpenChange={(open) => !open && setRescheduleTarget(null)}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleReschedule}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Reschedule viewing</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Pick a new date and time for {rescheduleTarget?.property?.title ?? 'this viewing'}.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"py-2\">\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">New date & time *</label>\n <input\n type=\"datetime-local\"\n value={scheduledAt}\n onChange={(event) => setScheduledAt(event.target.value)}\n className={inputClass}\n />\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setRescheduleTarget(null)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={updating}>\n {updating ? 'Saving…' : 'Reschedule'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA8BA,IAAM,IAAiD;CACrD;EAAE,OAAO;EAAO,OAAO;EAAO;CAC9B;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAa,OAAO;EAAa;CAC3C;AAED,SAAS,EAAc,GAAwE;AAC7F,SAAQ,GAAR;EACE,KAAK,YACH,QAAO;EACT,KAAK,YACH,QAAO;EACT,KAAK,YACH,QAAO;EACT,QACE,QAAO;;;AAIb,IAAM,IACJ;AAGF,SAAS,EAAa,GAAqB;CACzC,IAAM,IAAO,IAAI,KAAK,EAAI;AAC1B,KAAI,OAAO,MAAM,EAAK,SAAS,CAAC,CAAE,QAAO;CACzC,IAAM,IAAS,EAAK,mBAAmB;AACvC,yBAAO,IAAI,KAAK,EAAK,SAAS,GAAG,IAAS,IAAM,EAAC,aAAa,CAAC,MAAM,GAAG,GAAG;;AAI7E,IAAM,IAAS,OAAU,KAAK,KACxB,IAAW;CAAC;CAAO;CAAO;CAAO;CAAO;CAAO;CAAO;CAAM;AAElE,SAAS,EAAY,GAAkB;CACrC,IAAM,IAAO,IAAI,KAAK,EAAK,aAAa,EAAE,EAAK,UAAU,EAAE,EAAK,SAAS,CAAC;AAE1E,QADA,EAAK,QAAQ,EAAK,SAAS,GAAG,EAAK,QAAQ,CAAC,EACrC;;AAGT,SAAS,EAAQ,GAAS,GAAkB;AAC1C,QAAO,EAAE,aAAa,KAAK,EAAE,aAAa,IAAI,EAAE,UAAU,KAAK,EAAE,UAAU,IAAI,EAAE,SAAS,KAAK,EAAE,SAAS;;AAG5G,SAAgB,IAAe;AAC7B,GAAiB,+BAA+B;CAChD,IAAM,IAAW,GAAa,EACxB,IAAa,EAAoB,GAAG,EACpC,CAAC,GAAW,KAAgB,EAAS,MAAM,EAC3C,EAAE,MAAM,GAAU,eAAY,EAAY;EAC9C,QAAQ,MAAc,QAAQ,KAAA,IAAY,EAAE,QAAQ,GAAW;EAC/D,YAAY,EAAW;EACxB,CAAC,EACI,EAAE,mBAAe,SAAS,MAAa,GAAkB,EACzD,EAAE,kBAAe,SAAS,MAAe,GAAkB,EAE3D,CAAC,GAAkB,KAAuB,EAA4B,KAAK,EAC3E,CAAC,GAAa,KAAkB,EAAS,GAAG,EAG5C,CAAC,GAAW,KAAgB,QAAe,kBAAY,IAAI,MAAM,CAAC,CAAC,EACnE,CAAC,GAAa,KAAkB,EAAsB,KAAK,EAE3D,KAAW,QACT,MAAM,KAAK,EAAE,QAAQ,GAAG,GAAG,GAAG,MAAM,IAAI,KAAK,EAAU,SAAS,GAAG,IAAI,EAAO,CAAC,EACrF,CAAC,EAAU,CACZ,EAEK,KAAmB,QAAc;EACrC,IAAM,oBAAM,IAAI,KAAa;AAC7B,OAAK,IAAM,KAAK,GAAU;GACxB,IAAM,IAAI,IAAI,KAAK,EAAE,YAAY;AACjC,GAAK,OAAO,MAAM,EAAE,SAAS,CAAC,IAAE,EAAI,IAAI,GAAG,EAAE,aAAa,CAAC,GAAG,EAAE,UAAU,CAAC,GAAG,EAAE,SAAS,GAAG;;AAE9F,SAAO;IACN,CAAC,EAAS,CAAC,EAER,IAAgB,QACb,IAAc,EAAS,QAAQ,MAAM,EAAQ,IAAI,KAAK,EAAE,YAAY,EAAE,EAAY,CAAC,GAAG,GAC7F,CAAC,GAAU,EAAY,CACxB,EAEK,KAAa,EAAU,mBAAmB,SAAS;EAAE,OAAO;EAAQ,MAAM;EAAW,CAAC,EAEtF,MAAkB,MAAwB;AAE9C,EADA,EAAoB,EAAQ,EAC5B,EAAe,EAAa,EAAQ,YAAY,CAAC;IAG7C,KAAmB,OAAO,MAAqB;AACnD,QAAM,gBAAgB,EACjB,GACL;OAAI,CAAC,GAAa;AAChB,MAAO,QAAQ,2BAA2B;AAC1C;;AAEF,OAAI;AAGF,IAFA,MAAM,GAAc,EAAiB,IAAI,EAAE,aAAa,IAAI,KAAK,EAAY,CAAC,aAAa,EAAE,CAAC,EAC9F,EAAO,QAAQ,sBAAsB,EACrC,EAAoB,KAAK;YAClB,GAAK;AACZ,MAAO,MAAM,wBAAyB,EAAc,QAAQ;;;IAI1D,KAAe,OAAO,MAAwB;AAClD,MAAI;AAEF,GADA,MAAM,EAAc,EAAQ,GAAG,EAC/B,EAAO,QAAQ,oBAAoB;WAC5B,GAAK;AACZ,KAAO,MAAM,4BAA6B,EAAc,QAAQ;;IAI9D,KAAW,EAAS,QAAQ,MAAM,EAAE,WAAW,eAAe,EAAE,WAAW,YAAY,CAAC;AAE9F,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;EACR,SACE,kBAAC,GAAD;GAAQ,SAAQ;GAAU,eAAe,EAAS,gBAAgB;aAAlE,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAC5B,kBAAC,QAAD;IAAM,WAAU;cAAO;IAAqB,CAAA,CACrC;;YARb;GAWE,kBAAC,GAAD;IAAe,WAAU;cACvB,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAU,OAAM;OAAiB,OAAO,EAAS;OAAQ,MAAM,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA;OAAI,CAAA;MACvG,kBAAC,GAAD;OAAU,OAAM;OAAW,OAAO;OAAU,MAAM,kBAAC,GAAD,EAAe,WAAU,WAAY,CAAA;OAAI,CAAA;MAC3F,kBAAC,GAAD;OACE,OAAM;OACN,OAAO,EAAS,QAAQ,MAAM,EAAE,SAAS,UAAU,CAAC;OACpD,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;OACnC,CAAA;MACE;;IACQ,CAAA;GAGhB,kBAAC,GAAD;IAAW,WAAU;IAAQ,WAAU;cAAvC,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MAAM,WAAU;gBAA2C;MAAkB,CAAA,EAC7E,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACG,KACC,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAe,KAAK;QACnC,WAAU;kBACX;QAEQ,CAAA;OAEX,kBAAC,UAAD;QACE,MAAK;QACL,cAAW;QACX,eAAe,kBAAa,IAAI,KAAK,EAAU,SAAS,GAAG,IAAI,EAAO,CAAC;QACvE,WAAU;kBAEV,kBAAC,IAAD,EAAa,WAAU,WAAY,CAAA;QAC5B,CAAA;OACT,kBAAC,UAAD;QACE,MAAK;QACL,cAAW;QACX,eAAe,EAAa,IAAI,KAAK,EAAU,SAAS,GAAG,IAAI,EAAO,CAAC;QACvE,WAAU;kBAEV,kBAAC,IAAD,EAAc,WAAU,WAAY,CAAA;QAC7B,CAAA;OACL;QACF;QACN,kBAAC,OAAD;KAAK,WAAU;eACZ,GAAS,KAAK,MAAQ;MACrB,IAAM,IAAM,GAAG,EAAI,aAAa,CAAC,GAAG,EAAI,UAAU,CAAC,GAAG,EAAI,SAAS,IAC7D,IAAU,EAAQ,mBAAK,IAAI,MAAM,CAAC,EAClC,IAAa,IAAc,EAAQ,GAAK,EAAY,GAAG,IACvD,IAAc,GAAiB,IAAI,EAAI;AAC7C,aACE,kBAAC,UAAD;OAEE,MAAK;OACL,eAAe,EAAe,IAAa,OAAO,EAAI;OACtD,gBAAc;OACd,WAAW,EACT,oFACA,IACI,kDACA,yCACL;iBAVH;QAYE,kBAAC,QAAD;SAAM,WAAU;mBAAwC,EAAS,EAAI,QAAQ;SAAS,CAAA;QACtF,kBAAC,QAAD;SAAM,WAAW,EAAG,sCAAsC,CAAC,KAAc,KAAW,eAAe;mBAChG,EAAI,SAAS;SACT,CAAA;QACP,kBAAC,QAAD,EACE,WAAW,EACT,wBACA,IAAe,IAAa,2BAA2B,sBAAuB,iBAC/E,EACD,CAAA;QACK;SArBF,EAqBE;OAEX;KACE,CAAA,CACI;;GAEZ,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAW,KAAK,MACf,kBAAC,UAAD;KAEE,eAAe;AAEb,MADA,EAAa,EAAI,MAAM,EACvB,EAAW,QAAQ,EAAE;;KAEvB,WAAW,EACT,2EACA,MAAc,EAAI,QACd,kDACA,2DACL;eAEA,EAAI;KACE,EAbF,EAAI,MAaF,CACT;IACE,CAAA;GAEL,EAAS,WAAW,KAAK,CAAC,IACzB,kBAAC,GAAD;IACE,cAAa;IACb,MAAM,kBAAC,GAAD,EAAe,WAAU,aAAc,CAAA;IAC7C,OAAM;IACN,aAAY;IACZ,QAAQ,kBAAC,GAAD;KAAQ,eAAe,EAAS,gBAAgB;eAAE;KAA0B,CAAA;IACpF,CAAA,GACA,EAAc,WAAW,IAC3B,kBAAC,GAAD;IACE,cAAa;IACb,MAAM,kBAAC,GAAD,EAAe,WAAU,aAAc,CAAA;IAC7C,OAAM;IACN,aAAY;IACZ,QAAQ,kBAAC,GAAD;KAAQ,eAAe,EAAe,KAAK;eAAE;KAAsB,CAAA;IAC3E,CAAA,GAEF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,EAAc,KAAK,MAAY;KAC9B,IAAM,IAAQ,EAAQ,UAAU,OAAO,MAAM,MAAQ,EAAI,UAAU,IAAI,EAAQ,UAAU,OAAO,IAC1F,IAAY,EAAQ,WAAW,eAAe,EAAQ,WAAW,aACjE,IAAY,EAAQ,SAAS;AACnC,YACE,kBAAC,GAAD;MAA4B,WAAW,IAAY,YAAY;MAAS,WAAU;gBAChF,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAQ,YAAY,EAAS,iBAAiB,EAAQ,SAAS,KAAK;QACnF,WAAU;kBAHZ,CAKG,IACC,kBAAC,OAAD;SAAK,KAAK,EAAM;SAAK,KAAI;SAAG,WAAU;SAAsC,CAAA,GAE5E,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD,EAAM,WAAU,+BAAgC,CAAA;SAC5C,CAAA,EAER,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,OAAD;WAAK,WAAU;qBAAf;YACE,kBAAC,MAAD;aAAI,WAAU;uBAAmC,EAAQ,UAAU,SAAS;aAAgB,CAAA;YAC5F,kBAAC,GAAD;aAAa,OAAO,EAAQ;aAAQ,SAAS,EAAc,EAAQ,OAAO;aAAE,KAAA;aAAM,CAAA;YAClF,kBAAC,QAAD;aAAM,WAAU;uBAAhB,CACe,EAAZ,IAAa,IAAgC,GAAjC,EAAO,WAAU,WAAY,CAAgC,EACzE,IAAY,YAAY,YACpB;;YACH;;UACL,EAAQ,YACP,kBAAC,KAAD;WAAG,WAAU;qBAAb;YACE,kBAAC,IAAD,EAAQ,WAAU,WAAY,CAAA;YAC7B,EAAQ,SAAS,SAAS;YAAK;YAAG,EAAQ,SAAS,SAAS;YAC3D;;UAEN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,QAAD;YAAM,WAAU;sBAAhB,CACE,kBAAC,GAAD,EAAe,WAAU,eAAgB,CAAA,EACxC,EAAe,EAAQ,YAAY,CAC/B;eACN,EAAQ,gBACP,kBAAC,QAAD;YAAM,WAAU;sBAAhB,CACE,kBAAC,GAAD,EAAO,WAAU,eAAgB,CAAA,EAChC,EAAQ,aACJ;cAEL;;UACF;WACC;WAER,KACC,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAQ,SAAQ;SAAU,MAAK;SAAK,eAAe,GAAe,EAAQ;mBAA1E,CACE,kBAAC,GAAD,EAAe,WAAU,WAAY,CAAA,EACrC,kBAAC,QAAD;UAAM,WAAU;oBAAO;UAAiB,CAAA,CACjC;YACT,kBAAC,GAAD;SAAQ,SAAQ;SAAU,MAAK;SAAK,UAAU;SAAY,eAAe,GAAa,EAAQ;mBAA9F,CACE,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA,EACzB,kBAAC,QAAD;UAAM,WAAU;oBAAO;UAAa,CAAA,CAC7B;WACL;UAEJ;;MACI,EAzDI,EAAQ,GAyDZ;MAEd,EAED,CAAC,KACA,kBAAC,GAAD;KACE,MAAM,EAAW;KACjB,UAAU,EAAW;KACrB,aAAa,EAAS,UAAU,EAAW;KAC3C,iBAAiB,EAAW,OAAO;KACnC,cAAc,EAAW;KACzB,kBAAkB,EAAW;KAC7B,CAAA,CAEA;;GAIR,kBAAC,IAAD;IAAkB,MAAM,MAAqB;IAAM,eAAe,MAAS,CAAC,KAAQ,EAAoB,KAAK;cAC3G,kBAAC,IAAD;KAAyB,WAAU;eACjC,kBAAC,QAAD;MAAM,UAAU;gBAAhB;OACE,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,sBAA0C,CAAA,EACjE,kBAAC,IAAD,EAAA,UAAA;QAA6B;QACG,GAAkB,UAAU,SAAS;QAAe;QACtD,EAAA,CAAA,CACP,EAAA,CAAA;OACzB,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,SAAD;SAAO,WAAU;mBAAqD;SAA6B,CAAA,EACnG,kBAAC,SAAD;SACE,MAAK;SACL,OAAO;SACP,WAAW,MAAU,EAAe,EAAM,OAAO,MAAM;SACvD,WAAW;SACX,CAAA,CACE;;OACN,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,GAAD;QAAQ,MAAK;QAAS,SAAQ;QAAU,eAAe,EAAoB,KAAK;kBAAE;QAEzE,CAAA,EACT,kBAAC,GAAD;QAAQ,MAAK;QAAS,UAAU;kBAC7B,IAAW,YAAY;QACjB,CAAA,CACc,EAAA,CAAA;OACpB;;KACiB,CAAA;IACT,CAAA;GACR"}
|
|
1
|
+
{"version":3,"file":"ViewingsPage.js","names":[],"sources":["../../../../src/housingvista/pages/viewings/ViewingsPage.tsx"],"sourcesContent":["/**\n * Viewings — track property viewings. Buyer side: reschedule (update) or cancel\n * one you booked. Host side (agent/owner): confirm or decline incoming\n * `requested` viewings for the listings you manage. Server-side status filter +\n * pagination, plus an optional week strip that lets you jump to a day and see\n * which days have viewings. This is where the \"book a viewing\" story's\n * \"track your viewings\" and the host disposition step both land.\n * @module housingvista\n */\n\nimport { useMemo, useState, type FormEvent } from 'react';\nimport { useNavigate } from 'react-router';\nimport {\n CalendarClock,\n MapPin,\n Video,\n Users,\n X,\n XCircle,\n Check,\n CalendarDays,\n ChevronLeft,\n ChevronRight,\n Home,\n} from 'lucide-react';\nimport {\n Button,\n Pagination,\n EmphasisPanel,\n GlassCard,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout, StatCard, StatusBadge, EmptyState } from '../../components';\nimport { useViewings, useUpdateViewing, useCancelViewing, type ViewingRow } from '../../hooks';\nimport { useServerPagination } from '../../hooks/useServerPagination';\nimport { notify } from '../../lib/toast';\nimport { cn, formatDateTime } from '../../utils/helpers';\nimport { useTrackPageView } from '../../hooks/useHousingVistaAnalytics';\n\nconst statusTabs: { value: string; label: string }[] = [\n { value: 'all', label: 'All' },\n { value: 'requested', label: 'Requested' },\n { value: 'confirmed', label: 'Confirmed' },\n { value: 'completed', label: 'Completed' },\n { value: 'cancelled', label: 'Cancelled' },\n];\n\nfunction statusVariant(status: ViewingRow['status']): 'success' | 'warning' | 'info' | 'muted' {\n switch (status) {\n case 'confirmed':\n return 'success';\n case 'requested':\n return 'warning';\n case 'completed':\n return 'info';\n default:\n return 'muted';\n }\n}\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\n// Matches wspace-housingvista-svc's assertText default cap for `notes`.\nconst MAX_COMBINED_NOTES_LENGTH = 5000;\n\n/** ISO string → value for `datetime-local` input (local time, no seconds). */\nfunction toLocalInput(iso: string): string {\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) return '';\n const offset = date.getTimezoneOffset();\n return new Date(date.getTime() - offset * 60000).toISOString().slice(0, 16);\n}\n\n// ---- Week strip helpers ----\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst WEEKDAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];\n\nfunction startOfWeek(date: Date): Date {\n const copy = new Date(date.getFullYear(), date.getMonth(), date.getDate());\n copy.setDate(copy.getDate() - copy.getDay());\n return copy;\n}\n\nfunction sameDay(a: Date, b: Date): boolean {\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();\n}\n\nexport function ViewingsPage() {\n useTrackPageView('housingvista.viewings.viewed');\n const navigate = useNavigate();\n const pagination = useServerPagination(10);\n const [activeTab, setActiveTab] = useState('all');\n const { data: viewings, loading } = useViewings({\n filter: activeTab === 'all' ? undefined : { status: activeTab },\n pagination: pagination.pagination,\n });\n const { updateViewing, loading: updating } = useUpdateViewing();\n const { cancelViewing, loading: cancelling } = useCancelViewing();\n // One in-flight guard for every row action — confirm/decline/reschedule share\n // `updating`, cancel uses `cancelling`; no two mutations may overlap on a viewing.\n const busy = updating || cancelling;\n\n const [rescheduleTarget, setRescheduleTarget] = useState<ViewingRow | null>(null);\n const [scheduledAt, setScheduledAt] = useState('');\n const [declineTarget, setDeclineTarget] = useState<ViewingRow | null>(null);\n const [declineReason, setDeclineReason] = useState('');\n\n // Week strip state — a light day filter over the loaded viewings.\n const [weekStart, setWeekStart] = useState(() => startOfWeek(new Date()));\n const [selectedDay, setSelectedDay] = useState<Date | null>(null);\n\n const weekDays = useMemo(\n () => Array.from({ length: 7 }, (_, i) => new Date(weekStart.getTime() + i * DAY_MS)),\n [weekStart]\n );\n\n const daysWithViewings = useMemo(() => {\n const set = new Set<string>();\n for (const v of viewings) {\n const d = new Date(v.scheduledAt);\n if (!Number.isNaN(d.getTime())) set.add(`${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`);\n }\n return set;\n }, [viewings]);\n\n const shownViewings = useMemo(\n () => (selectedDay ? viewings.filter((v) => sameDay(new Date(v.scheduledAt), selectedDay)) : viewings),\n [viewings, selectedDay]\n );\n\n const monthLabel = weekStart.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });\n\n const openReschedule = (viewing: ViewingRow) => {\n setRescheduleTarget(viewing);\n setScheduledAt(toLocalInput(viewing.scheduledAt));\n };\n\n const handleReschedule = async (event: FormEvent) => {\n event.preventDefault();\n if (!rescheduleTarget) return;\n if (!scheduledAt) {\n notify.warning('Pick a new date and time');\n return;\n }\n try {\n await updateViewing(rescheduleTarget.id, { scheduledAt: new Date(scheduledAt).toISOString() });\n notify.success('Viewing rescheduled');\n setRescheduleTarget(null);\n } catch (err) {\n notify.error('Could not reschedule', (err as Error).message);\n }\n };\n\n const handleCancel = async (viewing: ViewingRow) => {\n try {\n await cancelViewing(viewing.id);\n notify.success('Viewing cancelled');\n } catch (err) {\n notify.error('Could not cancel viewing', (err as Error).message);\n }\n };\n\n const handleConfirm = async (viewing: ViewingRow) => {\n try {\n await updateViewing(viewing.id, { status: 'confirmed' });\n notify.success('Viewing confirmed', viewing.property?.title ?? undefined);\n } catch (err) {\n notify.error('Could not confirm viewing', (err as Error).message);\n }\n };\n\n const closeReschedule = () => {\n if (busy) return;\n setRescheduleTarget(null);\n };\n\n const closeDecline = () => {\n if (busy) return;\n setDeclineTarget(null);\n setDeclineReason('');\n };\n\n const handleDecline = async (event: FormEvent) => {\n event.preventDefault();\n if (!declineTarget) return;\n const reason = declineReason.trim();\n const disposition = reason ? `Declined: ${reason}` : 'Declined';\n // Append to the attendee's original booking notes — never overwrite them.\n const original = declineTarget.notes?.trim() ?? '';\n let notes = original ? `${original}\\n${disposition}` : disposition;\n // The subgraph caps notes at 5000 chars (assertText); keep the disposition\n // whole and trim the oldest booking-note text if the combined value overflows.\n if (notes.length > MAX_COMBINED_NOTES_LENGTH) {\n const keep = MAX_COMBINED_NOTES_LENGTH - disposition.length - 1;\n const tail = keep > 0 ? original.slice(Math.max(original.length - keep, 0)) : '';\n notes = tail ? `${tail}\\n${disposition}` : disposition;\n }\n try {\n await updateViewing(declineTarget.id, { status: 'cancelled', notes });\n notify.success('Viewing declined', declineTarget.property?.title ?? undefined);\n setDeclineTarget(null);\n setDeclineReason('');\n } catch (err) {\n notify.error('Could not decline viewing', (err as Error).message);\n }\n };\n\n const upcoming = viewings.filter((v) => v.status === 'requested' || v.status === 'confirmed').length;\n\n return (\n <PageLayout\n title=\"Viewings\"\n description=\"Track and manage property viewings — booked requests and the ones you host\"\n purpose=\"See every viewing in play, in person or virtual, with its date and status. Confirm or decline incoming requests on listings you manage, reschedule when plans change, or cancel one you no longer need.\"\n actions={\n <Button variant=\"outline\" onClick={() => navigate('../properties')}>\n <Home className=\"h-4 w-4\" />\n <span className=\"ml-1\">Book a viewing</span>\n </Button>\n }\n >\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <StatCard label=\"Total viewings\" value={viewings.length} icon={<CalendarDays className=\"h-4 w-4\" />} />\n <StatCard label=\"Upcoming\" value={upcoming} icon={<CalendarClock className=\"h-4 w-4\" />} />\n <StatCard\n label=\"Virtual\"\n value={viewings.filter((v) => v.type === 'virtual').length}\n icon={<Video className=\"h-4 w-4\" />}\n />\n </div>\n </EmphasisPanel>\n\n {/* Week strip */}\n <GlassCard treatment=\"plain\" className=\"p-3\">\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-sm font-semibold text-text-primary\">{monthLabel}</span>\n <div className=\"flex items-center gap-1\">\n {selectedDay && (\n <button\n type=\"button\"\n onClick={() => setSelectedDay(null)}\n className=\"mr-1 rounded-full bg-bg-sunken px-2.5 py-1 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n All days\n </button>\n )}\n <button\n type=\"button\"\n aria-label=\"Previous week\"\n onClick={() => setWeekStart(new Date(weekStart.getTime() - 7 * DAY_MS))}\n className=\"rounded-md p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n </button>\n <button\n type=\"button\"\n aria-label=\"Next week\"\n onClick={() => setWeekStart(new Date(weekStart.getTime() + 7 * DAY_MS))}\n className=\"rounded-md p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n >\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n <div className=\"grid grid-cols-7 gap-1.5\">\n {weekDays.map((day) => {\n const key = `${day.getFullYear()}-${day.getMonth()}-${day.getDate()}`;\n const isToday = sameDay(day, new Date());\n const isSelected = selectedDay ? sameDay(day, selectedDay) : false;\n const hasViewings = daysWithViewings.has(key);\n return (\n <button\n key={key}\n type=\"button\"\n onClick={() => setSelectedDay(isSelected ? null : day)}\n aria-pressed={isSelected}\n className={cn(\n 'flex flex-col items-center gap-0.5 rounded-lg py-2 text-center transition-colors',\n isSelected\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:bg-bg-sunken'\n )}\n >\n <span className=\"text-[0.65rem] font-medium uppercase\">{WEEKDAYS[day.getDay()]}</span>\n <span className={cn('text-sm font-semibold tabular-nums', !isSelected && isToday && 'text-primary')}>\n {day.getDate()}\n </span>\n <span\n className={cn(\n 'h-1 w-1 rounded-full',\n hasViewings ? (isSelected ? 'bg-action-primary-text' : 'bg-status-success') : 'bg-transparent'\n )}\n />\n </button>\n );\n })}\n </div>\n </GlassCard>\n\n <div className=\"-mx-1 flex flex-nowrap items-center gap-2 overflow-x-auto px-1 pb-1 sm:flex-wrap sm:overflow-visible\">\n {statusTabs.map((tab) => (\n <button\n key={tab.value}\n onClick={() => {\n setActiveTab(tab.value);\n pagination.setPage(1);\n }}\n className={cn(\n 'shrink-0 rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n activeTab === tab.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {tab.label}\n </button>\n ))}\n </div>\n\n {viewings.length === 0 && !loading ? (\n <EmptyState\n illustration=\"empty-generic\"\n icon={<CalendarClock className=\"h-12 w-12\" />}\n title=\"No viewings booked\"\n description=\"Book a viewing from any property page and it will show up here to track.\"\n action={<Button onClick={() => navigate('../properties')}>Browse properties</Button>}\n />\n ) : shownViewings.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n icon={<CalendarClock className=\"h-12 w-12\" />}\n title=\"No viewings that day\"\n description=\"There are no viewings scheduled for the selected day. Pick another day or clear the filter.\"\n action={<Button onClick={() => setSelectedDay(null)}>Show all days</Button>}\n />\n ) : (\n <div className=\"space-y-4\">\n {shownViewings.map((viewing) => {\n const image = viewing.property?.images.find((img) => img.isPrimary) ?? viewing.property?.images[0];\n const canManage = viewing.status === 'requested' || viewing.status === 'confirmed';\n const isVirtual = viewing.type === 'virtual';\n return (\n <GlassCard key={viewing.id} treatment={canManage ? 'insight' : 'plain'} className=\"p-5\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n <button\n type=\"button\"\n onClick={() => viewing.property && navigate(`../properties/${viewing.property.id}`)}\n className=\"flex flex-1 items-start gap-3 text-left\"\n >\n {image ? (\n <img src={image.url} alt=\"\" className=\"h-16 w-16 rounded-lg object-cover\" />\n ) : (\n <div className=\"flex h-16 w-16 items-center justify-center rounded-lg bg-bg-sunken\">\n <Home className=\"h-6 w-6 text-text-secondary\" />\n </div>\n )}\n <div className=\"min-w-0\">\n <div className=\"mb-1 flex flex-wrap items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary\">{viewing.property?.title ?? 'Property'}</h3>\n <StatusBadge label={viewing.status} variant={statusVariant(viewing.status)} dot />\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\">\n {isVirtual ? <Video className=\"h-3 w-3\" /> : <Users className=\"h-3 w-3\" />}\n {isVirtual ? 'Virtual' : 'In person'}\n </span>\n </div>\n {viewing.property && (\n <p className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <MapPin className=\"h-3 w-3\" />\n {viewing.property.location.city}, {viewing.property.location.state}\n </p>\n )}\n <div className=\"mt-2 flex flex-wrap gap-4 text-sm text-text-secondary\">\n <span className=\"flex items-center gap-1 font-medium text-text-primary\">\n <CalendarClock className=\"h-3.5 w-3.5\" />\n {formatDateTime(viewing.scheduledAt)}\n </span>\n {viewing.attendeeName && (\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3.5 w-3.5\" />\n {viewing.attendeeName}\n </span>\n )}\n </div>\n {viewing.status === 'cancelled' && viewing.notes && (\n // Surfaces the host's \"Declined: <reason>\" disposition (and any\n // booking notes it was appended to) to the attendee-facing view.\n // Not clamped: the disposition line must stay readable.\n <p className=\"mt-2 whitespace-pre-line text-xs text-text-secondary\">\n <span className=\"font-medium\">Note:</span> {viewing.notes}\n </p>\n )}\n </div>\n </button>\n\n {canManage && (\n <div className=\"flex shrink-0 flex-wrap gap-2\">\n {viewing.status === 'requested' && (\n <>\n <Button size=\"sm\" disabled={busy} onClick={() => handleConfirm(viewing)}>\n <Check className=\"h-4 w-4\" />\n <span className=\"ml-1\">{updating ? 'Confirming…' : 'Confirm'}</span>\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n onClick={() => {\n setDeclineTarget(viewing);\n setDeclineReason('');\n }}\n >\n <XCircle className=\"h-4 w-4\" />\n <span className=\"ml-1\">Decline</span>\n </Button>\n </>\n )}\n <Button variant=\"outline\" size=\"sm\" disabled={busy} onClick={() => openReschedule(viewing)}>\n <CalendarClock className=\"h-4 w-4\" />\n <span className=\"ml-1\">Reschedule</span>\n </Button>\n <Button variant=\"outline\" size=\"sm\" disabled={busy} onClick={() => handleCancel(viewing)}>\n <X className=\"h-4 w-4\" />\n <span className=\"ml-1\">Cancel</span>\n </Button>\n </div>\n )}\n </div>\n </GlassCard>\n );\n })}\n\n {!selectedDay && (\n <Pagination\n page={pagination.page}\n pageSize={pagination.pageSize}\n hasNextPage={viewings.length >= pagination.pageSize}\n hasPreviousPage={pagination.page > 1}\n onPageChange={pagination.setPage}\n onPageSizeChange={pagination.setPageSize}\n />\n )}\n </div>\n )}\n\n {/* Reschedule */}\n <ResponsiveDialog open={rescheduleTarget !== null} onOpenChange={(open) => !open && closeReschedule()}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleReschedule}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Reschedule viewing</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Pick a new date and time for {rescheduleTarget?.property?.title ?? 'this viewing'}.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"py-2\">\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">New date & time *</label>\n <input\n type=\"datetime-local\"\n value={scheduledAt}\n onChange={(event) => setScheduledAt(event.target.value)}\n className={inputClass}\n />\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={closeReschedule} disabled={busy}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={busy}>\n {updating ? 'Saving…' : 'Reschedule'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Decline a requested viewing */}\n <ResponsiveDialog open={declineTarget !== null} onOpenChange={(open) => !open && closeDecline()}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleDecline}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Decline viewing request</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Decline the requested viewing for {declineTarget?.property?.title ?? 'this property'}. The attendee sees\n the note you leave here.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"py-2\">\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\" htmlFor=\"decline-reason\">\n Reason (optional)\n </label>\n <textarea\n id=\"decline-reason\"\n value={declineReason}\n onChange={(event) => setDeclineReason(event.target.value)}\n rows={3}\n maxLength={480}\n placeholder=\"e.g. Agent unavailable that slot — suggest a new time\"\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={closeDecline} disabled={busy}>\n Keep request\n </Button>\n <Button type=\"submit\" variant=\"destructive\" disabled={busy}>\n {updating ? 'Declining…' : 'Decline viewing'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-housingvista",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.928.1",
|
|
4
4
|
"description": "Burdenoff HousingVista Microfrontend - Comprehensive real estate platform for property management, investment, and transactions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|