@burdenoff/microfe-housingvista 2026.930.1 → 2026.930.2
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/components/PropertyCard.d.ts +5 -1
- package/dist/housingvista/components/PropertyCard.d.ts.map +1 -1
- package/dist/housingvista/components/PropertyCard.js +77 -67
- package/dist/housingvista/components/PropertyCard.js.map +1 -1
- package/dist/housingvista/components/maps/PropertyMapView.d.ts.map +1 -1
- package/dist/housingvista/components/maps/PropertyMapView.js +72 -65
- package/dist/housingvista/components/maps/PropertyMapView.js.map +1 -1
- package/dist/housingvista/pages/properties/PropertiesPage.d.ts.map +1 -1
- package/dist/housingvista/pages/properties/PropertiesPage.js +355 -324
- package/dist/housingvista/pages/properties/PropertiesPage.js.map +1 -1
- package/dist/housingvista/pages/properties/PropertyDetailPage.d.ts.map +1 -1
- package/dist/housingvista/pages/properties/PropertyDetailPage.js +220 -220
- package/dist/housingvista/pages/properties/PropertyDetailPage.js.map +1 -1
- package/dist/housingvista/pages/shortlist/ShortlistPage.d.ts.map +1 -1
- package/dist/housingvista/pages/shortlist/ShortlistPage.js +1 -0
- package/dist/housingvista/pages/shortlist/ShortlistPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FC } from 'react';
|
|
2
|
-
import { ReraVerificationStatus } from '../types';
|
|
2
|
+
import { ReraVerificationStatus, VerificationStatus } from '../types';
|
|
3
3
|
|
|
4
4
|
interface PropertyCardProps {
|
|
5
5
|
id: string;
|
|
@@ -24,6 +24,10 @@ interface PropertyCardProps {
|
|
|
24
24
|
/** RERA (India) verification workflow status — badge shown for
|
|
25
25
|
* `verified`/`pending`, omitted for `not_applicable`/`rejected` (BOFF-4132). */
|
|
26
26
|
reraStatus?: ReraVerificationStatus;
|
|
27
|
+
/** Generic listing verification status — badge shown for `verified`,
|
|
28
|
+
* a muted marker for `rejected`; `pending` is the default state and
|
|
29
|
+
* stays silent (same rule as the detail page). BOFF-8088. */
|
|
30
|
+
verificationStatus?: VerificationStatus;
|
|
27
31
|
/** When provided, renders a compare-selection toggle (BOFF-3931). */
|
|
28
32
|
isCompareSelected?: boolean;
|
|
29
33
|
onToggleCompare?: () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PropertyCard.d.ts","sourceRoot":"","sources":["../../../src/housingvista/components/PropertyCard.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,EAAY,MAAM,OAAO,CAAC;AAI1C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"PropertyCard.d.ts","sourceRoot":"","sources":["../../../src/housingvista/components/PropertyCard.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,EAAY,MAAM,OAAO,CAAC;AAI1C,OAAO,KAAK,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE3E,UAAU,iBAAiB;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B;oFACgF;IAChF,UAAU,CAAC,EAAE,sBAAsB,CAAC;IACpC;;iEAE6D;IAC7D,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;CAC9B;AAsCD,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CAqM9C,CAAC"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
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
3
|
import { useState as i } from "react";
|
|
4
|
-
import {
|
|
5
|
-
import { jsx as
|
|
4
|
+
import { BadgeCheck as a, Bath as o, Bed as s, Check as c, GitCompare as l, Heart as u, Home as d, MapPin as f, ShieldCheck as p, Square as m } from "lucide-react";
|
|
5
|
+
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
6
6
|
//#region src/housingvista/components/PropertyCard.tsx
|
|
7
|
-
var
|
|
7
|
+
var _ = {
|
|
8
8
|
available: "success",
|
|
9
9
|
active: "success",
|
|
10
10
|
listed: "success",
|
|
@@ -20,137 +20,147 @@ var g = {
|
|
|
20
20
|
off_market: "muted",
|
|
21
21
|
"off market": "muted",
|
|
22
22
|
draft: "muted"
|
|
23
|
-
},
|
|
23
|
+
}, v = () => /* @__PURE__ */ g("div", {
|
|
24
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__ */
|
|
25
|
+
children: [/* @__PURE__ */ h("div", {
|
|
26
26
|
className: "absolute inset-0 opacity-[0.35]",
|
|
27
27
|
style: {
|
|
28
28
|
backgroundImage: "radial-gradient(circle at 1px 1px, var(--color-border-seam, rgba(120,120,120,.25)) 1px, transparent 0)",
|
|
29
29
|
backgroundSize: "16px 16px"
|
|
30
30
|
}
|
|
31
|
-
}), /* @__PURE__ */
|
|
31
|
+
}), /* @__PURE__ */ g("div", {
|
|
32
32
|
className: "relative flex flex-col items-center gap-2 text-text-secondary/70",
|
|
33
|
-
children: [/* @__PURE__ */
|
|
33
|
+
children: [/* @__PURE__ */ h(d, { className: "h-9 w-9" }), /* @__PURE__ */ h("span", {
|
|
34
34
|
className: "text-[11px] font-medium tracking-wide",
|
|
35
35
|
children: "HousingVista"
|
|
36
36
|
})]
|
|
37
37
|
})]
|
|
38
|
-
}),
|
|
39
|
-
let [
|
|
40
|
-
return /* @__PURE__ */
|
|
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",
|
|
42
|
-
onClick:
|
|
38
|
+
}), y = ({ title: d, price: y, priceType: b, pricePeriod: x, currency: S = "USD", location: C, bedrooms: w, bathrooms: T, area: E, imageUrl: D, status: O, type: k, onClick: A, className: j, isShortlisted: M, onToggleShortlist: N, reraStatus: P, verificationStatus: F, isCompareSelected: I, onToggleCompare: L }) => {
|
|
39
|
+
let [R, z] = i(!1), B = !!D && !R, V = t(y, S), H = b === "rent" ? `${V}${x ? `/${x.replace("ly", "")}` : "/mo"}` : V, U = _[O.toLowerCase()] ?? "default";
|
|
40
|
+
return /* @__PURE__ */ g("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", A && "cursor-pointer", j),
|
|
42
|
+
onClick: A,
|
|
43
43
|
onKeyDown: (e) => {
|
|
44
|
-
|
|
44
|
+
A && (e.key === "Enter" || e.key === " ") && (e.preventDefault(), A());
|
|
45
45
|
},
|
|
46
|
-
role:
|
|
47
|
-
tabIndex:
|
|
48
|
-
"aria-label":
|
|
49
|
-
children: [/* @__PURE__ */
|
|
46
|
+
role: A ? "button" : void 0,
|
|
47
|
+
tabIndex: A ? 0 : void 0,
|
|
48
|
+
"aria-label": A ? `View ${d}` : void 0,
|
|
49
|
+
children: [/* @__PURE__ */ g("div", {
|
|
50
50
|
className: "relative aspect-[16/10] shrink-0 overflow-hidden",
|
|
51
51
|
children: [
|
|
52
|
-
|
|
53
|
-
src:
|
|
54
|
-
alt:
|
|
52
|
+
B ? /* @__PURE__ */ h("img", {
|
|
53
|
+
src: D,
|
|
54
|
+
alt: d,
|
|
55
55
|
loading: "lazy",
|
|
56
|
-
onError: () =>
|
|
56
|
+
onError: () => z(!0),
|
|
57
57
|
className: "h-full w-full object-cover transition-[filter] duration-300 group-hover:brightness-[1.02]"
|
|
58
|
-
}) : /* @__PURE__ */
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
-
/* @__PURE__ */
|
|
58
|
+
}) : /* @__PURE__ */ h(v, {}),
|
|
59
|
+
/* @__PURE__ */ h("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__ */ h("div", {
|
|
61
61
|
className: "absolute left-3 top-3",
|
|
62
|
-
children: /* @__PURE__ */
|
|
63
|
-
label:
|
|
64
|
-
variant:
|
|
62
|
+
children: /* @__PURE__ */ h(r, {
|
|
63
|
+
label: O,
|
|
64
|
+
variant: U,
|
|
65
65
|
dot: !0
|
|
66
66
|
})
|
|
67
67
|
}),
|
|
68
|
-
|
|
68
|
+
N && /* @__PURE__ */ h("button", {
|
|
69
69
|
type: "button",
|
|
70
70
|
onClick: (e) => {
|
|
71
|
-
e.stopPropagation(),
|
|
71
|
+
e.stopPropagation(), N();
|
|
72
72
|
},
|
|
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)]",
|
|
74
|
-
"aria-pressed":
|
|
75
|
-
"aria-label":
|
|
76
|
-
children: /* @__PURE__ */
|
|
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)]", M ? "text-status-error-text" : "text-text-secondary"),
|
|
74
|
+
"aria-pressed": M,
|
|
75
|
+
"aria-label": M ? "Remove from shortlist" : "Save to shortlist",
|
|
76
|
+
children: /* @__PURE__ */ h(u, { className: e("h-4 w-4", M && "fill-current") })
|
|
77
77
|
}),
|
|
78
|
-
|
|
78
|
+
L && /* @__PURE__ */ h("button", {
|
|
79
79
|
type: "button",
|
|
80
80
|
onClick: (e) => {
|
|
81
|
-
e.stopPropagation(),
|
|
81
|
+
e.stopPropagation(), L();
|
|
82
82
|
},
|
|
83
|
-
className: e("absolute top-14 right-3 rounded-full p-2 shadow-elevation-1 backdrop-blur-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
|
|
84
|
-
"aria-pressed":
|
|
85
|
-
"aria-label":
|
|
86
|
-
children:
|
|
83
|
+
className: e("absolute top-14 right-3 rounded-full p-2 shadow-elevation-1 backdrop-blur-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]", I ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-surface/85 text-text-secondary hover:bg-bg-surface"),
|
|
84
|
+
"aria-pressed": I,
|
|
85
|
+
"aria-label": I ? "Remove from comparison" : "Add to comparison",
|
|
86
|
+
children: h(I ? c : l, { className: "h-4 w-4" })
|
|
87
87
|
}),
|
|
88
|
-
/* @__PURE__ */
|
|
88
|
+
/* @__PURE__ */ h("div", {
|
|
89
89
|
className: "absolute bottom-3 left-3",
|
|
90
|
-
children: /* @__PURE__ */
|
|
90
|
+
children: /* @__PURE__ */ h("span", {
|
|
91
91
|
className: "text-lg font-bold text-text-inverse drop-shadow-sm",
|
|
92
|
-
children:
|
|
92
|
+
children: H
|
|
93
93
|
})
|
|
94
94
|
}),
|
|
95
|
-
/* @__PURE__ */
|
|
95
|
+
/* @__PURE__ */ h("div", {
|
|
96
96
|
className: "absolute bottom-3 right-3",
|
|
97
|
-
children: /* @__PURE__ */
|
|
97
|
+
children: /* @__PURE__ */ h("span", {
|
|
98
98
|
className: "rounded-md bg-bg-surface/85 px-2 py-1 text-xs font-medium capitalize text-text-primary backdrop-blur-sm",
|
|
99
|
-
children:
|
|
99
|
+
children: k
|
|
100
100
|
})
|
|
101
101
|
})
|
|
102
102
|
]
|
|
103
|
-
}), /* @__PURE__ */
|
|
103
|
+
}), /* @__PURE__ */ g("div", {
|
|
104
104
|
className: "flex flex-1 flex-col p-4",
|
|
105
105
|
children: [
|
|
106
|
-
/* @__PURE__ */
|
|
106
|
+
/* @__PURE__ */ g("div", {
|
|
107
107
|
className: "flex items-center gap-1.5",
|
|
108
108
|
children: [
|
|
109
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ h("h3", {
|
|
110
110
|
className: "truncate text-sm font-semibold text-text-primary",
|
|
111
|
-
children:
|
|
111
|
+
children: d
|
|
112
112
|
}),
|
|
113
|
-
|
|
113
|
+
P === "verified" && /* @__PURE__ */ g("span", {
|
|
114
114
|
className: "inline-flex shrink-0 items-center gap-1 rounded-full bg-status-success-bg-subtle px-1.5 py-0.5 text-[10px] font-semibold text-status-success-text",
|
|
115
115
|
title: "RERA verified",
|
|
116
|
-
children: [/* @__PURE__ */
|
|
116
|
+
children: [/* @__PURE__ */ h(p, { className: "h-3 w-3" }), "RERA"]
|
|
117
117
|
}),
|
|
118
|
-
|
|
118
|
+
P === "pending" && /* @__PURE__ */ h("span", {
|
|
119
119
|
className: "inline-flex shrink-0 items-center gap-1 rounded-full bg-status-warning-bg-subtle px-1.5 py-0.5 text-[10px] font-semibold text-status-warning-text",
|
|
120
120
|
title: "RERA verification pending",
|
|
121
121
|
children: "RERA pending"
|
|
122
|
+
}),
|
|
123
|
+
F === "verified" && /* @__PURE__ */ g("span", {
|
|
124
|
+
className: "inline-flex shrink-0 items-center gap-1 rounded-full bg-status-success-bg-subtle px-1.5 py-0.5 text-[10px] font-semibold text-status-success-text",
|
|
125
|
+
title: "Listing verified",
|
|
126
|
+
children: [/* @__PURE__ */ h(a, { className: "h-3 w-3" }), "Verified"]
|
|
127
|
+
}),
|
|
128
|
+
F === "rejected" && /* @__PURE__ */ h("span", {
|
|
129
|
+
className: "inline-flex shrink-0 items-center gap-1 rounded-full bg-status-error-bg-subtle px-1.5 py-0.5 text-[10px] font-semibold text-status-error-text",
|
|
130
|
+
title: "Listing verification rejected",
|
|
131
|
+
children: "Verification rejected"
|
|
122
132
|
})
|
|
123
133
|
]
|
|
124
134
|
}),
|
|
125
|
-
/* @__PURE__ */
|
|
135
|
+
/* @__PURE__ */ g("div", {
|
|
126
136
|
className: "mt-1 flex items-center gap-1 text-text-secondary",
|
|
127
|
-
children: [/* @__PURE__ */
|
|
137
|
+
children: [/* @__PURE__ */ h(f, { className: "h-3.5 w-3.5 shrink-0" }), /* @__PURE__ */ h("span", {
|
|
128
138
|
className: "truncate text-xs",
|
|
129
|
-
children:
|
|
139
|
+
children: C
|
|
130
140
|
})]
|
|
131
141
|
}),
|
|
132
|
-
/* @__PURE__ */
|
|
142
|
+
/* @__PURE__ */ g("div", {
|
|
133
143
|
className: "mt-3 flex items-center gap-4 border-t border-border-subtle pt-3",
|
|
134
144
|
children: [
|
|
135
|
-
|
|
145
|
+
w != null && /* @__PURE__ */ g("div", {
|
|
136
146
|
className: "flex items-center gap-1.5 text-xs text-text-secondary",
|
|
137
|
-
children: [/* @__PURE__ */
|
|
138
|
-
|
|
147
|
+
children: [/* @__PURE__ */ h(s, { className: "h-4 w-4" }), /* @__PURE__ */ g("span", { children: [
|
|
148
|
+
w,
|
|
139
149
|
" ",
|
|
140
|
-
|
|
150
|
+
w === 1 ? "Bed" : "Beds"
|
|
141
151
|
] })]
|
|
142
152
|
}),
|
|
143
|
-
|
|
153
|
+
T != null && /* @__PURE__ */ g("div", {
|
|
144
154
|
className: "flex items-center gap-1.5 text-xs text-text-secondary",
|
|
145
|
-
children: [/* @__PURE__ */
|
|
146
|
-
|
|
155
|
+
children: [/* @__PURE__ */ h(o, { className: "h-4 w-4" }), /* @__PURE__ */ g("span", { children: [
|
|
156
|
+
T,
|
|
147
157
|
" ",
|
|
148
|
-
|
|
158
|
+
T === 1 ? "Bath" : "Baths"
|
|
149
159
|
] })]
|
|
150
160
|
}),
|
|
151
|
-
/* @__PURE__ */
|
|
161
|
+
/* @__PURE__ */ g("div", {
|
|
152
162
|
className: "flex items-center gap-1.5 text-xs text-text-secondary",
|
|
153
|
-
children: [/* @__PURE__ */ m
|
|
163
|
+
children: [/* @__PURE__ */ h(m, { className: "h-4 w-4" }), /* @__PURE__ */ g("span", { children: [n(E), " sq ft"] })]
|
|
154
164
|
})
|
|
155
165
|
]
|
|
156
166
|
})
|
|
@@ -159,6 +169,6 @@ var g = {
|
|
|
159
169
|
});
|
|
160
170
|
};
|
|
161
171
|
//#endregion
|
|
162
|
-
export {
|
|
172
|
+
export { y as PropertyCard };
|
|
163
173
|
|
|
164
174
|
//# sourceMappingURL=PropertyCard.js.map
|
|
@@ -1 +1 @@
|
|
|
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, ShieldCheck, GitCompare, Check } from 'lucide-react';\nimport { cn, formatNumber, formatCompactCurrency } from '../utils/helpers';\nimport { StatusBadge } from './StatusBadge';\nimport type { ReraVerificationStatus } from '../types';\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 /** RERA (India) verification workflow status — badge shown for\n * `verified`/`pending`, omitted for `not_applicable`/`rejected` (BOFF-4132). */\n reraStatus?: ReraVerificationStatus;\n /** When provided, renders a compare-selection toggle (BOFF-3931). */\n isCompareSelected?: boolean;\n onToggleCompare?: () => 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 reraStatus,\n isCompareSelected,\n onToggleCompare,\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 {/* Compare-selection toggle (bottom of the top-right stack, BOFF-3931) */}\n {onToggleCompare && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleCompare();\n }}\n className={cn(\n 'absolute top-14 right-3 rounded-full p-2 shadow-elevation-1 backdrop-blur-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]',\n isCompareSelected\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-surface/85 text-text-secondary hover:bg-bg-surface'\n )}\n aria-pressed={isCompareSelected}\n aria-label={isCompareSelected ? 'Remove from comparison' : 'Add to comparison'}\n >\n {isCompareSelected ? <Check className=\"h-4 w-4\" /> : <GitCompare className=\"h-4 w-4\" />}\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 <div className=\"flex items-center gap-1.5\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">{title}</h3>\n {reraStatus === 'verified' && (\n <span\n className=\"inline-flex shrink-0 items-center gap-1 rounded-full bg-status-success-bg-subtle px-1.5 py-0.5 text-[10px] font-semibold text-status-success-text\"\n title=\"RERA verified\"\n >\n <ShieldCheck className=\"h-3 w-3\" />\n RERA\n </span>\n )}\n {reraStatus === 'pending' && (\n <span\n className=\"inline-flex shrink-0 items-center gap-1 rounded-full bg-status-warning-bg-subtle px-1.5 py-0.5 text-[10px] font-semibold text-status-warning-text\"\n title=\"RERA verification pending\"\n >\n RERA pending\n </span>\n )}\n </div>\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":";;;;;;AAkCA,IAAM,IAAmG;CACvG,WAAW;CACX,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,YAAY;CACZ,UAAU;CACV,YAAY;CACZ,SAAS;CACT,kBAAkB;CAClB,eAAe;CACf,MAAM;CACN,QAAQ;CACR,YAAY;CACZ,cAAc;CACd,OAAO;CACR,EAGK,UACJ,kBAAC,OAAD;CAAK,WAAU;WAAf,CACE,kBAAC,OAAD;EACE,WAAU;EACV,OAAO;GACL,iBACE;GACF,gBAAgB;GACjB;EACD,CAAA,EACF,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAC5B,kBAAC,QAAD;GAAM,WAAU;aAAwC;GAAmB,CAAA,CACvE;IACF;IAGK,KAAuC,EAClD,UACA,UACA,cACA,gBACA,cAAW,OACX,aACA,aACA,cACA,SACA,aACA,WACA,SACA,YACA,cACA,kBACA,sBACA,eACA,sBACA,yBACI;CACJ,IAAM,CAAC,GAAW,KAAgB,EAAS,GAAM,EAC3C,IAAY,EAAQ,KAAa,CAAC,GAElC,IAAe,EAAsB,GAAO,EAAS,EACrD,IACJ,MAAc,SAAS,GAAG,IAAe,IAAc,IAAI,EAAY,QAAQ,MAAM,GAAG,KAAK,UAAU,GAEnG,IAAe,EAAiB,EAAO,aAAa,KAAK;AAE/D,QACE,kBAAC,OAAD;EACE,WAAW,EACT,0OACA,KAAW,kBACX,EACD;EACQ;EACT,YAAY,MAAM;AAChB,GAAI,MAAY,EAAE,QAAQ,WAAW,EAAE,QAAQ,SAC7C,EAAE,gBAAgB,EAClB,GAAS;;EAGb,MAAM,IAAU,WAAW,KAAA;EAC3B,UAAU,IAAU,IAAI,KAAA;EACxB,cAAY,IAAU,QAAQ,MAAU,KAAA;YAf1C,CAkBE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,IACC,kBAAC,OAAD;KACE,KAAK;KACL,KAAK;KACL,SAAQ;KACR,eAAe,EAAa,GAAK;KACjC,WAAU;KACV,CAAA,GAEF,kBAAC,GAAD,EAAuB,CAAA;IAIzB,kBAAC,OAAD,EAAK,WAAU,2GAA4G,CAAA;IAG3H,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAa,OAAO;MAAQ,SAAS;MAAc,KAAA;MAAM,CAAA;KACrD,CAAA;IAGL,KACC,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,MAAM;AAEd,MADA,EAAE,iBAAiB,EACnB,GAAmB;;KAErB,WAAW,EACT,mOACA,IAAgB,2BAA2B,sBAC5C;KACD,gBAAc;KACd,cAAY,IAAgB,0BAA0B;eAEtD,kBAAC,GAAD,EAAO,WAAW,EAAG,WAAW,KAAiB,eAAe,EAAI,CAAA;KAC7D,CAAA;IAIV,KACC,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,MAAM;AAEd,MADA,EAAE,iBAAiB,EACnB,GAAiB;;KAEnB,WAAW,EACT,+LACA,IACI,kDACA,2DACL;KACD,gBAAc;KACd,cAAY,IAAoB,2BAA2B;eAEtC,EAApB,IAAqB,IAAgC,GAAjC,EAAO,WAAU,WAAY,CAAqC;KAChF,CAAA;IAIX,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,QAAD;MAAM,WAAU;gBAAsD;MAAoB,CAAA;KACtF,CAAA;IAGN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,QAAD;MAAM,WAAU;gBACb;MACI,CAAA;KACH,CAAA;IACF;MAGN,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBAAoD;OAAW,CAAA;MAC5E,MAAe,cACd,kBAAC,QAAD;OACE,WAAU;OACV,OAAM;iBAFR,CAIE,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA,EAAA,OAE9B;;MAER,MAAe,aACd,kBAAC,QAAD;OACE,WAAU;OACV,OAAM;iBACP;OAEM,CAAA;MAEL;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,wBAAyB,CAAA,EAC3C,kBAAC,QAAD;MAAM,WAAU;gBAAoB;MAAgB,CAAA,CAChD;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,KAAY,QACX,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA,EAC3B,kBAAC,QAAD,EAAA,UAAA;QACG;QAAS;QAAE,MAAa,IAAI,QAAQ;QAChC,EAAA,CAAA,CACH;;MAEP,KAAa,QACZ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAC5B,kBAAC,QAAD,EAAA,UAAA;QACG;QAAU;QAAE,MAAc,IAAI,SAAS;QACnC,EAAA,CAAA,CACH;;MAER,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAC9B,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAa,EAAK,EAAC,SAAa,EAAA,CAAA,CACnC;;MACF;;IACF;KACF"}
|
|
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, ShieldCheck, BadgeCheck, GitCompare, Check } from 'lucide-react';\nimport { cn, formatNumber, formatCompactCurrency } from '../utils/helpers';\nimport { StatusBadge } from './StatusBadge';\nimport type { ReraVerificationStatus, VerificationStatus } from '../types';\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 /** RERA (India) verification workflow status — badge shown for\n * `verified`/`pending`, omitted for `not_applicable`/`rejected` (BOFF-4132). */\n reraStatus?: ReraVerificationStatus;\n /** Generic listing verification status — badge shown for `verified`,\n * a muted marker for `rejected`; `pending` is the default state and\n * stays silent (same rule as the detail page). BOFF-8088. */\n verificationStatus?: VerificationStatus;\n /** When provided, renders a compare-selection toggle (BOFF-3931). */\n isCompareSelected?: boolean;\n onToggleCompare?: () => 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 reraStatus,\n verificationStatus,\n isCompareSelected,\n onToggleCompare,\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 {/* Compare-selection toggle (bottom of the top-right stack, BOFF-3931) */}\n {onToggleCompare && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleCompare();\n }}\n className={cn(\n 'absolute top-14 right-3 rounded-full p-2 shadow-elevation-1 backdrop-blur-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]',\n isCompareSelected\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-surface/85 text-text-secondary hover:bg-bg-surface'\n )}\n aria-pressed={isCompareSelected}\n aria-label={isCompareSelected ? 'Remove from comparison' : 'Add to comparison'}\n >\n {isCompareSelected ? <Check className=\"h-4 w-4\" /> : <GitCompare className=\"h-4 w-4\" />}\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 <div className=\"flex items-center gap-1.5\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">{title}</h3>\n {reraStatus === 'verified' && (\n <span\n className=\"inline-flex shrink-0 items-center gap-1 rounded-full bg-status-success-bg-subtle px-1.5 py-0.5 text-[10px] font-semibold text-status-success-text\"\n title=\"RERA verified\"\n >\n <ShieldCheck className=\"h-3 w-3\" />\n RERA\n </span>\n )}\n {reraStatus === 'pending' && (\n <span\n className=\"inline-flex shrink-0 items-center gap-1 rounded-full bg-status-warning-bg-subtle px-1.5 py-0.5 text-[10px] font-semibold text-status-warning-text\"\n title=\"RERA verification pending\"\n >\n RERA pending\n </span>\n )}\n {verificationStatus === 'verified' && (\n <span\n className=\"inline-flex shrink-0 items-center gap-1 rounded-full bg-status-success-bg-subtle px-1.5 py-0.5 text-[10px] font-semibold text-status-success-text\"\n title=\"Listing verified\"\n >\n <BadgeCheck className=\"h-3 w-3\" />\n Verified\n </span>\n )}\n {verificationStatus === 'rejected' && (\n <span\n className=\"inline-flex shrink-0 items-center gap-1 rounded-full bg-status-error-bg-subtle px-1.5 py-0.5 text-[10px] font-semibold text-status-error-text\"\n title=\"Listing verification rejected\"\n >\n Verification rejected\n </span>\n )}\n </div>\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":";;;;;;AAsCA,IAAM,IAAmG;CACvG,WAAW;CACX,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,YAAY;CACZ,UAAU;CACV,YAAY;CACZ,SAAS;CACT,kBAAkB;CAClB,eAAe;CACf,MAAM;CACN,QAAQ;CACR,YAAY;CACZ,cAAc;CACd,OAAO;CACR,EAGK,UACJ,kBAAC,OAAD;CAAK,WAAU;WAAf,CACE,kBAAC,OAAD;EACE,WAAU;EACV,OAAO;GACL,iBACE;GACF,gBAAgB;GACjB;EACD,CAAA,EACF,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAC5B,kBAAC,QAAD;GAAM,WAAU;aAAwC;GAAmB,CAAA,CACvE;IACF;IAGK,KAAuC,EAClD,UACA,UACA,cACA,gBACA,cAAW,OACX,aACA,aACA,cACA,SACA,aACA,WACA,SACA,YACA,cACA,kBACA,sBACA,eACA,uBACA,sBACA,yBACI;CACJ,IAAM,CAAC,GAAW,KAAgB,EAAS,GAAM,EAC3C,IAAY,EAAQ,KAAa,CAAC,GAElC,IAAe,EAAsB,GAAO,EAAS,EACrD,IACJ,MAAc,SAAS,GAAG,IAAe,IAAc,IAAI,EAAY,QAAQ,MAAM,GAAG,KAAK,UAAU,GAEnG,IAAe,EAAiB,EAAO,aAAa,KAAK;AAE/D,QACE,kBAAC,OAAD;EACE,WAAW,EACT,0OACA,KAAW,kBACX,EACD;EACQ;EACT,YAAY,MAAM;AAChB,GAAI,MAAY,EAAE,QAAQ,WAAW,EAAE,QAAQ,SAC7C,EAAE,gBAAgB,EAClB,GAAS;;EAGb,MAAM,IAAU,WAAW,KAAA;EAC3B,UAAU,IAAU,IAAI,KAAA;EACxB,cAAY,IAAU,QAAQ,MAAU,KAAA;YAf1C,CAkBE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,IACC,kBAAC,OAAD;KACE,KAAK;KACL,KAAK;KACL,SAAQ;KACR,eAAe,EAAa,GAAK;KACjC,WAAU;KACV,CAAA,GAEF,kBAAC,GAAD,EAAuB,CAAA;IAIzB,kBAAC,OAAD,EAAK,WAAU,2GAA4G,CAAA;IAG3H,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAa,OAAO;MAAQ,SAAS;MAAc,KAAA;MAAM,CAAA;KACrD,CAAA;IAGL,KACC,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,MAAM;AAEd,MADA,EAAE,iBAAiB,EACnB,GAAmB;;KAErB,WAAW,EACT,mOACA,IAAgB,2BAA2B,sBAC5C;KACD,gBAAc;KACd,cAAY,IAAgB,0BAA0B;eAEtD,kBAAC,GAAD,EAAO,WAAW,EAAG,WAAW,KAAiB,eAAe,EAAI,CAAA;KAC7D,CAAA;IAIV,KACC,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,MAAM;AAEd,MADA,EAAE,iBAAiB,EACnB,GAAiB;;KAEnB,WAAW,EACT,+LACA,IACI,kDACA,2DACL;KACD,gBAAc;KACd,cAAY,IAAoB,2BAA2B;eAEtC,EAApB,IAAqB,IAAgC,GAAjC,EAAO,WAAU,WAAY,CAAqC;KAChF,CAAA;IAIX,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,QAAD;MAAM,WAAU;gBAAsD;MAAoB,CAAA;KACtF,CAAA;IAGN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,QAAD;MAAM,WAAU;gBACb;MACI,CAAA;KACH,CAAA;IACF;MAGN,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBAAoD;OAAW,CAAA;MAC5E,MAAe,cACd,kBAAC,QAAD;OACE,WAAU;OACV,OAAM;iBAFR,CAIE,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA,EAAA,OAE9B;;MAER,MAAe,aACd,kBAAC,QAAD;OACE,WAAU;OACV,OAAM;iBACP;OAEM,CAAA;MAER,MAAuB,cACtB,kBAAC,QAAD;OACE,WAAU;OACV,OAAM;iBAFR,CAIE,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA,EAAA,WAE7B;;MAER,MAAuB,cACtB,kBAAC,QAAD;OACE,WAAU;OACV,OAAM;iBACP;OAEM,CAAA;MAEL;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,wBAAyB,CAAA,EAC3C,kBAAC,QAAD;MAAM,WAAU;gBAAoB;MAAgB,CAAA,CAChD;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,KAAY,QACX,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA,EAC3B,kBAAC,QAAD,EAAA,UAAA;QACG;QAAS;QAAE,MAAa,IAAI,QAAQ;QAChC,EAAA,CAAA,CACH;;MAEP,KAAa,QACZ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAC5B,kBAAC,QAAD,EAAA,UAAA;QACG;QAAU;QAAE,MAAc,IAAI,SAAS;QACnC,EAAA,CAAA,CACH;;MAER,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAC9B,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAa,EAAK,EAAC,SAAa,EAAA,CAAA,CACnC;;MACF;;IACF;KACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PropertyMapView.d.ts","sourceRoot":"","sources":["../../../../src/housingvista/components/maps/PropertyMapView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA6B5C,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2DAA2D;IAC3D,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC1D;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,8GAS7B,oBAAoB,
|
|
1
|
+
{"version":3,"file":"PropertyMapView.d.ts","sourceRoot":"","sources":["../../../../src/housingvista/components/maps/PropertyMapView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA6B5C,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2DAA2D;IAC3D,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC1D;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,8GAS7B,oBAAoB,gCAyLtB,CAAC"}
|
|
@@ -6,14 +6,14 @@ import { Popup as o } from "../../../node_modules/react-leaflet/lib/Popup.js";
|
|
|
6
6
|
import { TileLayer as s } from "../../../node_modules/react-leaflet/lib/TileLayer.js";
|
|
7
7
|
import { OSM_ATTRIBUTION as c, OSM_TILE_URL as l, boundsFromLeaflet as u, createPropertyPinIcon as d, isRealCoord as f } from "./leaflet-setup.js";
|
|
8
8
|
import { useEffect as p, useMemo as m, useRef as h, useState as g } from "react";
|
|
9
|
-
import {
|
|
10
|
-
import { Fragment as
|
|
9
|
+
import { BadgeCheck as _, Bath as v, Bed as y, RotateCcw as b, ShieldCheck as x, Square as S } from "lucide-react";
|
|
10
|
+
import { Fragment as C, jsx as w, jsxs as T } from "react/jsx-runtime";
|
|
11
11
|
//#region src/housingvista/components/maps/PropertyMapView.tsx
|
|
12
|
-
function
|
|
12
|
+
function E() {
|
|
13
13
|
let [e, t] = g(!1);
|
|
14
14
|
return p(() => t(!0), []), e;
|
|
15
15
|
}
|
|
16
|
-
function
|
|
16
|
+
function D({ onChange: e }) {
|
|
17
17
|
let t = r({
|
|
18
18
|
moveend: () => e(t.getBounds()),
|
|
19
19
|
zoomend: () => e(t.getBounds())
|
|
@@ -22,25 +22,25 @@ function E({ onChange: e }) {
|
|
|
22
22
|
e(t.getBounds());
|
|
23
23
|
}, [t, e]), null;
|
|
24
24
|
}
|
|
25
|
-
var
|
|
26
|
-
let
|
|
27
|
-
if (!
|
|
28
|
-
let e = u(
|
|
25
|
+
var O = ({ properties: r, loading: p = !1, fallbackCenter: O = [20.5937, 78.9629], height: k = 520, className: A, selectedIds: j, onSelectProperty: M, onSearchThisArea: N }) => {
|
|
26
|
+
let P = E(), [F, I] = g(null), [L, R] = g(null), z = h(null), B = m(() => r.filter((e) => f(e.location.coordinates.latitude, e.location.coordinates.longitude)), [r]), V = B.length ? [B[0].location.coordinates.latitude, B[0].location.coordinates.longitude] : O, H = m(() => {
|
|
27
|
+
if (!F) return !1;
|
|
28
|
+
let e = u(F), t = z.current;
|
|
29
29
|
if (!t) return !0;
|
|
30
30
|
let n = 5e-4;
|
|
31
31
|
return Math.abs(e.neLat - t.neLat) > n || Math.abs(e.neLng - t.neLng) > n || Math.abs(e.swLat - t.swLat) > n || Math.abs(e.swLng - t.swLng) > n;
|
|
32
|
-
}, [
|
|
33
|
-
return /* @__PURE__ */
|
|
32
|
+
}, [F]);
|
|
33
|
+
return /* @__PURE__ */ T("div", {
|
|
34
34
|
className: "grid grid-cols-1 gap-4 lg:grid-cols-[minmax(0,1.6fr)_minmax(0,1fr)]",
|
|
35
|
-
children: [/* @__PURE__ */
|
|
36
|
-
className: e("relative overflow-hidden rounded-card border border-border-seam bg-bg-sunken",
|
|
35
|
+
children: [/* @__PURE__ */ w("div", {
|
|
36
|
+
className: e("relative overflow-hidden rounded-card border border-border-seam bg-bg-sunken", A),
|
|
37
37
|
style: {
|
|
38
|
-
height:
|
|
38
|
+
height: k,
|
|
39
39
|
width: "100%"
|
|
40
40
|
},
|
|
41
|
-
children:
|
|
42
|
-
center:
|
|
43
|
-
zoom:
|
|
41
|
+
children: P ? /* @__PURE__ */ T(C, { children: [/* @__PURE__ */ T(i, {
|
|
42
|
+
center: V,
|
|
43
|
+
zoom: B.length ? 11 : 5,
|
|
44
44
|
scrollWheelZoom: !0,
|
|
45
45
|
style: {
|
|
46
46
|
height: "100%",
|
|
@@ -48,47 +48,54 @@ var D = ({ properties: r, loading: p = !1, fallbackCenter: D = [20.5937, 78.9629
|
|
|
48
48
|
},
|
|
49
49
|
className: "z-0",
|
|
50
50
|
children: [
|
|
51
|
-
/* @__PURE__ */
|
|
51
|
+
/* @__PURE__ */ w(s, {
|
|
52
52
|
url: l,
|
|
53
53
|
attribution: c
|
|
54
54
|
}),
|
|
55
|
-
/* @__PURE__ */
|
|
56
|
-
|
|
57
|
-
let n =
|
|
58
|
-
return /* @__PURE__ */
|
|
55
|
+
/* @__PURE__ */ w(D, { onChange: I }),
|
|
56
|
+
B.map((e) => {
|
|
57
|
+
let n = L === e.id || j?.has(e.id);
|
|
58
|
+
return /* @__PURE__ */ w(a, {
|
|
59
59
|
position: [e.location.coordinates.latitude, e.location.coordinates.longitude],
|
|
60
60
|
icon: d(t(e.price.amount, e.price.currency), {
|
|
61
61
|
selected: n,
|
|
62
62
|
rera: e.reraVerificationStatus === "verified"
|
|
63
63
|
}),
|
|
64
64
|
eventHandlers: {
|
|
65
|
-
click: () =>
|
|
66
|
-
mouseover: () =>
|
|
67
|
-
mouseout: () =>
|
|
65
|
+
click: () => M?.(e.id),
|
|
66
|
+
mouseover: () => R(e.id),
|
|
67
|
+
mouseout: () => R((t) => t === e.id ? null : t)
|
|
68
68
|
},
|
|
69
|
-
children: /* @__PURE__ */
|
|
69
|
+
children: /* @__PURE__ */ w(o, {
|
|
70
70
|
className: "hv-popup",
|
|
71
|
-
children: /* @__PURE__ */
|
|
71
|
+
children: /* @__PURE__ */ T("div", {
|
|
72
72
|
className: "p-3",
|
|
73
73
|
children: [
|
|
74
|
-
/* @__PURE__ */
|
|
74
|
+
/* @__PURE__ */ T("div", {
|
|
75
75
|
className: "flex items-center gap-1.5",
|
|
76
|
-
children: [
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
children: [
|
|
77
|
+
/* @__PURE__ */ w("p", {
|
|
78
|
+
className: "text-sm font-semibold text-text-primary",
|
|
79
|
+
children: e.title
|
|
80
|
+
}),
|
|
81
|
+
e.reraVerificationStatus === "verified" && /* @__PURE__ */ w(x, { className: "h-3.5 w-3.5 shrink-0 text-status-success-text" }),
|
|
82
|
+
e.verificationStatus === "verified" && /* @__PURE__ */ w(_, {
|
|
83
|
+
className: "h-3.5 w-3.5 shrink-0 text-status-success-text",
|
|
84
|
+
"aria-label": "Verified listing"
|
|
85
|
+
})
|
|
86
|
+
]
|
|
80
87
|
}),
|
|
81
|
-
/* @__PURE__ */
|
|
88
|
+
/* @__PURE__ */ w("p", {
|
|
82
89
|
className: "text-xs text-text-secondary",
|
|
83
90
|
children: [e.location.neighborhood, e.location.city].filter(Boolean).join(", ")
|
|
84
91
|
}),
|
|
85
|
-
/* @__PURE__ */
|
|
92
|
+
/* @__PURE__ */ T("p", {
|
|
86
93
|
className: "mt-1 text-sm font-bold text-text-primary",
|
|
87
94
|
children: [t(e.price.amount, e.price.currency), e.price.type === "rent" ? "/mo" : ""]
|
|
88
95
|
}),
|
|
89
|
-
/* @__PURE__ */
|
|
96
|
+
/* @__PURE__ */ w("button", {
|
|
90
97
|
type: "button",
|
|
91
|
-
onClick: () =>
|
|
98
|
+
onClick: () => M?.(e.id),
|
|
92
99
|
className: "mt-2 text-xs font-medium text-[var(--color-action-primary-bg)] hover:underline",
|
|
93
100
|
children: "View details"
|
|
94
101
|
})
|
|
@@ -98,62 +105,62 @@ var D = ({ properties: r, loading: p = !1, fallbackCenter: D = [20.5937, 78.9629
|
|
|
98
105
|
}, e.id);
|
|
99
106
|
})
|
|
100
107
|
]
|
|
101
|
-
}),
|
|
108
|
+
}), N && /* @__PURE__ */ w("div", {
|
|
102
109
|
className: "pointer-events-none absolute inset-x-0 top-3 z-10 flex justify-center",
|
|
103
|
-
children: /* @__PURE__ */
|
|
110
|
+
children: /* @__PURE__ */ T("button", {
|
|
104
111
|
type: "button",
|
|
105
112
|
onClick: () => {
|
|
106
|
-
if (!
|
|
107
|
-
let e = u(
|
|
108
|
-
|
|
113
|
+
if (!F || !N) return;
|
|
114
|
+
let e = u(F);
|
|
115
|
+
z.current = e, N(e);
|
|
109
116
|
},
|
|
110
|
-
disabled: !
|
|
111
|
-
className: e("pointer-events-auto inline-flex items-center gap-1.5 rounded-full border border-border-subtle bg-bg-surface px-3 py-1.5 text-xs font-medium shadow-elevation-2 transition-colors",
|
|
112
|
-
children: [/* @__PURE__ */
|
|
117
|
+
disabled: !H || p,
|
|
118
|
+
className: e("pointer-events-auto inline-flex items-center gap-1.5 rounded-full border border-border-subtle bg-bg-surface px-3 py-1.5 text-xs font-medium shadow-elevation-2 transition-colors", H && !p ? "text-text-primary hover:bg-bg-sunken" : "cursor-not-allowed text-text-secondary opacity-70"),
|
|
119
|
+
children: [/* @__PURE__ */ w(b, { className: "h-3 w-3" }), p ? "Searching…" : "Search this area"]
|
|
113
120
|
})
|
|
114
|
-
})] }) : /* @__PURE__ */
|
|
121
|
+
})] }) : /* @__PURE__ */ w("div", {
|
|
115
122
|
className: "flex h-full w-full items-center justify-center",
|
|
116
123
|
"aria-busy": "true",
|
|
117
|
-
children: /* @__PURE__ */
|
|
124
|
+
children: /* @__PURE__ */ w("div", { className: "h-full w-full motion-safe:animate-pulse bg-bg-sunken" })
|
|
118
125
|
})
|
|
119
|
-
}), /* @__PURE__ */
|
|
126
|
+
}), /* @__PURE__ */ T("div", {
|
|
120
127
|
className: "flex flex-col gap-2 overflow-y-auto rounded-card border border-border-seam bg-bg-surface p-2",
|
|
121
|
-
style: { height:
|
|
122
|
-
children: [
|
|
128
|
+
style: { height: k },
|
|
129
|
+
children: [B.length === 0 && !p && /* @__PURE__ */ w("p", {
|
|
123
130
|
className: "p-4 text-center text-sm text-text-secondary",
|
|
124
131
|
children: "No properties with a mapped location match the current filters."
|
|
125
|
-
}),
|
|
132
|
+
}), B.map((r) => /* @__PURE__ */ T("button", {
|
|
126
133
|
type: "button",
|
|
127
|
-
onMouseEnter: () =>
|
|
128
|
-
onMouseLeave: () =>
|
|
129
|
-
onClick: () =>
|
|
130
|
-
className: e("flex items-center gap-3 rounded-lg border p-2 text-left transition-colors",
|
|
131
|
-
children: [/* @__PURE__ */
|
|
134
|
+
onMouseEnter: () => R(r.id),
|
|
135
|
+
onMouseLeave: () => R((e) => e === r.id ? null : e),
|
|
136
|
+
onClick: () => M?.(r.id),
|
|
137
|
+
className: e("flex items-center gap-3 rounded-lg border p-2 text-left transition-colors", L === r.id ? "border-[var(--color-action-primary-bg)] bg-bg-sunken" : "border-transparent hover:bg-bg-sunken"),
|
|
138
|
+
children: [/* @__PURE__ */ T("div", {
|
|
132
139
|
className: "min-w-0 flex-1",
|
|
133
140
|
children: [
|
|
134
|
-
/* @__PURE__ */
|
|
141
|
+
/* @__PURE__ */ w("p", {
|
|
135
142
|
className: "truncate text-sm font-medium text-text-primary",
|
|
136
143
|
children: r.title
|
|
137
144
|
}),
|
|
138
|
-
/* @__PURE__ */
|
|
145
|
+
/* @__PURE__ */ w("p", {
|
|
139
146
|
className: "truncate text-xs text-text-secondary",
|
|
140
147
|
children: [r.location.neighborhood, r.location.city].filter(Boolean).join(", ")
|
|
141
148
|
}),
|
|
142
|
-
/* @__PURE__ */
|
|
149
|
+
/* @__PURE__ */ T("div", {
|
|
143
150
|
className: "mt-1 flex items-center gap-3 text-[11px] text-text-secondary",
|
|
144
151
|
children: [
|
|
145
|
-
r.specifications.bedrooms != null && /* @__PURE__ */
|
|
152
|
+
r.specifications.bedrooms != null && /* @__PURE__ */ T("span", {
|
|
146
153
|
className: "inline-flex items-center gap-1",
|
|
147
|
-
children: [/* @__PURE__ */
|
|
154
|
+
children: [/* @__PURE__ */ w(y, { className: "h-3 w-3" }), r.specifications.bedrooms]
|
|
148
155
|
}),
|
|
149
|
-
r.specifications.bathrooms != null && /* @__PURE__ */
|
|
156
|
+
r.specifications.bathrooms != null && /* @__PURE__ */ T("span", {
|
|
150
157
|
className: "inline-flex items-center gap-1",
|
|
151
|
-
children: [/* @__PURE__ */
|
|
158
|
+
children: [/* @__PURE__ */ w(v, { className: "h-3 w-3" }), r.specifications.bathrooms]
|
|
152
159
|
}),
|
|
153
|
-
/* @__PURE__ */
|
|
160
|
+
/* @__PURE__ */ T("span", {
|
|
154
161
|
className: "inline-flex items-center gap-1",
|
|
155
162
|
children: [
|
|
156
|
-
/* @__PURE__ */
|
|
163
|
+
/* @__PURE__ */ w(S, { className: "h-3 w-3" }),
|
|
157
164
|
n(r.specifications.totalArea),
|
|
158
165
|
" sq ft"
|
|
159
166
|
]
|
|
@@ -161,7 +168,7 @@ var D = ({ properties: r, loading: p = !1, fallbackCenter: D = [20.5937, 78.9629
|
|
|
161
168
|
]
|
|
162
169
|
})
|
|
163
170
|
]
|
|
164
|
-
}), /* @__PURE__ */
|
|
171
|
+
}), /* @__PURE__ */ w("span", {
|
|
165
172
|
className: "shrink-0 text-sm font-bold text-text-primary",
|
|
166
173
|
children: t(r.price.amount, r.price.currency)
|
|
167
174
|
})]
|
|
@@ -170,6 +177,6 @@ var D = ({ properties: r, loading: p = !1, fallbackCenter: D = [20.5937, 78.9629
|
|
|
170
177
|
});
|
|
171
178
|
};
|
|
172
179
|
//#endregion
|
|
173
|
-
export {
|
|
180
|
+
export { O as PropertyMapView };
|
|
174
181
|
|
|
175
182
|
//# sourceMappingURL=PropertyMapView.js.map
|