@burdenoff/microfe-housingvista 2026.709.2 → 2026.709.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/housingvista/HousingVistaRoutes.d.ts.map +1 -1
- package/dist/housingvista/HousingVistaRoutes.js +136 -37
- package/dist/housingvista/HousingVistaRoutes.js.map +1 -1
- package/dist/housingvista/hooks/useHousingVistaAnalytics.d.ts +1 -1
- package/dist/housingvista/hooks/useHousingVistaAnalytics.d.ts.map +1 -1
- package/dist/housingvista/hooks/useHousingVistaAnalytics.js +4 -4
- package/dist/housingvista/hooks/useHousingVistaAnalytics.js.map +1 -1
- package/dist/housingvista/pages/amc/AMCManagementPage.d.ts.map +1 -1
- package/dist/housingvista/pages/amc/AMCManagementPage.js +62 -60
- package/dist/housingvista/pages/amc/AMCManagementPage.js.map +1 -1
- package/dist/housingvista/pages/assets/AssetDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/assets/AssetDetailPage.js +141 -138
- package/dist/housingvista/pages/assets/AssetDetailPage.js.map +1 -1
- package/dist/housingvista/pages/assets/AssetManagementPage.d.ts.map +1 -1
- package/dist/housingvista/pages/assets/AssetManagementPage.js +193 -191
- package/dist/housingvista/pages/assets/AssetManagementPage.js.map +1 -1
- package/dist/housingvista/pages/chat/ChatPage.d.ts.map +1 -1
- package/dist/housingvista/pages/chat/ChatPage.js +115 -113
- package/dist/housingvista/pages/chat/ChatPage.js.map +1 -1
- package/dist/housingvista/pages/finance/HomeLoanDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/finance/HomeLoanDetailPage.js +152 -149
- package/dist/housingvista/pages/finance/HomeLoanDetailPage.js.map +1 -1
- package/dist/housingvista/pages/finance/HomeLoansPage.d.ts.map +1 -1
- package/dist/housingvista/pages/finance/HomeLoansPage.js +141 -139
- package/dist/housingvista/pages/finance/HomeLoansPage.js.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentDetailPage.js +290 -287
- package/dist/housingvista/pages/investments/InvestmentDetailPage.js.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentsPage.d.ts.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentsPage.js +64 -62
- package/dist/housingvista/pages/investments/InvestmentsPage.js.map +1 -1
- package/dist/housingvista/pages/legal/LegalPage.d.ts.map +1 -1
- package/dist/housingvista/pages/legal/LegalPage.js +95 -93
- package/dist/housingvista/pages/legal/LegalPage.js.map +1 -1
- package/dist/housingvista/pages/legal/LegalServiceDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/legal/LegalServiceDetailPage.js +129 -126
- package/dist/housingvista/pages/legal/LegalServiceDetailPage.js.map +1 -1
- package/dist/housingvista/pages/market/AreaInsightsPage.d.ts.map +1 -1
- package/dist/housingvista/pages/market/AreaInsightsPage.js +99 -97
- package/dist/housingvista/pages/market/AreaInsightsPage.js.map +1 -1
- package/dist/housingvista/pages/market/MarketAnalyticsPage.d.ts.map +1 -1
- package/dist/housingvista/pages/market/MarketAnalyticsPage.js +138 -136
- package/dist/housingvista/pages/market/MarketAnalyticsPage.js.map +1 -1
- package/dist/housingvista/pages/marketplace/MarketplacePage.d.ts.map +1 -1
- package/dist/housingvista/pages/marketplace/MarketplacePage.js +99 -97
- package/dist/housingvista/pages/marketplace/MarketplacePage.js.map +1 -1
- package/dist/housingvista/pages/marketplace/ServiceDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/marketplace/ServiceDetailPage.js +137 -134
- package/dist/housingvista/pages/marketplace/ServiceDetailPage.js.map +1 -1
- package/dist/housingvista/pages/offers/OffersPage.d.ts.map +1 -1
- package/dist/housingvista/pages/offers/OffersPage.js +94 -92
- package/dist/housingvista/pages/offers/OffersPage.js.map +1 -1
- package/dist/housingvista/pages/properties/AddPropertyPage.d.ts.map +1 -1
- package/dist/housingvista/pages/properties/AddPropertyPage.js +280 -279
- package/dist/housingvista/pages/properties/AddPropertyPage.js.map +1 -1
- package/dist/housingvista/pages/rental/RentalManagementPage.d.ts.map +1 -1
- package/dist/housingvista/pages/rental/RentalManagementPage.js +153 -151
- package/dist/housingvista/pages/rental/RentalManagementPage.js.map +1 -1
- package/dist/housingvista/pages/settings/ProfilePage.d.ts.map +1 -1
- package/dist/housingvista/pages/settings/ProfilePage.js +52 -50
- package/dist/housingvista/pages/settings/ProfilePage.js.map +1 -1
- package/dist/housingvista/pages/settings/SettingsPage.d.ts.map +1 -1
- package/dist/housingvista/pages/settings/SettingsPage.js +54 -52
- package/dist/housingvista/pages/settings/SettingsPage.js.map +1 -1
- package/dist/housingvista/pages/shortlist/ShortlistPage.d.ts.map +1 -1
- package/dist/housingvista/pages/shortlist/ShortlistPage.js +43 -41
- package/dist/housingvista/pages/shortlist/ShortlistPage.js.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionDetailPage.js +211 -208
- package/dist/housingvista/pages/transactions/TransactionDetailPage.js.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionsPage.d.ts.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionsPage.js +85 -83
- package/dist/housingvista/pages/transactions/TransactionsPage.js.map +1 -1
- package/dist/housingvista/pages/verification/VerificationDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/verification/VerificationDetailPage.js +25 -22
- package/dist/housingvista/pages/verification/VerificationDetailPage.js.map +1 -1
- package/dist/housingvista/pages/verification/VerificationPage.d.ts.map +1 -1
- package/dist/housingvista/pages/verification/VerificationPage.js +123 -121
- package/dist/housingvista/pages/verification/VerificationPage.js.map +1 -1
- package/dist/housingvista/pages/viewings/ViewingsPage.d.ts.map +1 -1
- package/dist/housingvista/pages/viewings/ViewingsPage.js +128 -126
- package/dist/housingvista/pages/viewings/ViewingsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,17 +3,18 @@ import { PageLayout as r } from "../../components/PageLayout.js";
|
|
|
3
3
|
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
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { useTrackPageView as s } from "../../hooks/useHousingVistaAnalytics.js";
|
|
7
|
+
import { useOffers as c, useRespondOffer as l, useWithdrawOffer as u } from "../../hooks/index.js";
|
|
8
|
+
import { notify as d } from "../../lib/toast.js";
|
|
8
9
|
import "../../components/index.js";
|
|
9
|
-
import { useServerPagination as
|
|
10
|
-
import { useState as
|
|
11
|
-
import { ArrowRight as
|
|
10
|
+
import { useServerPagination as f } from "../../hooks/useServerPagination.js";
|
|
11
|
+
import { useState as p } from "react";
|
|
12
|
+
import { ArrowRight as m, Check as h, Home as g, MapPin as _, Repeat as v, Tag as y, Undo2 as b, X as ee } from "lucide-react";
|
|
12
13
|
import { useNavigate as x } from "react-router-dom";
|
|
13
14
|
import { jsx as S, jsxs as C } from "react/jsx-runtime";
|
|
14
|
-
import { Button as w, EmphasisPanel as T, GlassCard as
|
|
15
|
+
import { Button as w, EmphasisPanel as T, GlassCard as te, Pagination as ne, ResponsiveDialog as re, ResponsiveDialogContent as ie, ResponsiveDialogDescription as E, ResponsiveDialogFooter as D, ResponsiveDialogHeader as O, ResponsiveDialogTitle as k } from "@burdenoff/fe-libs/ui";
|
|
15
16
|
//#region src/housingvista/pages/offers/OffersPage.tsx
|
|
16
|
-
var
|
|
17
|
+
var A = [
|
|
17
18
|
{
|
|
18
19
|
value: "all",
|
|
19
20
|
label: "All"
|
|
@@ -39,7 +40,7 @@ var j = [
|
|
|
39
40
|
label: "Withdrawn"
|
|
40
41
|
}
|
|
41
42
|
];
|
|
42
|
-
function
|
|
43
|
+
function j(e) {
|
|
43
44
|
switch (e) {
|
|
44
45
|
case "accepted": return "success";
|
|
45
46
|
case "countered": return "warning";
|
|
@@ -48,14 +49,14 @@ function M(e) {
|
|
|
48
49
|
default: return "muted";
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
|
-
var
|
|
52
|
+
var M = {
|
|
52
53
|
success: "bg-status-success",
|
|
53
54
|
warning: "bg-status-warning",
|
|
54
55
|
error: "bg-status-error",
|
|
55
56
|
info: "bg-status-info",
|
|
56
57
|
muted: "bg-text-secondary"
|
|
57
58
|
};
|
|
58
|
-
function
|
|
59
|
+
function N(e) {
|
|
59
60
|
let t = {
|
|
60
61
|
label: "Submitted",
|
|
61
62
|
date: e.createdAt,
|
|
@@ -119,8 +120,8 @@ function P(e) {
|
|
|
119
120
|
}];
|
|
120
121
|
}
|
|
121
122
|
}
|
|
122
|
-
function
|
|
123
|
-
let r =
|
|
123
|
+
function P({ offer: t }) {
|
|
124
|
+
let r = N(t);
|
|
124
125
|
return /* @__PURE__ */ S("ol", {
|
|
125
126
|
className: "flex items-start",
|
|
126
127
|
"aria-label": "Offer status timeline",
|
|
@@ -130,7 +131,7 @@ function F({ offer: t }) {
|
|
|
130
131
|
className: e("flex flex-col items-center", !a && "flex-1"),
|
|
131
132
|
children: [/* @__PURE__ */ C("div", {
|
|
132
133
|
className: "flex w-full items-center",
|
|
133
|
-
children: [/* @__PURE__ */ S("span", { className: e("flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-full", t.filled ?
|
|
134
|
+
children: [/* @__PURE__ */ S("span", { className: e("flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-full", t.filled ? M[t.tone] : "border-2 border-border-subtle bg-bg-surface") }), !a && /* @__PURE__ */ S("span", { className: e("mx-1 h-0.5 flex-1 rounded-full", t.filled && o ? M[t.tone] : "bg-border-subtle") })]
|
|
134
135
|
}), /* @__PURE__ */ C("div", {
|
|
135
136
|
className: e("mt-1.5 text-center", a ? "items-end" : ""),
|
|
136
137
|
children: [/* @__PURE__ */ S("span", {
|
|
@@ -145,42 +146,43 @@ function F({ offer: t }) {
|
|
|
145
146
|
})
|
|
146
147
|
});
|
|
147
148
|
}
|
|
148
|
-
var
|
|
149
|
-
function
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
149
|
+
var F = "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)]";
|
|
150
|
+
function I() {
|
|
151
|
+
s("housingvista.offer.viewed");
|
|
152
|
+
let M = x(), N = f(10), [I, L] = p("all"), { data: R, loading: z } = c({
|
|
153
|
+
filter: I === "all" ? void 0 : { status: I },
|
|
154
|
+
pagination: N.pagination
|
|
155
|
+
}), { data: B } = c({ pagination: {
|
|
154
156
|
limit: 100,
|
|
155
157
|
offset: 0
|
|
156
|
-
} }), { respondOffer:
|
|
157
|
-
|
|
158
|
-
},
|
|
159
|
-
if (e.preventDefault(),
|
|
160
|
-
if (
|
|
161
|
-
let e = Number(
|
|
158
|
+
} }), { respondOffer: V, loading: H } = l(), { withdrawOffer: U, loading: W } = u(), [G, K] = p(null), [q, J] = p("accept"), [Y, X] = p(""), [Z, Q] = p(""), ae = (e) => {
|
|
159
|
+
K(e), J("accept"), X(String(e.amount)), Q("");
|
|
160
|
+
}, oe = async (e) => {
|
|
161
|
+
if (e.preventDefault(), G) {
|
|
162
|
+
if (q === "counter") {
|
|
163
|
+
let e = Number(Y);
|
|
162
164
|
if (!Number.isFinite(e) || e <= 0) {
|
|
163
|
-
|
|
165
|
+
d.warning("Enter a valid counter amount");
|
|
164
166
|
return;
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
169
|
try {
|
|
168
|
-
await
|
|
169
|
-
action:
|
|
170
|
-
counterAmount:
|
|
171
|
-
notes:
|
|
172
|
-
}),
|
|
170
|
+
await V(G.id, {
|
|
171
|
+
action: q,
|
|
172
|
+
counterAmount: q === "counter" ? Number(Y) : void 0,
|
|
173
|
+
notes: Z.trim() || void 0
|
|
174
|
+
}), d.success("Response sent", `Offer ${q === "accept" ? "accepted" : q === "reject" ? "rejected" : "countered"}.`), K(null);
|
|
173
175
|
} catch (e) {
|
|
174
|
-
|
|
176
|
+
d.error("Could not respond to offer", e.message);
|
|
175
177
|
}
|
|
176
178
|
}
|
|
177
|
-
},
|
|
179
|
+
}, se = async (e) => {
|
|
178
180
|
try {
|
|
179
|
-
await
|
|
181
|
+
await U(e.id), d.success("Offer withdrawn");
|
|
180
182
|
} catch (e) {
|
|
181
|
-
|
|
183
|
+
d.error("Could not withdraw offer", e.message);
|
|
182
184
|
}
|
|
183
|
-
},
|
|
185
|
+
}, ce = (e) => e === "all" ? B.length : B.filter((t) => t.status === e).length, le = B.filter((e) => e.status === "submitted" || e.status === "countered").length, $ = B.filter((e) => e.status === "accepted").length;
|
|
184
186
|
return /* @__PURE__ */ C(r, {
|
|
185
187
|
title: "Offers",
|
|
186
188
|
description: "Track the offers you have made and respond to offers on your listings",
|
|
@@ -193,29 +195,29 @@ function L() {
|
|
|
193
195
|
children: [
|
|
194
196
|
/* @__PURE__ */ S(i, {
|
|
195
197
|
label: "Total offers",
|
|
196
|
-
value:
|
|
197
|
-
icon: /* @__PURE__ */ S(
|
|
198
|
+
value: B.length,
|
|
199
|
+
icon: /* @__PURE__ */ S(y, { className: "h-4 w-4" })
|
|
198
200
|
}),
|
|
199
201
|
/* @__PURE__ */ S(i, {
|
|
200
202
|
label: "Awaiting action",
|
|
201
|
-
value:
|
|
202
|
-
icon: /* @__PURE__ */ S(
|
|
203
|
+
value: le,
|
|
204
|
+
icon: /* @__PURE__ */ S(v, { className: "h-4 w-4" })
|
|
203
205
|
}),
|
|
204
206
|
/* @__PURE__ */ S(i, {
|
|
205
207
|
label: "Accepted",
|
|
206
|
-
value:
|
|
207
|
-
icon: /* @__PURE__ */ S(
|
|
208
|
+
value: $,
|
|
209
|
+
icon: /* @__PURE__ */ S(h, { className: "h-4 w-4" })
|
|
208
210
|
})
|
|
209
211
|
]
|
|
210
212
|
})
|
|
211
213
|
}),
|
|
212
214
|
/* @__PURE__ */ S("div", {
|
|
213
215
|
className: "-mx-1 flex flex-nowrap items-center gap-2 overflow-x-auto px-1 pb-1 sm:flex-wrap sm:overflow-visible",
|
|
214
|
-
children:
|
|
215
|
-
let n =
|
|
216
|
+
children: A.map((t) => {
|
|
217
|
+
let n = ce(t.value), r = I === t.value;
|
|
216
218
|
return /* @__PURE__ */ C("button", {
|
|
217
219
|
onClick: () => {
|
|
218
|
-
|
|
220
|
+
L(t.value), N.setPage(1);
|
|
219
221
|
},
|
|
220
222
|
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"),
|
|
221
223
|
children: [t.label, /* @__PURE__ */ S("span", {
|
|
@@ -225,22 +227,22 @@ function L() {
|
|
|
225
227
|
}, t.value);
|
|
226
228
|
})
|
|
227
229
|
}),
|
|
228
|
-
|
|
230
|
+
R.length === 0 && !z ? /* @__PURE__ */ S(o, {
|
|
229
231
|
illustration: "empty-search",
|
|
230
|
-
icon: /* @__PURE__ */ S(
|
|
232
|
+
icon: /* @__PURE__ */ S(y, { className: "h-12 w-12" }),
|
|
231
233
|
title: "No offers here",
|
|
232
234
|
description: "Offers you submit on properties — and offers made on your listings — will appear here.",
|
|
233
235
|
action: /* @__PURE__ */ S(w, {
|
|
234
|
-
onClick: () =>
|
|
236
|
+
onClick: () => M("../properties"),
|
|
235
237
|
children: "Browse properties"
|
|
236
238
|
})
|
|
237
239
|
}) : /* @__PURE__ */ C("div", {
|
|
238
240
|
className: "space-y-4",
|
|
239
241
|
children: [/* @__PURE__ */ S("div", {
|
|
240
242
|
className: "grid grid-cols-1 gap-4 xl:grid-cols-2",
|
|
241
|
-
children:
|
|
243
|
+
children: R.map((e) => {
|
|
242
244
|
let r = e.property?.images.find((e) => e.isPrimary) ?? e.property?.images[0], i = e.status === "submitted" || e.status === "countered";
|
|
243
|
-
return /* @__PURE__ */ C(
|
|
245
|
+
return /* @__PURE__ */ C(te, {
|
|
244
246
|
treatment: i ? "insight" : "plain",
|
|
245
247
|
glow: i,
|
|
246
248
|
className: "flex flex-col gap-4 p-5",
|
|
@@ -249,7 +251,7 @@ function L() {
|
|
|
249
251
|
className: "flex items-start gap-3",
|
|
250
252
|
children: [/* @__PURE__ */ S("button", {
|
|
251
253
|
type: "button",
|
|
252
|
-
onClick: () => e.property &&
|
|
254
|
+
onClick: () => e.property && M(`../properties/${e.property.id}`),
|
|
253
255
|
className: "shrink-0",
|
|
254
256
|
"aria-label": `Open ${e.property?.title ?? "property"}`,
|
|
255
257
|
children: r ? /* @__PURE__ */ S("img", {
|
|
@@ -258,7 +260,7 @@ function L() {
|
|
|
258
260
|
className: "h-16 w-16 rounded-lg object-cover"
|
|
259
261
|
}) : /* @__PURE__ */ S("div", {
|
|
260
262
|
className: "flex h-16 w-16 items-center justify-center rounded-lg bg-bg-sunken",
|
|
261
|
-
children: /* @__PURE__ */ S(
|
|
263
|
+
children: /* @__PURE__ */ S(g, { className: "h-6 w-6 text-text-secondary" })
|
|
262
264
|
})
|
|
263
265
|
}), /* @__PURE__ */ C("div", {
|
|
264
266
|
className: "min-w-0 flex-1",
|
|
@@ -267,7 +269,7 @@ function L() {
|
|
|
267
269
|
className: "flex flex-wrap items-start justify-between gap-2",
|
|
268
270
|
children: [/* @__PURE__ */ C("button", {
|
|
269
271
|
type: "button",
|
|
270
|
-
onClick: () => e.property &&
|
|
272
|
+
onClick: () => e.property && M(`../properties/${e.property.id}`),
|
|
271
273
|
className: "text-left",
|
|
272
274
|
children: [/* @__PURE__ */ S("h3", {
|
|
273
275
|
className: "font-semibold text-text-primary hover:text-primary",
|
|
@@ -275,7 +277,7 @@ function L() {
|
|
|
275
277
|
}), e.property && /* @__PURE__ */ C("p", {
|
|
276
278
|
className: "flex items-center gap-1 text-xs text-text-secondary",
|
|
277
279
|
children: [
|
|
278
|
-
/* @__PURE__ */ S(
|
|
280
|
+
/* @__PURE__ */ S(_, { className: "h-3 w-3" }),
|
|
279
281
|
e.property.location.city,
|
|
280
282
|
", ",
|
|
281
283
|
e.property.location.state
|
|
@@ -283,7 +285,7 @@ function L() {
|
|
|
283
285
|
})]
|
|
284
286
|
}), /* @__PURE__ */ S(a, {
|
|
285
287
|
label: e.status,
|
|
286
|
-
variant:
|
|
288
|
+
variant: j(e.status),
|
|
287
289
|
dot: !0
|
|
288
290
|
})]
|
|
289
291
|
}),
|
|
@@ -313,15 +315,15 @@ function L() {
|
|
|
313
315
|
}),
|
|
314
316
|
/* @__PURE__ */ S("div", {
|
|
315
317
|
className: "rounded-lg bg-bg-sunken/50 p-3",
|
|
316
|
-
children: /* @__PURE__ */ S(
|
|
318
|
+
children: /* @__PURE__ */ S(P, { offer: e })
|
|
317
319
|
}),
|
|
318
320
|
i && /* @__PURE__ */ C("div", {
|
|
319
321
|
className: "flex flex-wrap gap-2",
|
|
320
322
|
children: [
|
|
321
323
|
/* @__PURE__ */ C(w, {
|
|
322
324
|
size: "sm",
|
|
323
|
-
onClick: () =>
|
|
324
|
-
children: [/* @__PURE__ */ S(
|
|
325
|
+
onClick: () => ae(e),
|
|
326
|
+
children: [/* @__PURE__ */ S(h, { className: "h-4 w-4" }), /* @__PURE__ */ S("span", {
|
|
325
327
|
className: "ml-1",
|
|
326
328
|
children: "Respond"
|
|
327
329
|
})]
|
|
@@ -329,9 +331,9 @@ function L() {
|
|
|
329
331
|
/* @__PURE__ */ C(w, {
|
|
330
332
|
variant: "outline",
|
|
331
333
|
size: "sm",
|
|
332
|
-
disabled:
|
|
333
|
-
onClick: () =>
|
|
334
|
-
children: [/* @__PURE__ */ S(
|
|
334
|
+
disabled: W,
|
|
335
|
+
onClick: () => se(e),
|
|
336
|
+
children: [/* @__PURE__ */ S(b, { className: "h-4 w-4" }), /* @__PURE__ */ S("span", {
|
|
335
337
|
className: "ml-1",
|
|
336
338
|
children: "Withdraw"
|
|
337
339
|
})]
|
|
@@ -339,32 +341,32 @@ function L() {
|
|
|
339
341
|
e.property && /* @__PURE__ */ C(w, {
|
|
340
342
|
variant: "ghost",
|
|
341
343
|
size: "sm",
|
|
342
|
-
onClick: () =>
|
|
343
|
-
children: [/* @__PURE__ */ S("span", { children: "View property" }), /* @__PURE__ */ S(
|
|
344
|
+
onClick: () => M(`../properties/${e.property?.id}`),
|
|
345
|
+
children: [/* @__PURE__ */ S("span", { children: "View property" }), /* @__PURE__ */ S(m, { className: "ml-1 h-4 w-4" })]
|
|
344
346
|
})
|
|
345
347
|
]
|
|
346
348
|
})
|
|
347
349
|
]
|
|
348
350
|
}, e.id);
|
|
349
351
|
})
|
|
350
|
-
}), /* @__PURE__ */ S(
|
|
351
|
-
page:
|
|
352
|
-
pageSize:
|
|
353
|
-
hasNextPage:
|
|
354
|
-
hasPreviousPage:
|
|
355
|
-
onPageChange:
|
|
356
|
-
onPageSizeChange:
|
|
352
|
+
}), /* @__PURE__ */ S(ne, {
|
|
353
|
+
page: N.page,
|
|
354
|
+
pageSize: N.pageSize,
|
|
355
|
+
hasNextPage: R.length >= N.pageSize,
|
|
356
|
+
hasPreviousPage: N.page > 1,
|
|
357
|
+
onPageChange: N.setPage,
|
|
358
|
+
onPageSizeChange: N.setPageSize
|
|
357
359
|
})]
|
|
358
360
|
}),
|
|
359
|
-
/* @__PURE__ */ S(
|
|
360
|
-
open:
|
|
361
|
-
onOpenChange: (e) => !e &&
|
|
362
|
-
children: /* @__PURE__ */ S(
|
|
361
|
+
/* @__PURE__ */ S(re, {
|
|
362
|
+
open: G !== null,
|
|
363
|
+
onOpenChange: (e) => !e && K(null),
|
|
364
|
+
children: /* @__PURE__ */ S(ie, {
|
|
363
365
|
className: "sm:max-w-md",
|
|
364
366
|
children: /* @__PURE__ */ C("form", {
|
|
365
|
-
onSubmit:
|
|
367
|
+
onSubmit: oe,
|
|
366
368
|
children: [
|
|
367
|
-
/* @__PURE__ */ C(
|
|
369
|
+
/* @__PURE__ */ C(O, { children: [/* @__PURE__ */ S(k, { children: "Respond to offer" }), /* @__PURE__ */ S(E, { children: G?.property?.title ? `Offer of ${t(G.amount, G.currency)} on ${G.property.title}.` : "Accept, counter, or reject this offer." })] }),
|
|
368
370
|
/* @__PURE__ */ C("div", {
|
|
369
371
|
className: "space-y-3 py-2",
|
|
370
372
|
children: [
|
|
@@ -376,47 +378,47 @@ function L() {
|
|
|
376
378
|
"reject"
|
|
377
379
|
].map((t) => /* @__PURE__ */ C("button", {
|
|
378
380
|
type: "button",
|
|
379
|
-
onClick: () =>
|
|
380
|
-
className: e("inline-flex items-center gap-1 rounded-md px-3 py-1.5 text-sm font-medium capitalize transition-colors",
|
|
381
|
+
onClick: () => J(t),
|
|
382
|
+
className: e("inline-flex items-center gap-1 rounded-md px-3 py-1.5 text-sm font-medium capitalize transition-colors", q === t ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
|
|
381
383
|
children: [
|
|
382
|
-
t === "accept" && /* @__PURE__ */ S(
|
|
383
|
-
t === "counter" && /* @__PURE__ */ S(
|
|
384
|
-
t === "reject" && /* @__PURE__ */ S(
|
|
384
|
+
t === "accept" && /* @__PURE__ */ S(h, { className: "h-4 w-4" }),
|
|
385
|
+
t === "counter" && /* @__PURE__ */ S(v, { className: "h-4 w-4" }),
|
|
386
|
+
t === "reject" && /* @__PURE__ */ S(ee, { className: "h-4 w-4" }),
|
|
385
387
|
t
|
|
386
388
|
]
|
|
387
389
|
}, t))
|
|
388
390
|
}),
|
|
389
|
-
|
|
391
|
+
q === "counter" && /* @__PURE__ */ C("div", { children: [/* @__PURE__ */ S("label", {
|
|
390
392
|
className: "mb-1.5 block text-sm font-medium text-text-primary",
|
|
391
393
|
children: "Counter amount *"
|
|
392
394
|
}), /* @__PURE__ */ S("input", {
|
|
393
395
|
type: "number",
|
|
394
396
|
min: 1,
|
|
395
397
|
step: "any",
|
|
396
|
-
value:
|
|
397
|
-
onChange: (e) =>
|
|
398
|
-
className:
|
|
398
|
+
value: Y,
|
|
399
|
+
onChange: (e) => X(e.target.value),
|
|
400
|
+
className: F
|
|
399
401
|
})] }),
|
|
400
402
|
/* @__PURE__ */ C("div", { children: [/* @__PURE__ */ S("label", {
|
|
401
403
|
className: "mb-1.5 block text-sm font-medium text-text-primary",
|
|
402
404
|
children: "Note (optional)"
|
|
403
405
|
}), /* @__PURE__ */ S("textarea", {
|
|
404
|
-
value:
|
|
405
|
-
onChange: (e) =>
|
|
406
|
+
value: Z,
|
|
407
|
+
onChange: (e) => Q(e.target.value),
|
|
406
408
|
rows: 2,
|
|
407
409
|
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)]"
|
|
408
410
|
})] })
|
|
409
411
|
]
|
|
410
412
|
}),
|
|
411
|
-
/* @__PURE__ */ C(
|
|
413
|
+
/* @__PURE__ */ C(D, { children: [/* @__PURE__ */ S(w, {
|
|
412
414
|
type: "button",
|
|
413
415
|
variant: "outline",
|
|
414
|
-
onClick: () =>
|
|
416
|
+
onClick: () => K(null),
|
|
415
417
|
children: "Cancel"
|
|
416
418
|
}), /* @__PURE__ */ S(w, {
|
|
417
419
|
type: "submit",
|
|
418
|
-
disabled:
|
|
419
|
-
children:
|
|
420
|
+
disabled: H,
|
|
421
|
+
children: H ? "Sending…" : "Send response"
|
|
420
422
|
})] })
|
|
421
423
|
]
|
|
422
424
|
})
|
|
@@ -426,6 +428,6 @@ function L() {
|
|
|
426
428
|
});
|
|
427
429
|
}
|
|
428
430
|
//#endregion
|
|
429
|
-
export {
|
|
431
|
+
export { I as OffersPage };
|
|
430
432
|
|
|
431
433
|
//# 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-dom';\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';\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];\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 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 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 </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-dom';\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];\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 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 </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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddPropertyPage.d.ts","sourceRoot":"","sources":["../../../../src/housingvista/pages/properties/AddPropertyPage.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"AddPropertyPage.d.ts","sourceRoot":"","sources":["../../../../src/housingvista/pages/properties/AddPropertyPage.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuEH,eAAO,MAAM,eAAe,mCAsrB3B,CAAC"}
|