@burdenoff/microfe-housingvista 2026.930.2 → 2026.1001.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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OffersPage.d.ts","sourceRoot":"","sources":["../../../../src/housingvista/pages/offers/OffersPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"OffersPage.d.ts","sourceRoot":"","sources":["../../../../src/housingvista/pages/offers/OffersPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAyIH,wBAAgB,UAAU,gCA6UzB"}
|
|
@@ -4,17 +4,18 @@ import { StatCard as i } from "../../components/StatCard.js";
|
|
|
4
4
|
import { StatusBadge as a } from "../../components/StatusBadge.js";
|
|
5
5
|
import { EmptyState as o } from "../../components/EmptyState.js";
|
|
6
6
|
import { useTrackPageView as s } from "../../hooks/useHousingVistaAnalytics.js";
|
|
7
|
-
import { useOffers as c, useRespondOffer as
|
|
8
|
-
import { notify as
|
|
7
|
+
import { useOffers as c, useRespondOffer as ee, useWithdrawOffer as l } from "../../hooks/index.js";
|
|
8
|
+
import { notify as u } from "../../lib/toast.js";
|
|
9
|
+
import { ContactOwnerDialog as d } from "../../components/ContactOwnerDialog.js";
|
|
9
10
|
import "../../components/index.js";
|
|
10
11
|
import { useServerPagination as f } from "../../hooks/useServerPagination.js";
|
|
11
12
|
import { useState as p } from "react";
|
|
12
|
-
import { ArrowRight as m, Check as h, Home as g, MapPin as _,
|
|
13
|
-
import { useNavigate as
|
|
14
|
-
import { jsx as
|
|
15
|
-
import { Button as
|
|
13
|
+
import { ArrowRight as m, Check as h, Home as g, MapPin as _, MessageSquare as v, Repeat as y, Tag as b, Undo2 as te, X as x } from "lucide-react";
|
|
14
|
+
import { useNavigate as S } from "react-router";
|
|
15
|
+
import { jsx as C, jsxs as w } from "react/jsx-runtime";
|
|
16
|
+
import { Button as T, EmphasisPanel as ne, GlassCard as re, Pagination as ie, ResponsiveDialog as ae, ResponsiveDialogContent as oe, ResponsiveDialogDescription as se, ResponsiveDialogFooter as E, ResponsiveDialogHeader as D, ResponsiveDialogTitle as O } from "@burdenoff/fe-libs/ui";
|
|
16
17
|
//#region src/housingvista/pages/offers/OffersPage.tsx
|
|
17
|
-
var
|
|
18
|
+
var k = [
|
|
18
19
|
{
|
|
19
20
|
value: "all",
|
|
20
21
|
label: "All"
|
|
@@ -44,7 +45,7 @@ var A = [
|
|
|
44
45
|
label: "Expired"
|
|
45
46
|
}
|
|
46
47
|
];
|
|
47
|
-
function
|
|
48
|
+
function A(e) {
|
|
48
49
|
switch (e) {
|
|
49
50
|
case "accepted": return "success";
|
|
50
51
|
case "countered": return "warning";
|
|
@@ -53,14 +54,14 @@ function j(e) {
|
|
|
53
54
|
default: return "muted";
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
|
-
var
|
|
57
|
+
var j = {
|
|
57
58
|
success: "bg-status-success",
|
|
58
59
|
warning: "bg-status-warning",
|
|
59
60
|
error: "bg-status-error",
|
|
60
61
|
info: "bg-status-info",
|
|
61
62
|
muted: "bg-text-secondary"
|
|
62
63
|
};
|
|
63
|
-
function
|
|
64
|
+
function M(e) {
|
|
64
65
|
let t = {
|
|
65
66
|
label: "Submitted",
|
|
66
67
|
date: e.createdAt,
|
|
@@ -130,24 +131,24 @@ function N(e) {
|
|
|
130
131
|
}];
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
|
-
function
|
|
134
|
-
let r =
|
|
135
|
-
return /* @__PURE__ */
|
|
134
|
+
function N({ offer: t }) {
|
|
135
|
+
let r = M(t);
|
|
136
|
+
return /* @__PURE__ */ C("ol", {
|
|
136
137
|
className: "flex items-start",
|
|
137
138
|
"aria-label": "Offer status timeline",
|
|
138
139
|
children: r.map((t, i) => {
|
|
139
140
|
let a = i === r.length - 1, o = !a && r[i + 1].filled;
|
|
140
|
-
return /* @__PURE__ */
|
|
141
|
+
return /* @__PURE__ */ w("li", {
|
|
141
142
|
className: e("flex flex-col items-center", !a && "flex-1"),
|
|
142
|
-
children: [/* @__PURE__ */
|
|
143
|
+
children: [/* @__PURE__ */ w("div", {
|
|
143
144
|
className: "flex w-full items-center",
|
|
144
|
-
children: [/* @__PURE__ */
|
|
145
|
-
}), /* @__PURE__ */
|
|
145
|
+
children: [/* @__PURE__ */ C("span", { className: e("flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-full", t.filled ? j[t.tone] : "border-2 border-border-subtle bg-bg-surface") }), !a && /* @__PURE__ */ C("span", { className: e("mx-1 h-0.5 flex-1 rounded-full", t.filled && o ? j[t.tone] : "bg-border-subtle") })]
|
|
146
|
+
}), /* @__PURE__ */ w("div", {
|
|
146
147
|
className: e("mt-1.5 text-center", a ? "items-end" : ""),
|
|
147
|
-
children: [/* @__PURE__ */
|
|
148
|
+
children: [/* @__PURE__ */ C("span", {
|
|
148
149
|
className: "block text-xs font-medium text-text-primary",
|
|
149
150
|
children: t.label
|
|
150
|
-
}), t.date && /* @__PURE__ */
|
|
151
|
+
}), t.date && /* @__PURE__ */ C("span", {
|
|
151
152
|
className: "block text-[0.65rem] text-text-secondary",
|
|
152
153
|
children: n(t.date)
|
|
153
154
|
})]
|
|
@@ -156,283 +157,302 @@ function P({ offer: t }) {
|
|
|
156
157
|
})
|
|
157
158
|
});
|
|
158
159
|
}
|
|
159
|
-
var
|
|
160
|
-
function
|
|
160
|
+
var P = "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)]";
|
|
161
|
+
function F() {
|
|
161
162
|
s("housingvista.offer.viewed");
|
|
162
|
-
let
|
|
163
|
-
filter:
|
|
164
|
-
pagination:
|
|
165
|
-
}), { data:
|
|
163
|
+
let j = S(), M = f(10), [F, I] = p("all"), { data: L, loading: R } = c({
|
|
164
|
+
filter: F === "all" ? void 0 : { status: F },
|
|
165
|
+
pagination: M.pagination
|
|
166
|
+
}), { data: z } = c({ pagination: {
|
|
166
167
|
limit: 100,
|
|
167
168
|
offset: 0
|
|
168
|
-
} }), { respondOffer:
|
|
169
|
-
|
|
170
|
-
},
|
|
171
|
-
if (e.preventDefault(),
|
|
172
|
-
if (
|
|
173
|
-
let e = Number(
|
|
169
|
+
} }), { respondOffer: B, loading: V } = ee(), { withdrawOffer: H, loading: U } = l(), [W, G] = p(null), [K, q] = p(null), [J, Y] = p("accept"), [X, Z] = p(""), [Q, $] = p(""), ce = (e) => {
|
|
170
|
+
G(e), Y("accept"), Z(String(e.amount)), $("");
|
|
171
|
+
}, le = async (e) => {
|
|
172
|
+
if (e.preventDefault(), W) {
|
|
173
|
+
if (J === "counter") {
|
|
174
|
+
let e = Number(X);
|
|
174
175
|
if (!Number.isFinite(e) || e <= 0) {
|
|
175
|
-
|
|
176
|
+
u.warning("Enter a valid counter amount");
|
|
176
177
|
return;
|
|
177
178
|
}
|
|
178
179
|
}
|
|
179
180
|
try {
|
|
180
|
-
await
|
|
181
|
-
action:
|
|
182
|
-
counterAmount:
|
|
183
|
-
notes:
|
|
184
|
-
}),
|
|
181
|
+
await B(W.id, {
|
|
182
|
+
action: J,
|
|
183
|
+
counterAmount: J === "counter" ? Number(X) : void 0,
|
|
184
|
+
notes: Q.trim() || void 0
|
|
185
|
+
}), u.success("Response sent", `Offer ${J === "accept" ? "accepted" : J === "reject" ? "rejected" : "countered"}.`), G(null);
|
|
185
186
|
} catch (e) {
|
|
186
|
-
|
|
187
|
+
u.error("Could not respond to offer", e.message);
|
|
187
188
|
}
|
|
188
189
|
}
|
|
189
|
-
},
|
|
190
|
+
}, ue = async (e) => {
|
|
190
191
|
try {
|
|
191
|
-
await
|
|
192
|
+
await H(e.id), u.success("Offer withdrawn");
|
|
192
193
|
} catch (e) {
|
|
193
|
-
|
|
194
|
+
u.error("Could not withdraw offer", e.message);
|
|
194
195
|
}
|
|
195
|
-
},
|
|
196
|
-
return /* @__PURE__ */
|
|
196
|
+
}, de = (e) => e === "all" ? z.length : z.filter((t) => t.status === e).length, fe = z.filter((e) => e.status === "submitted" || e.status === "countered").length, pe = z.filter((e) => e.status === "accepted").length;
|
|
197
|
+
return /* @__PURE__ */ w(r, {
|
|
197
198
|
title: "Offers",
|
|
198
199
|
description: "Track the offers you have made and respond to offers on your listings",
|
|
199
200
|
purpose: "Every offer in one place: watch its status move from submitted to countered, accepted, or rejected — withdraw one you no longer want, or respond to offers coming in on properties you own.",
|
|
200
201
|
children: [
|
|
201
|
-
/* @__PURE__ */
|
|
202
|
+
/* @__PURE__ */ C(ne, {
|
|
202
203
|
className: "p-4",
|
|
203
|
-
children: /* @__PURE__ */
|
|
204
|
+
children: /* @__PURE__ */ w("div", {
|
|
204
205
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
|
|
205
206
|
children: [
|
|
206
|
-
/* @__PURE__ */
|
|
207
|
+
/* @__PURE__ */ C(i, {
|
|
207
208
|
label: "Total offers",
|
|
208
|
-
value:
|
|
209
|
-
icon: /* @__PURE__ */
|
|
209
|
+
value: z.length,
|
|
210
|
+
icon: /* @__PURE__ */ C(b, { className: "h-4 w-4" })
|
|
210
211
|
}),
|
|
211
|
-
/* @__PURE__ */
|
|
212
|
+
/* @__PURE__ */ C(i, {
|
|
212
213
|
label: "Awaiting action",
|
|
213
|
-
value:
|
|
214
|
-
icon: /* @__PURE__ */
|
|
214
|
+
value: fe,
|
|
215
|
+
icon: /* @__PURE__ */ C(y, { className: "h-4 w-4" })
|
|
215
216
|
}),
|
|
216
|
-
/* @__PURE__ */
|
|
217
|
+
/* @__PURE__ */ C(i, {
|
|
217
218
|
label: "Accepted",
|
|
218
|
-
value:
|
|
219
|
-
icon: /* @__PURE__ */
|
|
219
|
+
value: pe,
|
|
220
|
+
icon: /* @__PURE__ */ C(h, { className: "h-4 w-4" })
|
|
220
221
|
})
|
|
221
222
|
]
|
|
222
223
|
})
|
|
223
224
|
}),
|
|
224
|
-
/* @__PURE__ */
|
|
225
|
+
/* @__PURE__ */ C("div", {
|
|
225
226
|
className: "-mx-1 flex flex-nowrap items-center gap-2 overflow-x-auto px-1 pb-1 sm:flex-wrap sm:overflow-visible",
|
|
226
|
-
children:
|
|
227
|
-
let n =
|
|
228
|
-
return /* @__PURE__ */
|
|
227
|
+
children: k.map((t) => {
|
|
228
|
+
let n = de(t.value), r = F === t.value;
|
|
229
|
+
return /* @__PURE__ */ w("button", {
|
|
229
230
|
onClick: () => {
|
|
230
|
-
|
|
231
|
+
I(t.value), M.setPage(1);
|
|
231
232
|
},
|
|
232
233
|
className: e("inline-flex shrink-0 items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors", r ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
|
|
233
|
-
children: [t.label, /* @__PURE__ */
|
|
234
|
+
children: [t.label, /* @__PURE__ */ C("span", {
|
|
234
235
|
className: e("inline-flex min-w-5 items-center justify-center rounded-full px-1.5 text-[0.65rem] font-semibold tabular-nums", r ? "bg-action-primary-text/20 text-action-primary-text" : "bg-bg-surface text-text-secondary"),
|
|
235
236
|
children: n
|
|
236
237
|
})]
|
|
237
238
|
}, t.value);
|
|
238
239
|
})
|
|
239
240
|
}),
|
|
240
|
-
|
|
241
|
+
L.length === 0 && !R ? /* @__PURE__ */ C(o, {
|
|
241
242
|
illustration: "empty-search",
|
|
242
|
-
icon: /* @__PURE__ */
|
|
243
|
+
icon: /* @__PURE__ */ C(b, { className: "h-12 w-12" }),
|
|
243
244
|
title: "No offers here",
|
|
244
245
|
description: "Offers you submit on properties — and offers made on your listings — will appear here.",
|
|
245
|
-
action: /* @__PURE__ */
|
|
246
|
-
onClick: () =>
|
|
246
|
+
action: /* @__PURE__ */ C(T, {
|
|
247
|
+
onClick: () => j("../properties"),
|
|
247
248
|
children: "Browse properties"
|
|
248
249
|
})
|
|
249
|
-
}) : /* @__PURE__ */
|
|
250
|
+
}) : /* @__PURE__ */ w("div", {
|
|
250
251
|
className: "space-y-4",
|
|
251
|
-
children: [/* @__PURE__ */
|
|
252
|
+
children: [/* @__PURE__ */ C("div", {
|
|
252
253
|
className: "grid grid-cols-1 gap-4 xl:grid-cols-2",
|
|
253
|
-
children:
|
|
254
|
+
children: L.map((e) => {
|
|
254
255
|
let r = e.property?.images.find((e) => e.isPrimary) ?? e.property?.images[0], i = e.status === "submitted" || e.status === "countered";
|
|
255
|
-
return /* @__PURE__ */
|
|
256
|
+
return /* @__PURE__ */ w(re, {
|
|
256
257
|
treatment: i ? "insight" : "plain",
|
|
257
258
|
glow: i,
|
|
258
259
|
className: "flex flex-col gap-4 p-5",
|
|
259
260
|
children: [
|
|
260
|
-
/* @__PURE__ */
|
|
261
|
+
/* @__PURE__ */ w("div", {
|
|
261
262
|
className: "flex items-start gap-3",
|
|
262
|
-
children: [/* @__PURE__ */
|
|
263
|
+
children: [/* @__PURE__ */ C("button", {
|
|
263
264
|
type: "button",
|
|
264
|
-
onClick: () => e.property &&
|
|
265
|
+
onClick: () => e.property && j(`../properties/${e.property.id}`),
|
|
265
266
|
className: "shrink-0",
|
|
266
267
|
"aria-label": `Open ${e.property?.title ?? "property"}`,
|
|
267
|
-
children: r ? /* @__PURE__ */
|
|
268
|
+
children: r ? /* @__PURE__ */ C("img", {
|
|
268
269
|
src: r.url,
|
|
269
270
|
alt: "",
|
|
270
271
|
className: "h-16 w-16 rounded-lg object-cover"
|
|
271
|
-
}) : /* @__PURE__ */
|
|
272
|
+
}) : /* @__PURE__ */ C("div", {
|
|
272
273
|
className: "flex h-16 w-16 items-center justify-center rounded-lg bg-bg-sunken",
|
|
273
|
-
children: /* @__PURE__ */
|
|
274
|
+
children: /* @__PURE__ */ C(g, { className: "h-6 w-6 text-text-secondary" })
|
|
274
275
|
})
|
|
275
|
-
}), /* @__PURE__ */
|
|
276
|
+
}), /* @__PURE__ */ w("div", {
|
|
276
277
|
className: "min-w-0 flex-1",
|
|
277
278
|
children: [
|
|
278
|
-
/* @__PURE__ */
|
|
279
|
+
/* @__PURE__ */ w("div", {
|
|
279
280
|
className: "flex flex-wrap items-start justify-between gap-2",
|
|
280
|
-
children: [/* @__PURE__ */
|
|
281
|
+
children: [/* @__PURE__ */ w("button", {
|
|
281
282
|
type: "button",
|
|
282
|
-
onClick: () => e.property &&
|
|
283
|
+
onClick: () => e.property && j(`../properties/${e.property.id}`),
|
|
283
284
|
className: "text-left",
|
|
284
|
-
children: [/* @__PURE__ */
|
|
285
|
+
children: [/* @__PURE__ */ C("h3", {
|
|
285
286
|
className: "font-semibold text-text-primary hover:text-primary",
|
|
286
287
|
children: e.property?.title ?? "Property"
|
|
287
|
-
}), e.property && /* @__PURE__ */
|
|
288
|
+
}), e.property && /* @__PURE__ */ w("p", {
|
|
288
289
|
className: "flex items-center gap-1 text-xs text-text-secondary",
|
|
289
290
|
children: [
|
|
290
|
-
/* @__PURE__ */
|
|
291
|
+
/* @__PURE__ */ C(_, { className: "h-3 w-3" }),
|
|
291
292
|
e.property.location.city,
|
|
292
293
|
", ",
|
|
293
294
|
e.property.location.state
|
|
294
295
|
]
|
|
295
296
|
})]
|
|
296
|
-
}), /* @__PURE__ */
|
|
297
|
+
}), /* @__PURE__ */ C(a, {
|
|
297
298
|
label: e.status,
|
|
298
|
-
variant:
|
|
299
|
+
variant: A(e.status),
|
|
299
300
|
dot: !0
|
|
300
301
|
})]
|
|
301
302
|
}),
|
|
302
|
-
/* @__PURE__ */
|
|
303
|
+
/* @__PURE__ */ w("div", {
|
|
303
304
|
className: "mt-2 flex items-baseline gap-3",
|
|
304
|
-
children: [/* @__PURE__ */
|
|
305
|
+
children: [/* @__PURE__ */ C("span", {
|
|
305
306
|
className: "text-xl font-bold tabular-nums text-text-primary",
|
|
306
307
|
children: t(e.amount, e.currency)
|
|
307
|
-
}), e.counterAmount != null && /* @__PURE__ */
|
|
308
|
+
}), e.counterAmount != null && /* @__PURE__ */ w("span", {
|
|
308
309
|
className: "text-sm text-text-secondary",
|
|
309
310
|
children: [
|
|
310
311
|
"Counter",
|
|
311
312
|
" ",
|
|
312
|
-
/* @__PURE__ */
|
|
313
|
+
/* @__PURE__ */ C("span", {
|
|
313
314
|
className: "font-semibold text-status-warning-text",
|
|
314
315
|
children: t(e.counterAmount, e.currency)
|
|
315
316
|
})
|
|
316
317
|
]
|
|
317
318
|
})]
|
|
318
319
|
}),
|
|
319
|
-
/* @__PURE__ */
|
|
320
|
+
/* @__PURE__ */ w("p", {
|
|
320
321
|
className: "mt-0.5 text-xs text-text-secondary",
|
|
321
322
|
children: ["Submitted ", n(e.createdAt)]
|
|
322
323
|
}),
|
|
323
|
-
(e.status === "submitted" || e.status === "countered") && e.expiresAt && /* @__PURE__ */
|
|
324
|
+
(e.status === "submitted" || e.status === "countered") && e.expiresAt && /* @__PURE__ */ w("p", {
|
|
324
325
|
className: "mt-0.5 text-xs text-text-secondary",
|
|
325
326
|
children: ["Expires ", n(e.expiresAt)]
|
|
326
327
|
})
|
|
327
328
|
]
|
|
328
329
|
})]
|
|
329
330
|
}),
|
|
330
|
-
/* @__PURE__ */
|
|
331
|
+
/* @__PURE__ */ C("div", {
|
|
331
332
|
className: "rounded-lg bg-bg-sunken/50 p-3",
|
|
332
|
-
children: /* @__PURE__ */
|
|
333
|
+
children: /* @__PURE__ */ C(N, { offer: e })
|
|
333
334
|
}),
|
|
334
|
-
i && /* @__PURE__ */
|
|
335
|
+
(i || e.property) && /* @__PURE__ */ w("div", {
|
|
335
336
|
className: "flex flex-wrap gap-2",
|
|
336
337
|
children: [
|
|
337
|
-
/* @__PURE__ */
|
|
338
|
+
i && /* @__PURE__ */ w(T, {
|
|
338
339
|
size: "sm",
|
|
339
|
-
onClick: () =>
|
|
340
|
-
children: [/* @__PURE__ */
|
|
340
|
+
onClick: () => ce(e),
|
|
341
|
+
children: [/* @__PURE__ */ C(h, { className: "h-4 w-4" }), /* @__PURE__ */ C("span", {
|
|
341
342
|
className: "ml-1",
|
|
342
343
|
children: "Respond"
|
|
343
344
|
})]
|
|
344
345
|
}),
|
|
345
|
-
/* @__PURE__ */
|
|
346
|
+
i && /* @__PURE__ */ w(T, {
|
|
346
347
|
variant: "outline",
|
|
347
348
|
size: "sm",
|
|
348
|
-
disabled:
|
|
349
|
-
onClick: () =>
|
|
350
|
-
children: [/* @__PURE__ */
|
|
349
|
+
disabled: U,
|
|
350
|
+
onClick: () => ue(e),
|
|
351
|
+
children: [/* @__PURE__ */ C(te, { className: "h-4 w-4" }), /* @__PURE__ */ C("span", {
|
|
351
352
|
className: "ml-1",
|
|
352
353
|
children: "Withdraw"
|
|
353
354
|
})]
|
|
354
355
|
}),
|
|
355
|
-
e.property && /* @__PURE__ */
|
|
356
|
+
e.property && /* @__PURE__ */ w(T, {
|
|
357
|
+
variant: "ghost",
|
|
358
|
+
size: "sm",
|
|
359
|
+
onClick: () => q(e),
|
|
360
|
+
children: [/* @__PURE__ */ C(v, { className: "h-4 w-4" }), /* @__PURE__ */ C("span", {
|
|
361
|
+
className: "ml-1",
|
|
362
|
+
children: "Message"
|
|
363
|
+
})]
|
|
364
|
+
}),
|
|
365
|
+
e.property && /* @__PURE__ */ w(T, {
|
|
356
366
|
variant: "ghost",
|
|
357
367
|
size: "sm",
|
|
358
|
-
onClick: () =>
|
|
359
|
-
children: [/* @__PURE__ */
|
|
368
|
+
onClick: () => j(`../properties/${e.property?.id}`),
|
|
369
|
+
children: [/* @__PURE__ */ C("span", { children: "View property" }), /* @__PURE__ */ C(m, { className: "ml-1 h-4 w-4" })]
|
|
360
370
|
})
|
|
361
371
|
]
|
|
362
372
|
})
|
|
363
373
|
]
|
|
364
374
|
}, e.id);
|
|
365
375
|
})
|
|
366
|
-
}), /* @__PURE__ */
|
|
367
|
-
page:
|
|
368
|
-
pageSize:
|
|
369
|
-
hasNextPage:
|
|
370
|
-
hasPreviousPage:
|
|
371
|
-
onPageChange:
|
|
372
|
-
onPageSizeChange:
|
|
376
|
+
}), /* @__PURE__ */ C(ie, {
|
|
377
|
+
page: M.page,
|
|
378
|
+
pageSize: M.pageSize,
|
|
379
|
+
hasNextPage: L.length >= M.pageSize,
|
|
380
|
+
hasPreviousPage: M.page > 1,
|
|
381
|
+
onPageChange: M.setPage,
|
|
382
|
+
onPageSizeChange: M.setPageSize
|
|
373
383
|
})]
|
|
374
384
|
}),
|
|
375
|
-
/* @__PURE__ */
|
|
376
|
-
open:
|
|
377
|
-
onOpenChange: (e) => !e &&
|
|
378
|
-
|
|
385
|
+
K?.property && /* @__PURE__ */ C(d, {
|
|
386
|
+
open: K !== null,
|
|
387
|
+
onOpenChange: (e) => !e && q(null),
|
|
388
|
+
entityId: K.property.id,
|
|
389
|
+
entityType: "housingvista-property",
|
|
390
|
+
title: K.property.title,
|
|
391
|
+
pagePath: "/housingvista/offers",
|
|
392
|
+
heading: "Message",
|
|
393
|
+
description: `Discuss ${K.property.title} with the other party. The thread also appears in Messages.`
|
|
394
|
+
}, K.property.id),
|
|
395
|
+
/* @__PURE__ */ C(ae, {
|
|
396
|
+
open: W !== null,
|
|
397
|
+
onOpenChange: (e) => !e && G(null),
|
|
398
|
+
children: /* @__PURE__ */ C(oe, {
|
|
379
399
|
className: "sm:max-w-md",
|
|
380
|
-
children: /* @__PURE__ */
|
|
381
|
-
onSubmit:
|
|
400
|
+
children: /* @__PURE__ */ w("form", {
|
|
401
|
+
onSubmit: le,
|
|
382
402
|
children: [
|
|
383
|
-
/* @__PURE__ */
|
|
384
|
-
/* @__PURE__ */
|
|
403
|
+
/* @__PURE__ */ w(D, { children: [/* @__PURE__ */ C(O, { children: "Respond to offer" }), /* @__PURE__ */ C(se, { children: W?.property?.title ? `Offer of ${t(W.amount, W.currency)} on ${W.property.title}.` : "Accept, counter, or reject this offer." })] }),
|
|
404
|
+
/* @__PURE__ */ w("div", {
|
|
385
405
|
className: "space-y-3 py-2",
|
|
386
406
|
children: [
|
|
387
|
-
/* @__PURE__ */
|
|
407
|
+
/* @__PURE__ */ C("div", {
|
|
388
408
|
className: "flex flex-wrap gap-2",
|
|
389
409
|
children: [
|
|
390
410
|
"accept",
|
|
391
411
|
"counter",
|
|
392
412
|
"reject"
|
|
393
|
-
].map((t) => /* @__PURE__ */
|
|
413
|
+
].map((t) => /* @__PURE__ */ w("button", {
|
|
394
414
|
type: "button",
|
|
395
|
-
onClick: () =>
|
|
396
|
-
className: e("inline-flex items-center gap-1 rounded-md px-3 py-1.5 text-sm font-medium capitalize transition-colors",
|
|
415
|
+
onClick: () => Y(t),
|
|
416
|
+
className: e("inline-flex items-center gap-1 rounded-md px-3 py-1.5 text-sm font-medium capitalize transition-colors", J === t ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
|
|
397
417
|
children: [
|
|
398
|
-
t === "accept" && /* @__PURE__ */
|
|
399
|
-
t === "counter" && /* @__PURE__ */
|
|
400
|
-
t === "reject" && /* @__PURE__ */
|
|
418
|
+
t === "accept" && /* @__PURE__ */ C(h, { className: "h-4 w-4" }),
|
|
419
|
+
t === "counter" && /* @__PURE__ */ C(y, { className: "h-4 w-4" }),
|
|
420
|
+
t === "reject" && /* @__PURE__ */ C(x, { className: "h-4 w-4" }),
|
|
401
421
|
t
|
|
402
422
|
]
|
|
403
423
|
}, t))
|
|
404
424
|
}),
|
|
405
|
-
|
|
425
|
+
J === "counter" && /* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("label", {
|
|
406
426
|
className: "mb-1.5 block text-sm font-medium text-text-primary",
|
|
407
427
|
children: "Counter amount *"
|
|
408
|
-
}), /* @__PURE__ */
|
|
428
|
+
}), /* @__PURE__ */ C("input", {
|
|
409
429
|
type: "number",
|
|
410
430
|
min: 1,
|
|
411
431
|
step: "any",
|
|
412
|
-
value:
|
|
413
|
-
onChange: (e) =>
|
|
414
|
-
className:
|
|
432
|
+
value: X,
|
|
433
|
+
onChange: (e) => Z(e.target.value),
|
|
434
|
+
className: P
|
|
415
435
|
})] }),
|
|
416
|
-
/* @__PURE__ */
|
|
436
|
+
/* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("label", {
|
|
417
437
|
className: "mb-1.5 block text-sm font-medium text-text-primary",
|
|
418
438
|
children: "Note (optional)"
|
|
419
|
-
}), /* @__PURE__ */
|
|
420
|
-
value:
|
|
421
|
-
onChange: (e) =>
|
|
439
|
+
}), /* @__PURE__ */ C("textarea", {
|
|
440
|
+
value: Q,
|
|
441
|
+
onChange: (e) => $(e.target.value),
|
|
422
442
|
rows: 2,
|
|
423
443
|
className: "w-full resize-none 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)]"
|
|
424
444
|
})] })
|
|
425
445
|
]
|
|
426
446
|
}),
|
|
427
|
-
/* @__PURE__ */
|
|
447
|
+
/* @__PURE__ */ w(E, { children: [/* @__PURE__ */ C(T, {
|
|
428
448
|
type: "button",
|
|
429
449
|
variant: "outline",
|
|
430
|
-
onClick: () =>
|
|
450
|
+
onClick: () => G(null),
|
|
431
451
|
children: "Cancel"
|
|
432
|
-
}), /* @__PURE__ */
|
|
452
|
+
}), /* @__PURE__ */ C(T, {
|
|
433
453
|
type: "submit",
|
|
434
|
-
disabled:
|
|
435
|
-
children:
|
|
454
|
+
disabled: V,
|
|
455
|
+
children: V ? "Sending…" : "Send response"
|
|
436
456
|
})] })
|
|
437
457
|
]
|
|
438
458
|
})
|
|
@@ -442,6 +462,6 @@ function I() {
|
|
|
442
462
|
});
|
|
443
463
|
}
|
|
444
464
|
//#endregion
|
|
445
|
-
export {
|
|
465
|
+
export { F as OffersPage };
|
|
446
466
|
|
|
447
467
|
//# sourceMappingURL=OffersPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OffersPage.js","names":[],"sources":["../../../../src/housingvista/pages/offers/OffersPage.tsx"],"sourcesContent":["/**\n * Offers — the buyer/owner surface for property offers. Buyers track offers they\n * submitted (status timeline submitted→countered→accepted/rejected/withdrawn)\n * and can withdraw; owners can respond (accept / counter / reject). Server-side\n * status filter + pagination, with live per-status tab counts driven off a real\n * unfiltered read (no fabricated numbers).\n * @module housingvista\n */\n\nimport { useState, type FormEvent } from 'react';\nimport { useNavigate } from 'react-router';\nimport { Tag, Check, X, Repeat, Undo2, MapPin, ArrowRight, 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 { useOffers, useRespondOffer, useWithdrawOffer, type OfferRow } from '../../hooks';\nimport { useServerPagination } from '../../hooks/useServerPagination';\nimport { notify } from '../../lib/toast';\nimport { cn, formatCurrency, formatDate } from '../../utils/helpers';\nimport { useTrackPageView } from '../../hooks/useHousingVistaAnalytics';\n\nconst statusTabs: { value: string; label: string }[] = [\n { value: 'all', label: 'All' },\n { value: 'submitted', label: 'Submitted' },\n { value: 'countered', label: 'Countered' },\n { value: 'accepted', label: 'Accepted' },\n { value: 'rejected', label: 'Rejected' },\n { value: 'withdrawn', label: 'Withdrawn' },\n { value: 'expired', label: 'Expired' },\n];\n\nfunction statusVariant(status: OfferRow['status']): 'success' | 'warning' | 'error' | 'info' | 'muted' {\n switch (status) {\n case 'accepted':\n return 'success';\n case 'countered':\n return 'warning';\n case 'rejected':\n return 'error';\n case 'submitted':\n return 'info';\n default:\n return 'muted';\n }\n}\n\n// ---- Status timeline (submitted → countered → accepted / rejected / withdrawn) ----\n\ntype NodeTone = 'success' | 'warning' | 'error' | 'info' | 'muted';\ninterface TimelineNode {\n label: string;\n date: string | null;\n tone: NodeTone;\n filled: boolean;\n}\n\nconst toneFill: Record<NodeTone, string> = {\n success: 'bg-status-success',\n warning: 'bg-status-warning',\n error: 'bg-status-error',\n info: 'bg-status-info',\n muted: 'bg-text-secondary',\n};\n\nfunction buildTimeline(offer: OfferRow): TimelineNode[] {\n const submitted: TimelineNode = { label: 'Submitted', date: offer.createdAt, tone: 'info', filled: true };\n const respondedAt = offer.respondedAt;\n switch (offer.status) {\n case 'accepted':\n return [\n { ...submitted, tone: 'success' },\n ...(offer.counterAmount != null\n ? [{ label: 'Countered', date: null, tone: 'success' as NodeTone, filled: true }]\n : []),\n { label: 'Accepted', date: respondedAt, tone: 'success', filled: true },\n ];\n case 'countered':\n return [\n { ...submitted, tone: 'warning' },\n { label: 'Countered', date: respondedAt, tone: 'warning', filled: true },\n { label: 'Accepted', date: null, tone: 'muted', filled: false },\n ];\n case 'rejected':\n return [{ ...submitted }, { label: 'Rejected', date: respondedAt, tone: 'error', filled: true }];\n case 'withdrawn':\n return [{ ...submitted }, { label: 'Withdrawn', date: offer.updatedAt, tone: 'muted', filled: true }];\n case 'expired':\n return [{ ...submitted }, { label: 'Expired', date: offer.expiresAt, tone: 'muted', filled: true }];\n default:\n // submitted\n return [{ ...submitted }, { label: 'Accepted', date: null, tone: 'muted', filled: false }];\n }\n}\n\nfunction OfferTimeline({ offer }: { offer: OfferRow }) {\n const nodes = buildTimeline(offer);\n return (\n <ol className=\"flex items-start\" aria-label=\"Offer status timeline\">\n {nodes.map((node, index) => {\n const isLast = index === nodes.length - 1;\n const nextFilled = !isLast && nodes[index + 1].filled;\n return (\n <li key={node.label} className={cn('flex flex-col items-center', !isLast && 'flex-1')}>\n <div className=\"flex w-full items-center\">\n <span\n className={cn(\n 'flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-full',\n node.filled ? toneFill[node.tone] : 'border-2 border-border-subtle bg-bg-surface'\n )}\n />\n {!isLast && (\n <span\n className={cn(\n 'mx-1 h-0.5 flex-1 rounded-full',\n node.filled && nextFilled ? toneFill[node.tone] : 'bg-border-subtle'\n )}\n />\n )}\n </div>\n <div className={cn('mt-1.5 text-center', isLast ? 'items-end' : '')}>\n <span className=\"block text-xs font-medium text-text-primary\">{node.label}</span>\n {node.date && <span className=\"block text-[0.65rem] text-text-secondary\">{formatDate(node.date)}</span>}\n </div>\n </li>\n );\n })}\n </ol>\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\nexport function OffersPage() {\n useTrackPageView('housingvista.offer.viewed');\n const navigate = useNavigate();\n const pagination = useServerPagination(10);\n const [activeTab, setActiveTab] = useState('all');\n const { data: offers, loading } = useOffers({\n filter: activeTab === 'all' ? undefined : { status: activeTab },\n pagination: pagination.pagination,\n });\n // Unfiltered read powers the live tab counts + summary stats (real data).\n const { data: allOffers } = useOffers({ pagination: { limit: 100, offset: 0 } });\n const { respondOffer, loading: responding } = useRespondOffer();\n const { withdrawOffer, loading: withdrawing } = useWithdrawOffer();\n\n const [respondTarget, setRespondTarget] = useState<OfferRow | null>(null);\n const [action, setAction] = useState<'accept' | 'reject' | 'counter'>('accept');\n const [counterAmount, setCounterAmount] = useState('');\n const [notes, setNotes] = useState('');\n\n const openRespond = (offer: OfferRow) => {\n setRespondTarget(offer);\n setAction('accept');\n setCounterAmount(String(offer.amount));\n setNotes('');\n };\n\n const handleRespond = async (event: FormEvent) => {\n event.preventDefault();\n if (!respondTarget) return;\n if (action === 'counter') {\n const amount = Number(counterAmount);\n if (!Number.isFinite(amount) || amount <= 0) {\n notify.warning('Enter a valid counter amount');\n return;\n }\n }\n try {\n await respondOffer(respondTarget.id, {\n action,\n counterAmount: action === 'counter' ? Number(counterAmount) : undefined,\n notes: notes.trim() || undefined,\n });\n notify.success(\n 'Response sent',\n `Offer ${action === 'accept' ? 'accepted' : action === 'reject' ? 'rejected' : 'countered'}.`\n );\n setRespondTarget(null);\n } catch (err) {\n notify.error('Could not respond to offer', (err as Error).message);\n }\n };\n\n const handleWithdraw = async (offer: OfferRow) => {\n try {\n await withdrawOffer(offer.id);\n notify.success('Offer withdrawn');\n } catch (err) {\n notify.error('Could not withdraw offer', (err as Error).message);\n }\n };\n\n const countFor = (value: string): number =>\n value === 'all' ? allOffers.length : allOffers.filter((o) => o.status === value).length;\n\n const activeCount = allOffers.filter((o) => o.status === 'submitted' || o.status === 'countered').length;\n const acceptedCount = allOffers.filter((o) => o.status === 'accepted').length;\n\n return (\n <PageLayout\n title=\"Offers\"\n description=\"Track the offers you have made and respond to offers on your listings\"\n purpose=\"Every offer in one place: watch its status move from submitted to countered, accepted, or rejected — withdraw one you no longer want, or respond to offers coming in on properties you own.\"\n >\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <StatCard label=\"Total offers\" value={allOffers.length} icon={<Tag className=\"h-4 w-4\" />} />\n <StatCard label=\"Awaiting action\" value={activeCount} icon={<Repeat className=\"h-4 w-4\" />} />\n <StatCard label=\"Accepted\" value={acceptedCount} icon={<Check className=\"h-4 w-4\" />} />\n </div>\n </EmphasisPanel>\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 const count = countFor(tab.value);\n const isActive = activeTab === tab.value;\n return (\n <button\n key={tab.value}\n onClick={() => {\n setActiveTab(tab.value);\n pagination.setPage(1);\n }}\n className={cn(\n 'inline-flex shrink-0 items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n isActive\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 <span\n className={cn(\n 'inline-flex min-w-5 items-center justify-center rounded-full px-1.5 text-[0.65rem] font-semibold tabular-nums',\n isActive ? 'bg-action-primary-text/20 text-action-primary-text' : 'bg-bg-surface text-text-secondary'\n )}\n >\n {count}\n </span>\n </button>\n );\n })}\n </div>\n\n {offers.length === 0 && !loading ? (\n <EmptyState\n illustration=\"empty-search\"\n icon={<Tag className=\"h-12 w-12\" />}\n title=\"No offers here\"\n description=\"Offers you submit on properties — and offers made on your listings — will appear here.\"\n action={<Button onClick={() => navigate('../properties')}>Browse properties</Button>}\n />\n ) : (\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 xl:grid-cols-2\">\n {offers.map((offer) => {\n const image = offer.property?.images.find((img) => img.isPrimary) ?? offer.property?.images[0];\n const canAct = offer.status === 'submitted' || offer.status === 'countered';\n return (\n <GlassCard\n key={offer.id}\n treatment={canAct ? 'insight' : 'plain'}\n glow={canAct}\n className=\"flex flex-col gap-4 p-5\"\n >\n <div className=\"flex items-start gap-3\">\n <button\n type=\"button\"\n onClick={() => offer.property && navigate(`../properties/${offer.property.id}`)}\n className=\"shrink-0\"\n aria-label={`Open ${offer.property?.title ?? 'property'}`}\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 </button>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-start justify-between gap-2\">\n <button\n type=\"button\"\n onClick={() => offer.property && navigate(`../properties/${offer.property.id}`)}\n className=\"text-left\"\n >\n <h3 className=\"font-semibold text-text-primary hover:text-primary\">\n {offer.property?.title ?? 'Property'}\n </h3>\n {offer.property && (\n <p className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <MapPin className=\"h-3 w-3\" />\n {offer.property.location.city}, {offer.property.location.state}\n </p>\n )}\n </button>\n <StatusBadge label={offer.status} variant={statusVariant(offer.status)} dot />\n </div>\n <div className=\"mt-2 flex items-baseline gap-3\">\n <span className=\"text-xl font-bold tabular-nums text-text-primary\">\n {formatCurrency(offer.amount, offer.currency)}\n </span>\n {offer.counterAmount != null && (\n <span className=\"text-sm text-text-secondary\">\n Counter{' '}\n <span className=\"font-semibold text-status-warning-text\">\n {formatCurrency(offer.counterAmount, offer.currency)}\n </span>\n </span>\n )}\n </div>\n <p className=\"mt-0.5 text-xs text-text-secondary\">Submitted {formatDate(offer.createdAt)}</p>\n {(offer.status === 'submitted' || offer.status === 'countered') && offer.expiresAt && (\n <p className=\"mt-0.5 text-xs text-text-secondary\">Expires {formatDate(offer.expiresAt)}</p>\n )}\n </div>\n </div>\n\n <div className=\"rounded-lg bg-bg-sunken/50 p-3\">\n <OfferTimeline offer={offer} />\n </div>\n\n {canAct && (\n <div className=\"flex flex-wrap gap-2\">\n <Button size=\"sm\" onClick={() => openRespond(offer)}>\n <Check className=\"h-4 w-4\" />\n <span className=\"ml-1\">Respond</span>\n </Button>\n <Button variant=\"outline\" size=\"sm\" disabled={withdrawing} onClick={() => handleWithdraw(offer)}>\n <Undo2 className=\"h-4 w-4\" />\n <span className=\"ml-1\">Withdraw</span>\n </Button>\n {offer.property && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => navigate(`../properties/${offer.property?.id}`)}\n >\n <span>View property</span>\n <ArrowRight className=\"ml-1 h-4 w-4\" />\n </Button>\n )}\n </div>\n )}\n </GlassCard>\n );\n })}\n </div>\n\n <Pagination\n page={pagination.page}\n pageSize={pagination.pageSize}\n hasNextPage={offers.length >= pagination.pageSize}\n hasPreviousPage={pagination.page > 1}\n onPageChange={pagination.setPage}\n onPageSizeChange={pagination.setPageSize}\n />\n </div>\n )}\n\n {/* Respond to offer */}\n <ResponsiveDialog open={respondTarget !== null} onOpenChange={(open) => !open && setRespondTarget(null)}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleRespond}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Respond to offer</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {respondTarget?.property?.title\n ? `Offer of ${formatCurrency(respondTarget.amount, respondTarget.currency)} on ${respondTarget.property.title}.`\n : 'Accept, counter, or reject this offer.'}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div className=\"flex flex-wrap gap-2\">\n {(['accept', 'counter', 'reject'] as const).map((value) => (\n <button\n key={value}\n type=\"button\"\n onClick={() => setAction(value)}\n className={cn(\n 'inline-flex items-center gap-1 rounded-md px-3 py-1.5 text-sm font-medium capitalize transition-colors',\n action === value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {value === 'accept' && <Check className=\"h-4 w-4\" />}\n {value === 'counter' && <Repeat className=\"h-4 w-4\" />}\n {value === 'reject' && <X className=\"h-4 w-4\" />}\n {value}\n </button>\n ))}\n </div>\n {action === 'counter' && (\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Counter amount *</label>\n <input\n type=\"number\"\n min={1}\n step=\"any\"\n value={counterAmount}\n onChange={(event) => setCounterAmount(event.target.value)}\n className={inputClass}\n />\n </div>\n )}\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Note (optional)</label>\n <textarea\n value={notes}\n onChange={(event) => setNotes(event.target.value)}\n rows={2}\n className=\"w-full resize-none 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 </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setRespondTarget(null)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={responding}>\n {responding ? 'Sending…' : 'Send response'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OffersPage.js","names":[],"sources":["../../../../src/housingvista/pages/offers/OffersPage.tsx"],"sourcesContent":["/**\n * Offers — the buyer/owner surface for property offers. Buyers track offers they\n * submitted (status timeline submitted→countered→accepted/rejected/withdrawn)\n * and can withdraw; owners can respond (accept / counter / reject). Server-side\n * status filter + pagination, with live per-status tab counts driven off a real\n * unfiltered read (no fabricated numbers).\n * @module housingvista\n */\n\nimport { useState, type FormEvent } from 'react';\nimport { useNavigate } from 'react-router';\nimport { Tag, Check, X, Repeat, Undo2, MapPin, ArrowRight, Home, MessageSquare } 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 { ContactOwnerDialog } from '../../components';\nimport { useOffers, useRespondOffer, useWithdrawOffer, type OfferRow } from '../../hooks';\nimport { useServerPagination } from '../../hooks/useServerPagination';\nimport { notify } from '../../lib/toast';\nimport { cn, formatCurrency, formatDate } from '../../utils/helpers';\nimport { useTrackPageView } from '../../hooks/useHousingVistaAnalytics';\n\nconst statusTabs: { value: string; label: string }[] = [\n { value: 'all', label: 'All' },\n { value: 'submitted', label: 'Submitted' },\n { value: 'countered', label: 'Countered' },\n { value: 'accepted', label: 'Accepted' },\n { value: 'rejected', label: 'Rejected' },\n { value: 'withdrawn', label: 'Withdrawn' },\n { value: 'expired', label: 'Expired' },\n];\n\nfunction statusVariant(status: OfferRow['status']): 'success' | 'warning' | 'error' | 'info' | 'muted' {\n switch (status) {\n case 'accepted':\n return 'success';\n case 'countered':\n return 'warning';\n case 'rejected':\n return 'error';\n case 'submitted':\n return 'info';\n default:\n return 'muted';\n }\n}\n\n// ---- Status timeline (submitted → countered → accepted / rejected / withdrawn) ----\n\ntype NodeTone = 'success' | 'warning' | 'error' | 'info' | 'muted';\ninterface TimelineNode {\n label: string;\n date: string | null;\n tone: NodeTone;\n filled: boolean;\n}\n\nconst toneFill: Record<NodeTone, string> = {\n success: 'bg-status-success',\n warning: 'bg-status-warning',\n error: 'bg-status-error',\n info: 'bg-status-info',\n muted: 'bg-text-secondary',\n};\n\nfunction buildTimeline(offer: OfferRow): TimelineNode[] {\n const submitted: TimelineNode = { label: 'Submitted', date: offer.createdAt, tone: 'info', filled: true };\n const respondedAt = offer.respondedAt;\n switch (offer.status) {\n case 'accepted':\n return [\n { ...submitted, tone: 'success' },\n ...(offer.counterAmount != null\n ? [{ label: 'Countered', date: null, tone: 'success' as NodeTone, filled: true }]\n : []),\n { label: 'Accepted', date: respondedAt, tone: 'success', filled: true },\n ];\n case 'countered':\n return [\n { ...submitted, tone: 'warning' },\n { label: 'Countered', date: respondedAt, tone: 'warning', filled: true },\n { label: 'Accepted', date: null, tone: 'muted', filled: false },\n ];\n case 'rejected':\n return [{ ...submitted }, { label: 'Rejected', date: respondedAt, tone: 'error', filled: true }];\n case 'withdrawn':\n return [{ ...submitted }, { label: 'Withdrawn', date: offer.updatedAt, tone: 'muted', filled: true }];\n case 'expired':\n return [{ ...submitted }, { label: 'Expired', date: offer.expiresAt, tone: 'muted', filled: true }];\n default:\n // submitted\n return [{ ...submitted }, { label: 'Accepted', date: null, tone: 'muted', filled: false }];\n }\n}\n\nfunction OfferTimeline({ offer }: { offer: OfferRow }) {\n const nodes = buildTimeline(offer);\n return (\n <ol className=\"flex items-start\" aria-label=\"Offer status timeline\">\n {nodes.map((node, index) => {\n const isLast = index === nodes.length - 1;\n const nextFilled = !isLast && nodes[index + 1].filled;\n return (\n <li key={node.label} className={cn('flex flex-col items-center', !isLast && 'flex-1')}>\n <div className=\"flex w-full items-center\">\n <span\n className={cn(\n 'flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-full',\n node.filled ? toneFill[node.tone] : 'border-2 border-border-subtle bg-bg-surface'\n )}\n />\n {!isLast && (\n <span\n className={cn(\n 'mx-1 h-0.5 flex-1 rounded-full',\n node.filled && nextFilled ? toneFill[node.tone] : 'bg-border-subtle'\n )}\n />\n )}\n </div>\n <div className={cn('mt-1.5 text-center', isLast ? 'items-end' : '')}>\n <span className=\"block text-xs font-medium text-text-primary\">{node.label}</span>\n {node.date && <span className=\"block text-[0.65rem] text-text-secondary\">{formatDate(node.date)}</span>}\n </div>\n </li>\n );\n })}\n </ol>\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\nexport function OffersPage() {\n useTrackPageView('housingvista.offer.viewed');\n const navigate = useNavigate();\n const pagination = useServerPagination(10);\n const [activeTab, setActiveTab] = useState('all');\n const { data: offers, loading } = useOffers({\n filter: activeTab === 'all' ? undefined : { status: activeTab },\n pagination: pagination.pagination,\n });\n // Unfiltered read powers the live tab counts + summary stats (real data).\n const { data: allOffers } = useOffers({ pagination: { limit: 100, offset: 0 } });\n const { respondOffer, loading: responding } = useRespondOffer();\n const { withdrawOffer, loading: withdrawing } = useWithdrawOffer();\n\n const [respondTarget, setRespondTarget] = useState<OfferRow | null>(null);\n const [messageTarget, setMessageTarget] = useState<OfferRow | null>(null);\n const [action, setAction] = useState<'accept' | 'reject' | 'counter'>('accept');\n const [counterAmount, setCounterAmount] = useState('');\n const [notes, setNotes] = useState('');\n\n const openRespond = (offer: OfferRow) => {\n setRespondTarget(offer);\n setAction('accept');\n setCounterAmount(String(offer.amount));\n setNotes('');\n };\n\n const handleRespond = async (event: FormEvent) => {\n event.preventDefault();\n if (!respondTarget) return;\n if (action === 'counter') {\n const amount = Number(counterAmount);\n if (!Number.isFinite(amount) || amount <= 0) {\n notify.warning('Enter a valid counter amount');\n return;\n }\n }\n try {\n await respondOffer(respondTarget.id, {\n action,\n counterAmount: action === 'counter' ? Number(counterAmount) : undefined,\n notes: notes.trim() || undefined,\n });\n notify.success(\n 'Response sent',\n `Offer ${action === 'accept' ? 'accepted' : action === 'reject' ? 'rejected' : 'countered'}.`\n );\n setRespondTarget(null);\n } catch (err) {\n notify.error('Could not respond to offer', (err as Error).message);\n }\n };\n\n const handleWithdraw = async (offer: OfferRow) => {\n try {\n await withdrawOffer(offer.id);\n notify.success('Offer withdrawn');\n } catch (err) {\n notify.error('Could not withdraw offer', (err as Error).message);\n }\n };\n\n const countFor = (value: string): number =>\n value === 'all' ? allOffers.length : allOffers.filter((o) => o.status === value).length;\n\n const activeCount = allOffers.filter((o) => o.status === 'submitted' || o.status === 'countered').length;\n const acceptedCount = allOffers.filter((o) => o.status === 'accepted').length;\n\n return (\n <PageLayout\n title=\"Offers\"\n description=\"Track the offers you have made and respond to offers on your listings\"\n purpose=\"Every offer in one place: watch its status move from submitted to countered, accepted, or rejected — withdraw one you no longer want, or respond to offers coming in on properties you own.\"\n >\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <StatCard label=\"Total offers\" value={allOffers.length} icon={<Tag className=\"h-4 w-4\" />} />\n <StatCard label=\"Awaiting action\" value={activeCount} icon={<Repeat className=\"h-4 w-4\" />} />\n <StatCard label=\"Accepted\" value={acceptedCount} icon={<Check className=\"h-4 w-4\" />} />\n </div>\n </EmphasisPanel>\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 const count = countFor(tab.value);\n const isActive = activeTab === tab.value;\n return (\n <button\n key={tab.value}\n onClick={() => {\n setActiveTab(tab.value);\n pagination.setPage(1);\n }}\n className={cn(\n 'inline-flex shrink-0 items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n isActive\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 <span\n className={cn(\n 'inline-flex min-w-5 items-center justify-center rounded-full px-1.5 text-[0.65rem] font-semibold tabular-nums',\n isActive ? 'bg-action-primary-text/20 text-action-primary-text' : 'bg-bg-surface text-text-secondary'\n )}\n >\n {count}\n </span>\n </button>\n );\n })}\n </div>\n\n {offers.length === 0 && !loading ? (\n <EmptyState\n illustration=\"empty-search\"\n icon={<Tag className=\"h-12 w-12\" />}\n title=\"No offers here\"\n description=\"Offers you submit on properties — and offers made on your listings — will appear here.\"\n action={<Button onClick={() => navigate('../properties')}>Browse properties</Button>}\n />\n ) : (\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 xl:grid-cols-2\">\n {offers.map((offer) => {\n const image = offer.property?.images.find((img) => img.isPrimary) ?? offer.property?.images[0];\n const canAct = offer.status === 'submitted' || offer.status === 'countered';\n return (\n <GlassCard\n key={offer.id}\n treatment={canAct ? 'insight' : 'plain'}\n glow={canAct}\n className=\"flex flex-col gap-4 p-5\"\n >\n <div className=\"flex items-start gap-3\">\n <button\n type=\"button\"\n onClick={() => offer.property && navigate(`../properties/${offer.property.id}`)}\n className=\"shrink-0\"\n aria-label={`Open ${offer.property?.title ?? 'property'}`}\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 </button>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-start justify-between gap-2\">\n <button\n type=\"button\"\n onClick={() => offer.property && navigate(`../properties/${offer.property.id}`)}\n className=\"text-left\"\n >\n <h3 className=\"font-semibold text-text-primary hover:text-primary\">\n {offer.property?.title ?? 'Property'}\n </h3>\n {offer.property && (\n <p className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <MapPin className=\"h-3 w-3\" />\n {offer.property.location.city}, {offer.property.location.state}\n </p>\n )}\n </button>\n <StatusBadge label={offer.status} variant={statusVariant(offer.status)} dot />\n </div>\n <div className=\"mt-2 flex items-baseline gap-3\">\n <span className=\"text-xl font-bold tabular-nums text-text-primary\">\n {formatCurrency(offer.amount, offer.currency)}\n </span>\n {offer.counterAmount != null && (\n <span className=\"text-sm text-text-secondary\">\n Counter{' '}\n <span className=\"font-semibold text-status-warning-text\">\n {formatCurrency(offer.counterAmount, offer.currency)}\n </span>\n </span>\n )}\n </div>\n <p className=\"mt-0.5 text-xs text-text-secondary\">Submitted {formatDate(offer.createdAt)}</p>\n {(offer.status === 'submitted' || offer.status === 'countered') && offer.expiresAt && (\n <p className=\"mt-0.5 text-xs text-text-secondary\">Expires {formatDate(offer.expiresAt)}</p>\n )}\n </div>\n </div>\n\n <div className=\"rounded-lg bg-bg-sunken/50 p-3\">\n <OfferTimeline offer={offer} />\n </div>\n\n {(canAct || offer.property) && (\n <div className=\"flex flex-wrap gap-2\">\n {canAct && (\n <Button size=\"sm\" onClick={() => openRespond(offer)}>\n <Check className=\"h-4 w-4\" />\n <span className=\"ml-1\">Respond</span>\n </Button>\n )}\n {canAct && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={withdrawing}\n onClick={() => handleWithdraw(offer)}\n >\n <Undo2 className=\"h-4 w-4\" />\n <span className=\"ml-1\">Withdraw</span>\n </Button>\n )}\n {offer.property && (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setMessageTarget(offer)}>\n <MessageSquare className=\"h-4 w-4\" />\n <span className=\"ml-1\">Message</span>\n </Button>\n )}\n {offer.property && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => navigate(`../properties/${offer.property?.id}`)}\n >\n <span>View property</span>\n <ArrowRight className=\"ml-1 h-4 w-4\" />\n </Button>\n )}\n </div>\n )}\n </GlassCard>\n );\n })}\n </div>\n\n <Pagination\n page={pagination.page}\n pageSize={pagination.pageSize}\n hasNextPage={offers.length >= pagination.pageSize}\n hasPreviousPage={pagination.page > 1}\n onPageChange={pagination.setPage}\n onPageSizeChange={pagination.setPageSize}\n />\n </div>\n )}\n\n {/* Message the other party about this offer — the conversation thread is\n property-scoped (ContactOwnerDialog), the same thread both parties\n already reach from the property detail page; keyed by property id so\n switching offers never reuses a stale conversation (BOFF-8220). */}\n {messageTarget?.property && (\n <ContactOwnerDialog\n key={messageTarget.property.id}\n open={messageTarget !== null}\n onOpenChange={(open) => !open && setMessageTarget(null)}\n entityId={messageTarget.property.id}\n entityType=\"housingvista-property\"\n title={messageTarget.property.title}\n pagePath=\"/housingvista/offers\"\n heading=\"Message\"\n description={`Discuss ${messageTarget.property.title} with the other party. The thread also appears in Messages.`}\n />\n )}\n\n {/* Respond to offer */}\n <ResponsiveDialog open={respondTarget !== null} onOpenChange={(open) => !open && setRespondTarget(null)}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleRespond}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Respond to offer</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {respondTarget?.property?.title\n ? `Offer of ${formatCurrency(respondTarget.amount, respondTarget.currency)} on ${respondTarget.property.title}.`\n : 'Accept, counter, or reject this offer.'}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div className=\"flex flex-wrap gap-2\">\n {(['accept', 'counter', 'reject'] as const).map((value) => (\n <button\n key={value}\n type=\"button\"\n onClick={() => setAction(value)}\n className={cn(\n 'inline-flex items-center gap-1 rounded-md px-3 py-1.5 text-sm font-medium capitalize transition-colors',\n action === value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {value === 'accept' && <Check className=\"h-4 w-4\" />}\n {value === 'counter' && <Repeat className=\"h-4 w-4\" />}\n {value === 'reject' && <X className=\"h-4 w-4\" />}\n {value}\n </button>\n ))}\n </div>\n {action === 'counter' && (\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Counter amount *</label>\n <input\n type=\"number\"\n min={1}\n step=\"any\"\n value={counterAmount}\n onChange={(event) => setCounterAmount(event.target.value)}\n className={inputClass}\n />\n </div>\n )}\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Note (optional)</label>\n <textarea\n value={notes}\n onChange={(event) => setNotes(event.target.value)}\n rows={2}\n className=\"w-full resize-none 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 </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setRespondTarget(null)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={responding}>\n {responding ? 'Sending…' : 'Send response'}\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.1001.1",
|
|
4
4
|
"description": "Burdenoff HousingVista Microfrontend - Comprehensive real estate platform for property management, investment, and transactions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|