@burdenoff/microfe-housingvista 2026.703.11 → 2026.703.13
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.js +31 -20
- package/dist/housingvista/HousingVistaRoutes.js.map +1 -1
- package/dist/housingvista/components/ContactOwnerDialog.js +74 -58
- package/dist/housingvista/components/ContactOwnerDialog.js.map +1 -1
- package/dist/housingvista/components/PropertyCard.js +92 -67
- package/dist/housingvista/components/PropertyCard.js.map +1 -1
- package/dist/housingvista/hooks/messages.js +66 -0
- package/dist/housingvista/hooks/messages.js.map +1 -0
- package/dist/housingvista/pages/HomePage.js +182 -146
- package/dist/housingvista/pages/HomePage.js.map +1 -1
- package/dist/housingvista/pages/amc/AMCManagementPage.js +212 -217
- package/dist/housingvista/pages/amc/AMCManagementPage.js.map +1 -1
- package/dist/housingvista/pages/analytics/AnalyticsPage.js +377 -213
- package/dist/housingvista/pages/analytics/AnalyticsPage.js.map +1 -1
- package/dist/housingvista/pages/assets/AssetDetailPage.js +237 -144
- package/dist/housingvista/pages/assets/AssetDetailPage.js.map +1 -1
- package/dist/housingvista/pages/assets/AssetManagementPage.js +357 -233
- package/dist/housingvista/pages/assets/AssetManagementPage.js.map +1 -1
- package/dist/housingvista/pages/chat/ChatPage.js +260 -0
- package/dist/housingvista/pages/chat/ChatPage.js.map +1 -0
- package/dist/housingvista/pages/finance/HomeLoanDetailPage.js +157 -154
- package/dist/housingvista/pages/finance/HomeLoanDetailPage.js.map +1 -1
- package/dist/housingvista/pages/finance/HomeLoansPage.js +162 -161
- package/dist/housingvista/pages/finance/HomeLoansPage.js.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentDetailPage.js +3 -3
- package/dist/housingvista/pages/investments/InvestmentDetailPage.js.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentsPage.js +347 -234
- package/dist/housingvista/pages/investments/InvestmentsPage.js.map +1 -1
- package/dist/housingvista/pages/legal/LegalPage.js +146 -119
- package/dist/housingvista/pages/legal/LegalPage.js.map +1 -1
- package/dist/housingvista/pages/legal/LegalServiceDetailPage.js +132 -132
- package/dist/housingvista/pages/legal/LegalServiceDetailPage.js.map +1 -1
- package/dist/housingvista/pages/market/AreaInsightsPage.js +150 -115
- package/dist/housingvista/pages/market/AreaInsightsPage.js.map +1 -1
- package/dist/housingvista/pages/market/MarketAnalyticsPage.js +321 -203
- package/dist/housingvista/pages/market/MarketAnalyticsPage.js.map +1 -1
- package/dist/housingvista/pages/marketplace/MarketplacePage.js +89 -77
- package/dist/housingvista/pages/marketplace/MarketplacePage.js.map +1 -1
- package/dist/housingvista/pages/marketplace/ServiceDetailPage.js +2 -2
- package/dist/housingvista/pages/marketplace/ServiceDetailPage.js.map +1 -1
- package/dist/housingvista/pages/offers/OffersPage.js +297 -172
- package/dist/housingvista/pages/offers/OffersPage.js.map +1 -1
- package/dist/housingvista/pages/properties/AddPropertyPage.js +37 -13
- package/dist/housingvista/pages/properties/AddPropertyPage.js.map +1 -1
- package/dist/housingvista/pages/properties/PropertiesPage.js +1 -0
- package/dist/housingvista/pages/properties/PropertiesPage.js.map +1 -1
- package/dist/housingvista/pages/properties/PropertyDetailPage.js +803 -583
- package/dist/housingvista/pages/properties/PropertyDetailPage.js.map +1 -1
- package/dist/housingvista/pages/rental/RentalManagementPage.js +513 -418
- package/dist/housingvista/pages/rental/RentalManagementPage.js.map +1 -1
- package/dist/housingvista/pages/shortlist/ShortlistPage.js +43 -30
- package/dist/housingvista/pages/shortlist/ShortlistPage.js.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionDetailPage.js +207 -199
- package/dist/housingvista/pages/transactions/TransactionDetailPage.js.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionsPage.js +204 -84
- package/dist/housingvista/pages/transactions/TransactionsPage.js.map +1 -1
- package/dist/housingvista/pages/transactions/milestones.js +68 -0
- package/dist/housingvista/pages/transactions/milestones.js.map +1 -0
- package/dist/housingvista/pages/verification/VerificationPage.js +175 -125
- package/dist/housingvista/pages/verification/VerificationPage.js.map +1 -1
- package/dist/housingvista/pages/viewings/ViewingsPage.js +241 -136
- package/dist/housingvista/pages/viewings/ViewingsPage.js.map +1 -1
- package/dist/housingvista/utils/helpers.js +26 -7
- package/dist/housingvista/utils/helpers.js.map +1 -1
- package/dist/node_modules/lodash/maxBy.js +17 -0
- package/dist/node_modules/lodash/maxBy.js.map +1 -0
- package/dist/node_modules/lodash/minBy.js +17 -0
- package/dist/node_modules/lodash/minBy.js.map +1 -0
- package/dist/node_modules/recharts/es6/chart/BarChart.js +24 -0
- package/dist/node_modules/recharts/es6/chart/BarChart.js.map +1 -0
- package/dist/node_modules/recharts/es6/chart/PieChart.js +34 -0
- package/dist/node_modules/recharts/es6/chart/PieChart.js.map +1 -0
- package/dist/node_modules/recharts/es6/polar/Pie.js +435 -0
- package/dist/node_modules/recharts/es6/polar/Pie.js.map +1 -0
- package/dist/node_modules/recharts/es6/polar/PolarAngleAxis.js +215 -0
- package/dist/node_modules/recharts/es6/polar/PolarAngleAxis.js.map +1 -0
- package/dist/node_modules/recharts/es6/polar/PolarRadiusAxis.js +268 -0
- package/dist/node_modules/recharts/es6/polar/PolarRadiusAxis.js.map +1 -0
- package/dist/node_modules/recharts/es6/shape/Polygon.js +112 -0
- package/dist/node_modules/recharts/es6/shape/Polygon.js.map +1 -0
- package/dist/node_modules/recharts/es6/util/PolarUtils.js +133 -51
- package/dist/node_modules/recharts/es6/util/PolarUtils.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import { cn as e,
|
|
1
|
+
import { cn as e, formatCompactCurrency as t, formatNumber as n } from "../utils/helpers.js";
|
|
2
2
|
import { StatusBadge as r } from "./StatusBadge.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { useState as i } from "react";
|
|
4
|
+
import { Bath as a, Bed as o, Heart as s, Home as c, MapPin as l, Square as u } from "lucide-react";
|
|
5
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
5
6
|
//#region src/housingvista/components/PropertyCard.tsx
|
|
6
|
-
var
|
|
7
|
+
var p = {
|
|
7
8
|
available: "success",
|
|
8
9
|
active: "success",
|
|
9
10
|
listed: "success",
|
|
11
|
+
for_sale: "success",
|
|
12
|
+
"for sale": "success",
|
|
13
|
+
for_rent: "info",
|
|
14
|
+
"for rent": "info",
|
|
10
15
|
pending: "warning",
|
|
11
16
|
"under contract": "warning",
|
|
12
17
|
"under offer": "warning",
|
|
@@ -15,92 +20,112 @@ var d = {
|
|
|
15
20
|
off_market: "muted",
|
|
16
21
|
"off market": "muted",
|
|
17
22
|
draft: "muted"
|
|
18
|
-
},
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
className:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
}, m = () => /* @__PURE__ */ f("div", {
|
|
24
|
+
className: "relative flex h-full w-full items-center justify-center overflow-hidden bg-gradient-to-br from-[var(--color-action-primary-bg)]/15 via-bg-sunken to-[var(--color-accent-blue)]/15",
|
|
25
|
+
children: [/* @__PURE__ */ d("div", {
|
|
26
|
+
className: "absolute inset-0 opacity-[0.35]",
|
|
27
|
+
style: {
|
|
28
|
+
backgroundImage: "radial-gradient(circle at 1px 1px, var(--color-border-seam, rgba(120,120,120,.25)) 1px, transparent 0)",
|
|
29
|
+
backgroundSize: "16px 16px"
|
|
30
|
+
}
|
|
31
|
+
}), /* @__PURE__ */ f("div", {
|
|
32
|
+
className: "relative flex flex-col items-center gap-2 text-text-secondary/70",
|
|
33
|
+
children: [/* @__PURE__ */ d(c, { className: "h-9 w-9" }), /* @__PURE__ */ d("span", {
|
|
34
|
+
className: "text-[11px] font-medium tracking-wide",
|
|
35
|
+
children: "HousingVista"
|
|
36
|
+
})]
|
|
37
|
+
})]
|
|
38
|
+
}), h = ({ title: c, price: h, priceType: g, pricePeriod: _, currency: v = "USD", location: y, bedrooms: b, bathrooms: x, area: S, imageUrl: C, status: w, type: T, onClick: E, className: D, isShortlisted: O, onToggleShortlist: k }) => {
|
|
39
|
+
let [A, j] = i(!1), M = !!C && !A, N = t(h, v), P = g === "rent" ? `${N}${_ ? `/${_.replace("ly", "")}` : "/mo"}` : N, F = p[w.toLowerCase()] ?? "default";
|
|
40
|
+
return /* @__PURE__ */ f("div", {
|
|
41
|
+
className: e("group flex flex-col overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-elevation-1 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-elevation-2 focus-within:border-border-strong", E && "cursor-pointer", D),
|
|
42
|
+
onClick: E,
|
|
43
|
+
onKeyDown: (e) => {
|
|
44
|
+
E && (e.key === "Enter" || e.key === " ") && (e.preventDefault(), E());
|
|
45
|
+
},
|
|
46
|
+
role: E ? "button" : void 0,
|
|
47
|
+
tabIndex: E ? 0 : void 0,
|
|
48
|
+
"aria-label": E ? `View ${c}` : void 0,
|
|
49
|
+
children: [/* @__PURE__ */ f("div", {
|
|
50
|
+
className: "relative aspect-[16/10] shrink-0 overflow-hidden",
|
|
25
51
|
children: [
|
|
26
|
-
|
|
27
|
-
src:
|
|
28
|
-
alt:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
className: "
|
|
32
|
-
|
|
33
|
-
}),
|
|
34
|
-
/* @__PURE__ */
|
|
52
|
+
M ? /* @__PURE__ */ d("img", {
|
|
53
|
+
src: C,
|
|
54
|
+
alt: c,
|
|
55
|
+
loading: "lazy",
|
|
56
|
+
onError: () => j(!0),
|
|
57
|
+
className: "h-full w-full object-cover transition-[filter] duration-300 group-hover:brightness-[1.02]"
|
|
58
|
+
}) : /* @__PURE__ */ d(m, {}),
|
|
59
|
+
/* @__PURE__ */ d("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 h-24 bg-gradient-to-t from-bg-overlay/75 to-transparent" }),
|
|
60
|
+
/* @__PURE__ */ d("div", {
|
|
35
61
|
className: "absolute left-3 top-3",
|
|
36
|
-
children: /* @__PURE__ */
|
|
37
|
-
label:
|
|
38
|
-
variant:
|
|
62
|
+
children: /* @__PURE__ */ d(r, {
|
|
63
|
+
label: w,
|
|
64
|
+
variant: F,
|
|
39
65
|
dot: !0
|
|
40
66
|
})
|
|
41
67
|
}),
|
|
42
|
-
|
|
68
|
+
k && /* @__PURE__ */ d("button", {
|
|
69
|
+
type: "button",
|
|
43
70
|
onClick: (e) => {
|
|
44
|
-
e.stopPropagation(),
|
|
71
|
+
e.stopPropagation(), k();
|
|
45
72
|
},
|
|
46
|
-
className: e("absolute right-3 top-3 rounded-full bg-bg-surface/
|
|
47
|
-
"aria-
|
|
48
|
-
|
|
73
|
+
className: e("absolute right-3 top-3 rounded-full bg-bg-surface/85 p-2 shadow-elevation-1 backdrop-blur-sm transition-colors hover:bg-bg-surface focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]", O ? "text-status-error-text" : "text-text-secondary"),
|
|
74
|
+
"aria-pressed": O,
|
|
75
|
+
"aria-label": O ? "Remove from shortlist" : "Save to shortlist",
|
|
76
|
+
children: /* @__PURE__ */ d(s, { className: e("h-4 w-4", O && "fill-current") })
|
|
49
77
|
}),
|
|
50
|
-
/* @__PURE__ */
|
|
78
|
+
/* @__PURE__ */ d("div", {
|
|
51
79
|
className: "absolute bottom-3 left-3",
|
|
52
|
-
children: /* @__PURE__ */
|
|
53
|
-
className: "
|
|
54
|
-
children:
|
|
80
|
+
children: /* @__PURE__ */ d("span", {
|
|
81
|
+
className: "text-lg font-bold text-text-inverse drop-shadow-sm",
|
|
82
|
+
children: P
|
|
83
|
+
})
|
|
84
|
+
}),
|
|
85
|
+
/* @__PURE__ */ d("div", {
|
|
86
|
+
className: "absolute bottom-3 right-3",
|
|
87
|
+
children: /* @__PURE__ */ d("span", {
|
|
88
|
+
className: "rounded-md bg-bg-surface/85 px-2 py-1 text-xs font-medium capitalize text-text-primary backdrop-blur-sm",
|
|
89
|
+
children: T
|
|
55
90
|
})
|
|
56
91
|
})
|
|
57
92
|
]
|
|
58
|
-
}), /* @__PURE__ */
|
|
59
|
-
className: "p-4",
|
|
93
|
+
}), /* @__PURE__ */ f("div", {
|
|
94
|
+
className: "flex flex-1 flex-col p-4",
|
|
60
95
|
children: [
|
|
61
|
-
/* @__PURE__ */
|
|
62
|
-
className: "
|
|
63
|
-
children:
|
|
64
|
-
className: "text-lg font-bold text-text-primary",
|
|
65
|
-
children: D
|
|
66
|
-
}), m === "sale" && /* @__PURE__ */ l("span", {
|
|
67
|
-
className: "text-xs font-medium text-text-secondary",
|
|
68
|
-
children: "For Sale"
|
|
69
|
-
})]
|
|
70
|
-
}),
|
|
71
|
-
/* @__PURE__ */ l("h3", {
|
|
72
|
-
className: "mt-1 truncate text-sm font-medium text-text-primary",
|
|
73
|
-
children: f
|
|
96
|
+
/* @__PURE__ */ d("h3", {
|
|
97
|
+
className: "truncate text-sm font-semibold text-text-primary",
|
|
98
|
+
children: c
|
|
74
99
|
}),
|
|
75
|
-
/* @__PURE__ */
|
|
76
|
-
className: "mt-1
|
|
77
|
-
children: [/* @__PURE__ */ l
|
|
100
|
+
/* @__PURE__ */ f("div", {
|
|
101
|
+
className: "mt-1 flex items-center gap-1 text-text-secondary",
|
|
102
|
+
children: [/* @__PURE__ */ d(l, { className: "h-3.5 w-3.5 shrink-0" }), /* @__PURE__ */ d("span", {
|
|
78
103
|
className: "truncate text-xs",
|
|
79
|
-
children:
|
|
104
|
+
children: y
|
|
80
105
|
})]
|
|
81
106
|
}),
|
|
82
|
-
/* @__PURE__ */
|
|
83
|
-
className: "mt-3 flex items-center gap-
|
|
107
|
+
/* @__PURE__ */ f("div", {
|
|
108
|
+
className: "mt-3 flex items-center gap-4 border-t border-border-subtle pt-3",
|
|
84
109
|
children: [
|
|
85
|
-
|
|
86
|
-
className: "flex items-center gap-1 text-xs text-text-secondary",
|
|
87
|
-
children: [/* @__PURE__ */
|
|
88
|
-
|
|
110
|
+
b != null && /* @__PURE__ */ f("div", {
|
|
111
|
+
className: "flex items-center gap-1.5 text-xs text-text-secondary",
|
|
112
|
+
children: [/* @__PURE__ */ d(o, { className: "h-4 w-4" }), /* @__PURE__ */ f("span", { children: [
|
|
113
|
+
b,
|
|
89
114
|
" ",
|
|
90
|
-
|
|
115
|
+
b === 1 ? "Bed" : "Beds"
|
|
91
116
|
] })]
|
|
92
117
|
}),
|
|
93
|
-
|
|
94
|
-
className: "flex items-center gap-1 text-xs text-text-secondary",
|
|
95
|
-
children: [/* @__PURE__ */
|
|
96
|
-
|
|
118
|
+
x != null && /* @__PURE__ */ f("div", {
|
|
119
|
+
className: "flex items-center gap-1.5 text-xs text-text-secondary",
|
|
120
|
+
children: [/* @__PURE__ */ d(a, { className: "h-4 w-4" }), /* @__PURE__ */ f("span", { children: [
|
|
121
|
+
x,
|
|
97
122
|
" ",
|
|
98
|
-
|
|
123
|
+
x === 1 ? "Bath" : "Baths"
|
|
99
124
|
] })]
|
|
100
125
|
}),
|
|
101
|
-
/* @__PURE__ */
|
|
102
|
-
className: "flex items-center gap-1 text-xs text-text-secondary",
|
|
103
|
-
children: [/* @__PURE__ */
|
|
126
|
+
/* @__PURE__ */ f("div", {
|
|
127
|
+
className: "flex items-center gap-1.5 text-xs text-text-secondary",
|
|
128
|
+
children: [/* @__PURE__ */ d(u, { className: "h-4 w-4" }), /* @__PURE__ */ f("span", { children: [n(S), " sq ft"] })]
|
|
104
129
|
})
|
|
105
130
|
]
|
|
106
131
|
})
|
|
@@ -109,6 +134,6 @@ var d = {
|
|
|
109
134
|
});
|
|
110
135
|
};
|
|
111
136
|
//#endregion
|
|
112
|
-
export {
|
|
137
|
+
export { h as PropertyCard };
|
|
113
138
|
|
|
114
139
|
//# sourceMappingURL=PropertyCard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PropertyCard.js","names":[],"sources":["../../../src/housingvista/components/PropertyCard.tsx"],"sourcesContent":["import { type FC } from 'react';\nimport { MapPin, Bed, Bath, Square, Heart } from 'lucide-react';\nimport { cn,
|
|
1
|
+
{"version":3,"file":"PropertyCard.js","names":[],"sources":["../../../src/housingvista/components/PropertyCard.tsx"],"sourcesContent":["import { type FC, useState } from 'react';\nimport { MapPin, Bed, Bath, Square, Heart, Home } from 'lucide-react';\nimport { cn, formatNumber, formatCompactCurrency } from '../utils/helpers';\nimport { StatusBadge } from './StatusBadge';\n\ninterface PropertyCardProps {\n id: string;\n title: string;\n price: number;\n priceType: 'sale' | 'rent';\n pricePeriod?: string;\n /** ISO currency code for the price (defaults to USD). */\n currency?: string;\n location: string;\n bedrooms?: number;\n bathrooms?: number;\n area: number;\n imageUrl?: string;\n status: string;\n type: string;\n onClick?: () => void;\n className?: string;\n /** When provided the heart becomes a controlled shortlist toggle. */\n isShortlisted?: boolean;\n onToggleShortlist?: () => void;\n}\n\nconst statusVariantMap: Record<string, 'success' | 'warning' | 'error' | 'info' | 'default' | 'muted'> = {\n available: 'success',\n active: 'success',\n listed: 'success',\n for_sale: 'success',\n 'for sale': 'success',\n for_rent: 'info',\n 'for rent': 'info',\n pending: 'warning',\n 'under contract': 'warning',\n 'under offer': 'warning',\n sold: 'error',\n rented: 'error',\n off_market: 'muted',\n 'off market': 'muted',\n draft: 'muted',\n};\n\n/** Tasteful branded fallback shown when a listing has no files-svc image. */\nconst PropertyPlaceholder: FC = () => (\n <div className=\"relative flex h-full w-full items-center justify-center overflow-hidden bg-gradient-to-br from-[var(--color-action-primary-bg)]/15 via-bg-sunken to-[var(--color-accent-blue)]/15\">\n <div\n className=\"absolute inset-0 opacity-[0.35]\"\n style={{\n backgroundImage:\n 'radial-gradient(circle at 1px 1px, var(--color-border-seam, rgba(120,120,120,.25)) 1px, transparent 0)',\n backgroundSize: '16px 16px',\n }}\n />\n <div className=\"relative flex flex-col items-center gap-2 text-text-secondary/70\">\n <Home className=\"h-9 w-9\" />\n <span className=\"text-[11px] font-medium tracking-wide\">HousingVista</span>\n </div>\n </div>\n);\n\nexport const PropertyCard: FC<PropertyCardProps> = ({\n title,\n price,\n priceType,\n pricePeriod,\n currency = 'USD',\n location,\n bedrooms,\n bathrooms,\n area,\n imageUrl,\n status,\n type,\n onClick,\n className,\n isShortlisted,\n onToggleShortlist,\n}) => {\n const [imgFailed, setImgFailed] = useState(false);\n const showImage = Boolean(imageUrl) && !imgFailed;\n\n const compactPrice = formatCompactCurrency(price, currency);\n const priceDisplay =\n priceType === 'rent' ? `${compactPrice}${pricePeriod ? `/${pricePeriod.replace('ly', '')}` : '/mo'}` : compactPrice;\n\n const badgeVariant = statusVariantMap[status.toLowerCase()] ?? 'default';\n\n return (\n <div\n className={cn(\n 'group flex flex-col overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-elevation-1 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-elevation-2 focus-within:border-border-strong',\n onClick && 'cursor-pointer',\n className\n )}\n onClick={onClick}\n onKeyDown={(e) => {\n if (onClick && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n onClick();\n }\n }}\n role={onClick ? 'button' : undefined}\n tabIndex={onClick ? 0 : undefined}\n aria-label={onClick ? `View ${title}` : undefined}\n >\n {/* Image Section */}\n <div className=\"relative aspect-[16/10] shrink-0 overflow-hidden\">\n {showImage ? (\n <img\n src={imageUrl}\n alt={title}\n loading=\"lazy\"\n onError={() => setImgFailed(true)}\n className=\"h-full w-full object-cover transition-[filter] duration-300 group-hover:brightness-[1.02]\"\n />\n ) : (\n <PropertyPlaceholder />\n )}\n\n {/* Gradient scrim so overlaid text stays legible over any photo */}\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-24 bg-gradient-to-t from-bg-overlay/75 to-transparent\" />\n\n {/* Status pill (top-left) */}\n <div className=\"absolute left-3 top-3\">\n <StatusBadge label={status} variant={badgeVariant} dot />\n </div>\n\n {/* Shortlist heart (top-right) */}\n {onToggleShortlist && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleShortlist();\n }}\n className={cn(\n 'absolute right-3 top-3 rounded-full bg-bg-surface/85 p-2 shadow-elevation-1 backdrop-blur-sm transition-colors hover:bg-bg-surface focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]',\n isShortlisted ? 'text-status-error-text' : 'text-text-secondary'\n )}\n aria-pressed={isShortlisted}\n aria-label={isShortlisted ? 'Remove from shortlist' : 'Save to shortlist'}\n >\n <Heart className={cn('h-4 w-4', isShortlisted && 'fill-current')} />\n </button>\n )}\n\n {/* Price overlay (bottom-left) */}\n <div className=\"absolute bottom-3 left-3\">\n <span className=\"text-lg font-bold text-text-inverse drop-shadow-sm\">{priceDisplay}</span>\n </div>\n\n {/* Category chip (bottom-right) */}\n <div className=\"absolute bottom-3 right-3\">\n <span className=\"rounded-md bg-bg-surface/85 px-2 py-1 text-xs font-medium capitalize text-text-primary backdrop-blur-sm\">\n {type}\n </span>\n </div>\n </div>\n\n {/* Content Section */}\n <div className=\"flex flex-1 flex-col p-4\">\n {/* Title */}\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">{title}</h3>\n\n {/* Location */}\n <div className=\"mt-1 flex items-center gap-1 text-text-secondary\">\n <MapPin className=\"h-3.5 w-3.5 shrink-0\" />\n <span className=\"truncate text-xs\">{location}</span>\n </div>\n\n {/* Specs Row */}\n <div className=\"mt-3 flex items-center gap-4 border-t border-border-subtle pt-3\">\n {bedrooms != null && (\n <div className=\"flex items-center gap-1.5 text-xs text-text-secondary\">\n <Bed className=\"h-4 w-4\" />\n <span>\n {bedrooms} {bedrooms === 1 ? 'Bed' : 'Beds'}\n </span>\n </div>\n )}\n {bathrooms != null && (\n <div className=\"flex items-center gap-1.5 text-xs text-text-secondary\">\n <Bath className=\"h-4 w-4\" />\n <span>\n {bathrooms} {bathrooms === 1 ? 'Bath' : 'Baths'}\n </span>\n </div>\n )}\n <div className=\"flex items-center gap-1.5 text-xs text-text-secondary\">\n <Square className=\"h-4 w-4\" />\n <span>{formatNumber(area)} sq ft</span>\n </div>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { gql as e } from "@apollo/client";
|
|
2
|
+
import { useQuery as t } from "@apollo/client/react";
|
|
3
|
+
//#region src/housingvista/hooks/messages.ts
|
|
4
|
+
var n = e`
|
|
5
|
+
query HvConversations($first: Int, $sort: ConversationSort) {
|
|
6
|
+
conversations(first: $first, sort: $sort) {
|
|
7
|
+
edges {
|
|
8
|
+
node {
|
|
9
|
+
id
|
|
10
|
+
title
|
|
11
|
+
avatar
|
|
12
|
+
unreadCount
|
|
13
|
+
updatedAt
|
|
14
|
+
settings
|
|
15
|
+
lastMessage {
|
|
16
|
+
id
|
|
17
|
+
authorId
|
|
18
|
+
content
|
|
19
|
+
createdAt
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
`, r = (e) => e && typeof e == "object" && !Array.isArray(e) ? e : null, i = (e) => typeof e == "string" && e.length > 0 ? e : null, a = (e) => {
|
|
26
|
+
let t = r(e?.metadata), n = r(t?.sourceContext), a = r(t?.channelLink), o = r(t?.jumpToContext), s = i(n?.product), c = i(a?.channelKey);
|
|
27
|
+
return {
|
|
28
|
+
isHousingVista: s === "housingvista" || c === "housingvista-widget",
|
|
29
|
+
contextHref: i(o?.href) ?? i(n?.contextHref),
|
|
30
|
+
entityType: i(n?.entityType)
|
|
31
|
+
};
|
|
32
|
+
}, o = (e = 50) => {
|
|
33
|
+
let { data: r, loading: i, error: o, refetch: s } = t(n, {
|
|
34
|
+
variables: {
|
|
35
|
+
first: e,
|
|
36
|
+
sort: {
|
|
37
|
+
field: "LAST_MESSAGE_AT",
|
|
38
|
+
direction: "DESC"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
fetchPolicy: "cache-and-network"
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
data: (r?.conversations.edges ?? []).map((e) => e.node).filter((e) => a(e.settings).isHousingVista).map((e) => {
|
|
45
|
+
let t = a(e.settings);
|
|
46
|
+
return {
|
|
47
|
+
id: e.id,
|
|
48
|
+
title: e.title ?? "Conversation",
|
|
49
|
+
avatar: e.avatar,
|
|
50
|
+
unreadCount: e.unreadCount,
|
|
51
|
+
updatedAt: e.updatedAt,
|
|
52
|
+
lastMessageText: e.lastMessage?.content?.text ?? "",
|
|
53
|
+
lastMessageAt: e.lastMessage?.createdAt ?? null,
|
|
54
|
+
contextHref: t.contextHref,
|
|
55
|
+
entityType: t.entityType
|
|
56
|
+
};
|
|
57
|
+
}),
|
|
58
|
+
loading: i,
|
|
59
|
+
error: o,
|
|
60
|
+
refetch: s
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
//#endregion
|
|
64
|
+
export { o as useHousingVistaConversations };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","names":[],"sources":["../../../src/housingvista/hooks/messages.ts"],"sourcesContent":["/**\n * HousingVista inbox — the conversation list behind the `/housingvista/chat`\n * (Messages) surface. Reads the current user's workspace conversations from\n * `wspace-conversations-svc` and narrows them to the threads THIS product\n * created: `resolveContextConversation` stamps every HousingVista thread with a\n * `sourceContext.product = \"housingvista\"` (and `channelLink.channelKey =\n * \"housingvista-widget\"`) inside the conversation `settings`, so we filter on\n * that. Thread reads + sends reuse the shared platform hooks\n * (`useConversationMessages` / `useSendMessage`) — no stub, no mock data.\n * @module housingvista\n */\n\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\n\nconst GET_HV_CONVERSATIONS = gql`\n query HvConversations($first: Int, $sort: ConversationSort) {\n conversations(first: $first, sort: $sort) {\n edges {\n node {\n id\n title\n avatar\n unreadCount\n updatedAt\n settings\n lastMessage {\n id\n authorId\n content\n createdAt\n }\n }\n }\n }\n }\n`;\n\n/** A HousingVista thread as shown in the inbox list. */\nexport interface HvConversationSummary {\n id: string;\n title: string;\n avatar: string | null;\n unreadCount: number;\n updatedAt: string;\n lastMessageText: string;\n lastMessageAt: string | null;\n /** Deep link back to the property/service the thread is about, if any. */\n contextHref: string | null;\n /** e.g. `housingvista-property`, `housingvista-marketplace-service`. */\n entityType: string | null;\n}\n\ninterface MessageContentJson {\n text?: string;\n}\n\ninterface ConversationNode {\n id: string;\n title: string | null;\n avatar: string | null;\n unreadCount: number;\n updatedAt: string;\n settings: Record<string, unknown> | null;\n lastMessage: {\n id: string;\n authorId: string;\n content: MessageContentJson | null;\n createdAt: string;\n } | null;\n}\n\ninterface HvConversationsResult {\n conversations: {\n edges: { node: ConversationNode }[];\n };\n}\n\n/** Safely read a nested object off an opaque JSON value. */\nconst asRecord = (value: unknown): Record<string, unknown> | null =>\n value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : null;\n\nconst asString = (value: unknown): string | null => (typeof value === 'string' && value.length > 0 ? value : null);\n\n/**\n * A conversation belongs to HousingVista when its source context (or channel\n * link) was stamped by this product. Reads the shape written by\n * `buildContextConversationSettings` in wspace-conversations-svc.\n */\nconst readHousingVistaContext = (\n settings: Record<string, unknown> | null\n): { isHousingVista: boolean; contextHref: string | null; entityType: string | null } => {\n const metadata = asRecord(settings?.metadata);\n const sourceContext = asRecord(metadata?.sourceContext);\n const channelLink = asRecord(metadata?.channelLink);\n const jumpToContext = asRecord(metadata?.jumpToContext);\n\n const product = asString(sourceContext?.product);\n const channelKey = asString(channelLink?.channelKey);\n const isHousingVista = product === 'housingvista' || channelKey === 'housingvista-widget';\n\n return {\n isHousingVista,\n contextHref: asString(jumpToContext?.href) ?? asString(sourceContext?.contextHref),\n entityType: asString(sourceContext?.entityType),\n };\n};\n\n/**\n * The user's HousingVista conversations, most-recent first. Filtered client-side\n * to product-owned threads (see {@link readHousingVistaContext}); everything\n * else is honestly empty rather than fabricated.\n */\nexport const useHousingVistaConversations = (first = 50) => {\n const { data, loading, error, refetch } = useQuery<HvConversationsResult>(GET_HV_CONVERSATIONS, {\n variables: { first, sort: { field: 'LAST_MESSAGE_AT', direction: 'DESC' } },\n fetchPolicy: 'cache-and-network',\n });\n\n const conversations: HvConversationSummary[] = (data?.conversations.edges ?? [])\n .map((edge) => edge.node)\n .filter((node) => readHousingVistaContext(node.settings).isHousingVista)\n .map((node) => {\n const context = readHousingVistaContext(node.settings);\n return {\n id: node.id,\n title: node.title ?? 'Conversation',\n avatar: node.avatar,\n unreadCount: node.unreadCount,\n updatedAt: node.updatedAt,\n lastMessageText: node.lastMessage?.content?.text ?? '',\n lastMessageAt: node.lastMessage?.createdAt ?? null,\n contextHref: context.contextHref,\n entityType: context.entityType,\n };\n });\n\n return { data: conversations, loading, error, refetch };\n};\n"],"mappings":";;;AAeA,IAAM,IAAuB,CAAG;;;;;;;;;;;;;;;;;;;;;GAgE1B,KAAY,MAChB,KAAS,OAAO,KAAU,YAAY,CAAC,MAAM,QAAQ,EAAM,GAAI,IAAoC,MAE/F,KAAY,MAAmC,OAAO,KAAU,YAAY,EAAM,SAAS,IAAI,IAAQ,MAOvG,KACJ,MACuF;CACvF,IAAM,IAAW,EAAS,GAAU,SAAS,EACvC,IAAgB,EAAS,GAAU,cAAc,EACjD,IAAc,EAAS,GAAU,YAAY,EAC7C,IAAgB,EAAS,GAAU,cAAc,EAEjD,IAAU,EAAS,GAAe,QAAQ,EAC1C,IAAa,EAAS,GAAa,WAAW;AAGpD,QAAO;EACL,gBAHqB,MAAY,kBAAkB,MAAe;EAIlE,aAAa,EAAS,GAAe,KAAK,IAAI,EAAS,GAAe,YAAY;EAClF,YAAY,EAAS,GAAe,WAAW;EAChD;GAQU,KAAgC,IAAQ,OAAO;CAC1D,IAAM,EAAE,SAAM,YAAS,UAAO,eAAY,EAAgC,GAAsB;EAC9F,WAAW;GAAE;GAAO,MAAM;IAAE,OAAO;IAAmB,WAAW;IAAQ;GAAE;EAC3E,aAAa;EACd,CAAC;AAoBF,QAAO;EAAE,OAlBuC,GAAM,cAAc,SAAS,EAAE,EAC5E,KAAK,MAAS,EAAK,KAAK,CACxB,QAAQ,MAAS,EAAwB,EAAK,SAAS,CAAC,eAAe,CACvE,KAAK,MAAS;GACb,IAAM,IAAU,EAAwB,EAAK,SAAS;AACtD,UAAO;IACL,IAAI,EAAK;IACT,OAAO,EAAK,SAAS;IACrB,QAAQ,EAAK;IACb,aAAa,EAAK;IAClB,WAAW,EAAK;IAChB,iBAAiB,EAAK,aAAa,SAAS,QAAQ;IACpD,eAAe,EAAK,aAAa,aAAa;IAC9C,aAAa,EAAQ;IACrB,YAAY,EAAQ;IACrB;IACD;EAE0B;EAAS;EAAO;EAAS"}
|