@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.
@@ -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;AAEvD,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,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;CAC9B;AAsCD,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CAmL9C,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 { Bath as a, Bed as o, Check as s, GitCompare as c, Heart as l, Home as u, MapPin as d, ShieldCheck as f, Square as p } from "lucide-react";
5
- import { jsx as m, jsxs as h } from "react/jsx-runtime";
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 g = {
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
- }, _ = () => /* @__PURE__ */ h("div", {
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__ */ m("div", {
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__ */ h("div", {
31
+ }), /* @__PURE__ */ g("div", {
32
32
  className: "relative flex flex-col items-center gap-2 text-text-secondary/70",
33
- children: [/* @__PURE__ */ m(u, { className: "h-9 w-9" }), /* @__PURE__ */ m("span", {
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
- }), v = ({ title: u, price: v, priceType: y, pricePeriod: b, currency: x = "USD", location: S, bedrooms: C, bathrooms: w, area: T, imageUrl: E, status: D, type: O, onClick: k, className: A, isShortlisted: j, onToggleShortlist: M, reraStatus: N, isCompareSelected: P, onToggleCompare: F }) => {
39
- let [I, L] = i(!1), R = !!E && !I, z = t(v, x), B = y === "rent" ? `${z}${b ? `/${b.replace("ly", "")}` : "/mo"}` : z, V = g[D.toLowerCase()] ?? "default";
40
- return /* @__PURE__ */ h("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", k && "cursor-pointer", A),
42
- onClick: k,
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
- k && (e.key === "Enter" || e.key === " ") && (e.preventDefault(), k());
44
+ A && (e.key === "Enter" || e.key === " ") && (e.preventDefault(), A());
45
45
  },
46
- role: k ? "button" : void 0,
47
- tabIndex: k ? 0 : void 0,
48
- "aria-label": k ? `View ${u}` : void 0,
49
- children: [/* @__PURE__ */ h("div", {
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
- R ? /* @__PURE__ */ m("img", {
53
- src: E,
54
- alt: u,
52
+ B ? /* @__PURE__ */ h("img", {
53
+ src: D,
54
+ alt: d,
55
55
  loading: "lazy",
56
- onError: () => L(!0),
56
+ onError: () => z(!0),
57
57
  className: "h-full w-full object-cover transition-[filter] duration-300 group-hover:brightness-[1.02]"
58
- }) : /* @__PURE__ */ m(_, {}),
59
- /* @__PURE__ */ m("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__ */ m("div", {
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__ */ m(r, {
63
- label: D,
64
- variant: V,
62
+ children: /* @__PURE__ */ h(r, {
63
+ label: O,
64
+ variant: U,
65
65
  dot: !0
66
66
  })
67
67
  }),
68
- M && /* @__PURE__ */ m("button", {
68
+ N && /* @__PURE__ */ h("button", {
69
69
  type: "button",
70
70
  onClick: (e) => {
71
- e.stopPropagation(), M();
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)]", j ? "text-status-error-text" : "text-text-secondary"),
74
- "aria-pressed": j,
75
- "aria-label": j ? "Remove from shortlist" : "Save to shortlist",
76
- children: /* @__PURE__ */ m(l, { className: e("h-4 w-4", j && "fill-current") })
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
- F && /* @__PURE__ */ m("button", {
78
+ L && /* @__PURE__ */ h("button", {
79
79
  type: "button",
80
80
  onClick: (e) => {
81
- e.stopPropagation(), F();
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)]", P ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-surface/85 text-text-secondary hover:bg-bg-surface"),
84
- "aria-pressed": P,
85
- "aria-label": P ? "Remove from comparison" : "Add to comparison",
86
- children: m(P ? s : c, { className: "h-4 w-4" })
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__ */ m("div", {
88
+ /* @__PURE__ */ h("div", {
89
89
  className: "absolute bottom-3 left-3",
90
- children: /* @__PURE__ */ m("span", {
90
+ children: /* @__PURE__ */ h("span", {
91
91
  className: "text-lg font-bold text-text-inverse drop-shadow-sm",
92
- children: B
92
+ children: H
93
93
  })
94
94
  }),
95
- /* @__PURE__ */ m("div", {
95
+ /* @__PURE__ */ h("div", {
96
96
  className: "absolute bottom-3 right-3",
97
- children: /* @__PURE__ */ m("span", {
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: O
99
+ children: k
100
100
  })
101
101
  })
102
102
  ]
103
- }), /* @__PURE__ */ h("div", {
103
+ }), /* @__PURE__ */ g("div", {
104
104
  className: "flex flex-1 flex-col p-4",
105
105
  children: [
106
- /* @__PURE__ */ h("div", {
106
+ /* @__PURE__ */ g("div", {
107
107
  className: "flex items-center gap-1.5",
108
108
  children: [
109
- /* @__PURE__ */ m("h3", {
109
+ /* @__PURE__ */ h("h3", {
110
110
  className: "truncate text-sm font-semibold text-text-primary",
111
- children: u
111
+ children: d
112
112
  }),
113
- N === "verified" && /* @__PURE__ */ h("span", {
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__ */ m(f, { className: "h-3 w-3" }), "RERA"]
116
+ children: [/* @__PURE__ */ h(p, { className: "h-3 w-3" }), "RERA"]
117
117
  }),
118
- N === "pending" && /* @__PURE__ */ m("span", {
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__ */ h("div", {
135
+ /* @__PURE__ */ g("div", {
126
136
  className: "mt-1 flex items-center gap-1 text-text-secondary",
127
- children: [/* @__PURE__ */ m(d, { className: "h-3.5 w-3.5 shrink-0" }), /* @__PURE__ */ m("span", {
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: S
139
+ children: C
130
140
  })]
131
141
  }),
132
- /* @__PURE__ */ h("div", {
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
- C != null && /* @__PURE__ */ h("div", {
145
+ w != null && /* @__PURE__ */ g("div", {
136
146
  className: "flex items-center gap-1.5 text-xs text-text-secondary",
137
- children: [/* @__PURE__ */ m(o, { className: "h-4 w-4" }), /* @__PURE__ */ h("span", { children: [
138
- C,
147
+ children: [/* @__PURE__ */ h(s, { className: "h-4 w-4" }), /* @__PURE__ */ g("span", { children: [
148
+ w,
139
149
  " ",
140
- C === 1 ? "Bed" : "Beds"
150
+ w === 1 ? "Bed" : "Beds"
141
151
  ] })]
142
152
  }),
143
- w != null && /* @__PURE__ */ h("div", {
153
+ T != null && /* @__PURE__ */ g("div", {
144
154
  className: "flex items-center gap-1.5 text-xs text-text-secondary",
145
- children: [/* @__PURE__ */ m(a, { className: "h-4 w-4" }), /* @__PURE__ */ h("span", { children: [
146
- w,
155
+ children: [/* @__PURE__ */ h(o, { className: "h-4 w-4" }), /* @__PURE__ */ g("span", { children: [
156
+ T,
147
157
  " ",
148
- w === 1 ? "Bath" : "Baths"
158
+ T === 1 ? "Bath" : "Baths"
149
159
  ] })]
150
160
  }),
151
- /* @__PURE__ */ h("div", {
161
+ /* @__PURE__ */ g("div", {
152
162
  className: "flex items-center gap-1.5 text-xs text-text-secondary",
153
- children: [/* @__PURE__ */ m(p, { className: "h-4 w-4" }), /* @__PURE__ */ h("span", { children: [n(T), " sq ft"] })]
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 { v as PropertyCard };
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":"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"}
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,gCAmLtB,CAAC"}
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 { Bath as _, Bed as v, RotateCcw as y, ShieldCheck as b, Square as x } from "lucide-react";
10
- import { Fragment as S, jsx as C, jsxs as w } from "react/jsx-runtime";
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 T() {
12
+ function E() {
13
13
  let [e, t] = g(!1);
14
14
  return p(() => t(!0), []), e;
15
15
  }
16
- function E({ onChange: e }) {
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 D = ({ properties: r, loading: p = !1, fallbackCenter: D = [20.5937, 78.9629], height: O = 520, className: k, selectedIds: A, onSelectProperty: j, onSearchThisArea: M }) => {
26
- let N = T(), [P, F] = g(null), [I, L] = g(null), R = h(null), z = m(() => r.filter((e) => f(e.location.coordinates.latitude, e.location.coordinates.longitude)), [r]), B = z.length ? [z[0].location.coordinates.latitude, z[0].location.coordinates.longitude] : D, V = m(() => {
27
- if (!P) return !1;
28
- let e = u(P), t = R.current;
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
- }, [P]);
33
- return /* @__PURE__ */ w("div", {
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__ */ C("div", {
36
- className: e("relative overflow-hidden rounded-card border border-border-seam bg-bg-sunken", k),
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: O,
38
+ height: k,
39
39
  width: "100%"
40
40
  },
41
- children: N ? /* @__PURE__ */ w(S, { children: [/* @__PURE__ */ w(i, {
42
- center: B,
43
- zoom: z.length ? 11 : 5,
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__ */ C(s, {
51
+ /* @__PURE__ */ w(s, {
52
52
  url: l,
53
53
  attribution: c
54
54
  }),
55
- /* @__PURE__ */ C(E, { onChange: F }),
56
- z.map((e) => {
57
- let n = I === e.id || A?.has(e.id);
58
- return /* @__PURE__ */ C(a, {
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: () => j?.(e.id),
66
- mouseover: () => L(e.id),
67
- mouseout: () => L((t) => t === e.id ? null : t)
65
+ click: () => M?.(e.id),
66
+ mouseover: () => R(e.id),
67
+ mouseout: () => R((t) => t === e.id ? null : t)
68
68
  },
69
- children: /* @__PURE__ */ C(o, {
69
+ children: /* @__PURE__ */ w(o, {
70
70
  className: "hv-popup",
71
- children: /* @__PURE__ */ w("div", {
71
+ children: /* @__PURE__ */ T("div", {
72
72
  className: "p-3",
73
73
  children: [
74
- /* @__PURE__ */ w("div", {
74
+ /* @__PURE__ */ T("div", {
75
75
  className: "flex items-center gap-1.5",
76
- children: [/* @__PURE__ */ C("p", {
77
- className: "text-sm font-semibold text-text-primary",
78
- children: e.title
79
- }), e.reraVerificationStatus === "verified" && /* @__PURE__ */ C(b, { className: "h-3.5 w-3.5 shrink-0 text-status-success-text" })]
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__ */ C("p", {
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__ */ w("p", {
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__ */ C("button", {
96
+ /* @__PURE__ */ w("button", {
90
97
  type: "button",
91
- onClick: () => j?.(e.id),
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
- }), M && /* @__PURE__ */ C("div", {
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__ */ w("button", {
110
+ children: /* @__PURE__ */ T("button", {
104
111
  type: "button",
105
112
  onClick: () => {
106
- if (!P || !M) return;
107
- let e = u(P);
108
- R.current = e, M(e);
113
+ if (!F || !N) return;
114
+ let e = u(F);
115
+ z.current = e, N(e);
109
116
  },
110
- disabled: !V || p,
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", V && !p ? "text-text-primary hover:bg-bg-sunken" : "cursor-not-allowed text-text-secondary opacity-70"),
112
- children: [/* @__PURE__ */ C(y, { className: "h-3 w-3" }), p ? "Searching…" : "Search this area"]
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__ */ C("div", {
121
+ })] }) : /* @__PURE__ */ w("div", {
115
122
  className: "flex h-full w-full items-center justify-center",
116
123
  "aria-busy": "true",
117
- children: /* @__PURE__ */ C("div", { className: "h-full w-full motion-safe:animate-pulse bg-bg-sunken" })
124
+ children: /* @__PURE__ */ w("div", { className: "h-full w-full motion-safe:animate-pulse bg-bg-sunken" })
118
125
  })
119
- }), /* @__PURE__ */ w("div", {
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: O },
122
- children: [z.length === 0 && !p && /* @__PURE__ */ C("p", {
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
- }), z.map((r) => /* @__PURE__ */ w("button", {
132
+ }), B.map((r) => /* @__PURE__ */ T("button", {
126
133
  type: "button",
127
- onMouseEnter: () => L(r.id),
128
- onMouseLeave: () => L((e) => e === r.id ? null : e),
129
- onClick: () => j?.(r.id),
130
- className: e("flex items-center gap-3 rounded-lg border p-2 text-left transition-colors", I === r.id ? "border-[var(--color-action-primary-bg)] bg-bg-sunken" : "border-transparent hover:bg-bg-sunken"),
131
- children: [/* @__PURE__ */ w("div", {
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__ */ C("p", {
141
+ /* @__PURE__ */ w("p", {
135
142
  className: "truncate text-sm font-medium text-text-primary",
136
143
  children: r.title
137
144
  }),
138
- /* @__PURE__ */ C("p", {
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__ */ w("div", {
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__ */ w("span", {
152
+ r.specifications.bedrooms != null && /* @__PURE__ */ T("span", {
146
153
  className: "inline-flex items-center gap-1",
147
- children: [/* @__PURE__ */ C(v, { className: "h-3 w-3" }), r.specifications.bedrooms]
154
+ children: [/* @__PURE__ */ w(y, { className: "h-3 w-3" }), r.specifications.bedrooms]
148
155
  }),
149
- r.specifications.bathrooms != null && /* @__PURE__ */ w("span", {
156
+ r.specifications.bathrooms != null && /* @__PURE__ */ T("span", {
150
157
  className: "inline-flex items-center gap-1",
151
- children: [/* @__PURE__ */ C(_, { className: "h-3 w-3" }), r.specifications.bathrooms]
158
+ children: [/* @__PURE__ */ w(v, { className: "h-3 w-3" }), r.specifications.bathrooms]
152
159
  }),
153
- /* @__PURE__ */ w("span", {
160
+ /* @__PURE__ */ T("span", {
154
161
  className: "inline-flex items-center gap-1",
155
162
  children: [
156
- /* @__PURE__ */ C(x, { className: "h-3 w-3" }),
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__ */ C("span", {
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 { D as PropertyMapView };
180
+ export { O as PropertyMapView };
174
181
 
175
182
  //# sourceMappingURL=PropertyMapView.js.map