@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 you have booked. Reschedule (update) or
3
- * cancel a viewing. Server-side status filter + pagination, plus an optional
4
- * week strip that lets you jump to a day and see which days have viewings. This
5
- * is where the "book a viewing" story's "track your viewings" lands.
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;;;;;;GAMG;AAsEH,wBAAgB,YAAY,gCAwT3B"}
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 s, useUpdateViewing as c, useViewings as l } from "../../hooks/index.js";
8
- import { notify as u } from "../../lib/toast.js";
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 d } from "../../hooks/useServerPagination.js";
11
- import { useMemo as f, useState as p } from "react";
12
- import { CalendarClock as m, CalendarDays as h, ChevronLeft as ee, ChevronRight as te, Home as g, MapPin as ne, Users as _, Video as v, X as y } from "lucide-react";
13
- import { useNavigate as b } from "react-router";
14
- import { jsx as x, jsxs as S } from "react/jsx-runtime";
15
- import { Button as C, EmphasisPanel as w, GlassCard as T, Pagination as E, ResponsiveDialog as re, ResponsiveDialogContent as ie, ResponsiveDialogDescription as ae, ResponsiveDialogFooter as oe, ResponsiveDialogHeader as se, ResponsiveDialogTitle as D } from "@burdenoff/fe-libs/ui";
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 O = [
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 k(e) {
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 A = "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)]";
48
- function j(e) {
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 M = 1440 * 60 * 1e3, N = [
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 P(e) {
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 F(e, t) {
67
+ function k(e, t) {
68
68
  return e.getFullYear() === t.getFullYear() && e.getMonth() === t.getMonth() && e.getDate() === t.getDate();
69
69
  }
70
- function I() {
70
+ function A() {
71
71
  o("housingvista.viewings.viewed");
72
- let I = b(), L = d(10), [R, z] = p("all"), { data: B, loading: V } = l({
73
- filter: R === "all" ? void 0 : { status: R },
74
- pagination: L.pagination
75
- }), { updateViewing: ce, loading: H } = c(), { cancelViewing: U, loading: W } = s(), [G, K] = p(null), [q, J] = p(""), [Y, X] = p(() => P(/* @__PURE__ */ new Date())), [Z, Q] = p(null), le = f(() => Array.from({ length: 7 }, (e, t) => new Date(Y.getTime() + t * M)), [Y]), ue = f(() => {
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 B) {
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
- }, [B]), $ = f(() => Z ? B.filter((e) => F(new Date(e.scheduledAt), Z)) : B, [B, Z]), de = Y.toLocaleDateString("en-US", {
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
- }), fe = (e) => {
86
- K(e), J(j(e.scheduledAt));
87
- }, pe = async (e) => {
88
- if (e.preventDefault(), G) {
89
- if (!q) {
90
- u.warning("Pick a new date and time");
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 ce(G.id, { scheduledAt: new Date(q).toISOString() }), u.success("Viewing rescheduled"), K(null);
94
+ await I(z.id, { scheduledAt: new Date(V).toISOString() }), s.success("Viewing rescheduled"), B(null);
95
95
  } catch (e) {
96
- u.error("Could not reschedule", e.message);
96
+ s.error("Could not reschedule", e.message);
97
97
  }
98
98
  }
99
- }, me = async (e) => {
99
+ }, Te = async (e) => {
100
100
  try {
101
- await U(e.id), u.success("Viewing cancelled");
101
+ await ve(e.id), s.success("Viewing cancelled");
102
102
  } catch (e) {
103
- u.error("Could not cancel viewing", e.message);
103
+ s.error("Could not cancel viewing", e.message);
104
104
  }
105
- }, he = B.filter((e) => e.status === "requested" || e.status === "confirmed").length;
106
- return /* @__PURE__ */ S(n, {
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 the property viewings you have booked",
109
- 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.",
110
- actions: /* @__PURE__ */ S(C, {
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: () => I("../properties"),
113
- children: [/* @__PURE__ */ x(g, { className: "h-4 w-4" }), /* @__PURE__ */ x("span", {
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__ */ x(w, {
144
+ /* @__PURE__ */ _(ue, {
120
145
  className: "p-4",
121
- children: /* @__PURE__ */ S("div", {
146
+ children: /* @__PURE__ */ v("div", {
122
147
  className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
123
148
  children: [
124
- /* @__PURE__ */ x(r, {
149
+ /* @__PURE__ */ _(r, {
125
150
  label: "Total viewings",
126
- value: B.length,
127
- icon: /* @__PURE__ */ x(h, { className: "h-4 w-4" })
151
+ value: P.length,
152
+ icon: /* @__PURE__ */ _(f, { className: "h-4 w-4" })
128
153
  }),
129
- /* @__PURE__ */ x(r, {
154
+ /* @__PURE__ */ _(r, {
130
155
  label: "Upcoming",
131
- value: he,
132
- icon: /* @__PURE__ */ x(m, { className: "h-4 w-4" })
156
+ value: Oe,
157
+ icon: /* @__PURE__ */ _(d, { className: "h-4 w-4" })
133
158
  }),
134
- /* @__PURE__ */ x(r, {
159
+ /* @__PURE__ */ _(r, {
135
160
  label: "Virtual",
136
- value: B.filter((e) => e.type === "virtual").length,
137
- icon: /* @__PURE__ */ x(v, { className: "h-4 w-4" })
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__ */ S(T, {
167
+ /* @__PURE__ */ v(b, {
143
168
  treatment: "plain",
144
169
  className: "p-3",
145
- children: [/* @__PURE__ */ S("div", {
170
+ children: [/* @__PURE__ */ v("div", {
146
171
  className: "mb-2 flex items-center justify-between",
147
- children: [/* @__PURE__ */ x("span", {
172
+ children: [/* @__PURE__ */ _("span", {
148
173
  className: "text-sm font-semibold text-text-primary",
149
- children: de
150
- }), /* @__PURE__ */ S("div", {
174
+ children: Se
175
+ }), /* @__PURE__ */ v("div", {
151
176
  className: "flex items-center gap-1",
152
177
  children: [
153
- Z && /* @__PURE__ */ x("button", {
178
+ Y && /* @__PURE__ */ _("button", {
154
179
  type: "button",
155
- onClick: () => Q(null),
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__ */ x("button", {
184
+ /* @__PURE__ */ _("button", {
160
185
  type: "button",
161
186
  "aria-label": "Previous week",
162
- onClick: () => X(/* @__PURE__ */ new Date(Y.getTime() - 7 * M)),
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__ */ x(ee, { className: "h-4 w-4" })
189
+ children: /* @__PURE__ */ _(ie, { className: "h-4 w-4" })
165
190
  }),
166
- /* @__PURE__ */ x("button", {
191
+ /* @__PURE__ */ _("button", {
167
192
  type: "button",
168
193
  "aria-label": "Next week",
169
- onClick: () => X(new Date(Y.getTime() + 7 * M)),
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__ */ x(te, { className: "h-4 w-4" })
196
+ children: /* @__PURE__ */ _(ae, { className: "h-4 w-4" })
172
197
  })
173
198
  ]
174
199
  })]
175
- }), /* @__PURE__ */ x("div", {
200
+ }), /* @__PURE__ */ _("div", {
176
201
  className: "grid grid-cols-7 gap-1.5",
177
- children: le.map((t) => {
178
- let n = `${t.getFullYear()}-${t.getMonth()}-${t.getDate()}`, r = F(t, /* @__PURE__ */ new Date()), i = Z ? F(t, Z) : !1, a = ue.has(n);
179
- return /* @__PURE__ */ S("button", {
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: () => Q(i ? null : t),
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__ */ x("span", {
210
+ /* @__PURE__ */ _("span", {
186
211
  className: "text-[0.65rem] font-medium uppercase",
187
- children: N[t.getDay()]
212
+ children: ge[t.getDay()]
188
213
  }),
189
- /* @__PURE__ */ x("span", {
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__ */ x("span", { className: e("h-1 w-1 rounded-full", a ? i ? "bg-action-primary-text" : "bg-status-success" : "bg-transparent") })
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__ */ x("div", {
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: O.map((t) => /* @__PURE__ */ x("button", {
226
+ children: fe.map((t) => /* @__PURE__ */ _("button", {
202
227
  onClick: () => {
203
- z(t.value), L.setPage(1);
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", R === t.value ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
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
- B.length === 0 && !V ? /* @__PURE__ */ x(a, {
234
+ P.length === 0 && !F ? /* @__PURE__ */ _(a, {
210
235
  illustration: "empty-generic",
211
- icon: /* @__PURE__ */ x(m, { className: "h-12 w-12" }),
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__ */ x(C, {
215
- onClick: () => I("../properties"),
239
+ action: /* @__PURE__ */ _(y, {
240
+ onClick: () => A("../properties"),
216
241
  children: "Browse properties"
217
242
  })
218
- }) : $.length === 0 ? /* @__PURE__ */ x(a, {
243
+ }) : Z.length === 0 ? /* @__PURE__ */ _(a, {
219
244
  illustration: "empty-generic",
220
- icon: /* @__PURE__ */ x(m, { className: "h-12 w-12" }),
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__ */ x(C, {
224
- onClick: () => Q(null),
248
+ action: /* @__PURE__ */ _(y, {
249
+ onClick: () => X(null),
225
250
  children: "Show all days"
226
251
  })
227
- }) : /* @__PURE__ */ S("div", {
252
+ }) : /* @__PURE__ */ v("div", {
228
253
  className: "space-y-4",
229
- children: [$.map((e) => {
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__ */ x(T, {
256
+ return /* @__PURE__ */ _(b, {
232
257
  treatment: r ? "insight" : "plain",
233
258
  className: "p-5",
234
- children: /* @__PURE__ */ S("div", {
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__ */ S("button", {
261
+ children: [/* @__PURE__ */ v("button", {
237
262
  type: "button",
238
- onClick: () => e.property && I(`../properties/${e.property.id}`),
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__ */ x("img", {
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__ */ x("div", {
269
+ }) : /* @__PURE__ */ _("div", {
245
270
  className: "flex h-16 w-16 items-center justify-center rounded-lg bg-bg-sunken",
246
- children: /* @__PURE__ */ x(g, { className: "h-6 w-6 text-text-secondary" })
247
- }), /* @__PURE__ */ S("div", {
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__ */ S("div", {
275
+ /* @__PURE__ */ v("div", {
251
276
  className: "mb-1 flex flex-wrap items-center gap-2",
252
277
  children: [
253
- /* @__PURE__ */ x("h3", {
278
+ /* @__PURE__ */ _("h3", {
254
279
  className: "font-semibold text-text-primary",
255
280
  children: e.property?.title ?? "Property"
256
281
  }),
257
- /* @__PURE__ */ x(i, {
282
+ /* @__PURE__ */ _(i, {
258
283
  label: e.status,
259
- variant: k(e.status),
284
+ variant: pe(e.status),
260
285
  dot: !0
261
286
  }),
262
- /* @__PURE__ */ S("span", {
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: [x(a ? v : _, { className: "h-3 w-3" }), a ? "Virtual" : "In person"]
289
+ children: [_(a ? h : m, { className: "h-3 w-3" }), a ? "Virtual" : "In person"]
265
290
  })
266
291
  ]
267
292
  }),
268
- e.property && /* @__PURE__ */ S("p", {
293
+ e.property && /* @__PURE__ */ v("p", {
269
294
  className: "flex items-center gap-1 text-xs text-text-secondary",
270
295
  children: [
271
- /* @__PURE__ */ x(ne, { className: "h-3 w-3" }),
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__ */ S("div", {
302
+ /* @__PURE__ */ v("div", {
278
303
  className: "mt-2 flex flex-wrap gap-4 text-sm text-text-secondary",
279
- children: [/* @__PURE__ */ S("span", {
304
+ children: [/* @__PURE__ */ v("span", {
280
305
  className: "flex items-center gap-1 font-medium text-text-primary",
281
- children: [/* @__PURE__ */ x(m, { className: "h-3.5 w-3.5" }), t(e.scheduledAt)]
282
- }), e.attendeeName && /* @__PURE__ */ S("span", {
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__ */ x(_, { className: "h-3.5 w-3.5" }), e.attendeeName]
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__ */ S("div", {
325
+ }), r && /* @__PURE__ */ v("div", {
290
326
  className: "flex shrink-0 flex-wrap gap-2",
291
- children: [/* @__PURE__ */ S(C, {
292
- variant: "outline",
293
- size: "sm",
294
- onClick: () => fe(e),
295
- children: [/* @__PURE__ */ x(m, { className: "h-4 w-4" }), /* @__PURE__ */ x("span", {
296
- className: "ml-1",
297
- children: "Reschedule"
298
- })]
299
- }), /* @__PURE__ */ S(C, {
300
- variant: "outline",
301
- size: "sm",
302
- disabled: W,
303
- onClick: () => me(e),
304
- children: [/* @__PURE__ */ x(y, { className: "h-4 w-4" }), /* @__PURE__ */ x("span", {
305
- className: "ml-1",
306
- children: "Cancel"
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
- }), !Z && /* @__PURE__ */ x(E, {
313
- page: L.page,
314
- pageSize: L.pageSize,
315
- hasNextPage: B.length >= L.pageSize,
316
- hasPreviousPage: L.page > 1,
317
- onPageChange: L.setPage,
318
- onPageSizeChange: L.setPageSize
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(re, {
322
- open: G !== null,
323
- onOpenChange: (e) => !e && K(null),
324
- children: /* @__PURE__ */ x(ie, {
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__ */ S("form", {
327
- onSubmit: pe,
386
+ children: /* @__PURE__ */ v("form", {
387
+ onSubmit: we,
328
388
  children: [
329
- /* @__PURE__ */ S(se, { children: [/* @__PURE__ */ x(D, { children: "Reschedule viewing" }), /* @__PURE__ */ S(ae, { children: [
389
+ /* @__PURE__ */ v(T, { children: [/* @__PURE__ */ _(E, { children: "Reschedule viewing" }), /* @__PURE__ */ v(C, { children: [
330
390
  "Pick a new date and time for ",
331
- G?.property?.title ?? "this viewing",
391
+ z?.property?.title ?? "this viewing",
332
392
  "."
333
393
  ] })] }),
334
- /* @__PURE__ */ S("div", {
394
+ /* @__PURE__ */ v("div", {
335
395
  className: "py-2",
336
- children: [/* @__PURE__ */ x("label", {
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__ */ x("input", {
399
+ }), /* @__PURE__ */ _("input", {
340
400
  type: "datetime-local",
341
- value: q,
342
- onChange: (e) => J(e.target.value),
343
- className: A
401
+ value: V,
402
+ onChange: (e) => H(e.target.value),
403
+ className: me
344
404
  })]
345
405
  }),
346
- /* @__PURE__ */ S(oe, { children: [/* @__PURE__ */ x(C, {
406
+ /* @__PURE__ */ v(w, { children: [/* @__PURE__ */ _(y, {
347
407
  type: "button",
348
408
  variant: "outline",
349
- onClick: () => K(null),
409
+ onClick: Q,
410
+ disabled: R,
350
411
  children: "Cancel"
351
- }), /* @__PURE__ */ x(C, {
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
- disabled: H,
354
- children: H ? "Saving…" : "Reschedule"
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 { I as ViewingsPage };
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 &amp; 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":"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"}
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 &amp; 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.925.1",
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": [