@burdenoff/microfe-housingvista 2026.624.2 → 2026.625.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.
Files changed (59) hide show
  1. package/dist/housingvista/components/ChartCard.js +11 -9
  2. package/dist/housingvista/components/ChartCard.js.map +1 -1
  3. package/dist/housingvista/components/DataTable.js +63 -60
  4. package/dist/housingvista/components/DataTable.js.map +1 -1
  5. package/dist/housingvista/components/EmptyState.js +16 -9
  6. package/dist/housingvista/components/EmptyState.js.map +1 -1
  7. package/dist/housingvista/components/PageLayout.js +21 -14
  8. package/dist/housingvista/components/PageLayout.js.map +1 -1
  9. package/dist/housingvista/components/PropertyCard.js +2 -2
  10. package/dist/housingvista/components/PropertyCard.js.map +1 -1
  11. package/dist/housingvista/components/StatCard.js +20 -17
  12. package/dist/housingvista/components/StatCard.js.map +1 -1
  13. package/dist/housingvista/components/StatusBadge.js +1 -1
  14. package/dist/housingvista/components/StatusBadge.js.map +1 -1
  15. package/dist/housingvista/pages/HomePage.js +101 -58
  16. package/dist/housingvista/pages/HomePage.js.map +1 -1
  17. package/dist/housingvista/pages/amc/AMCManagementPage.js +59 -48
  18. package/dist/housingvista/pages/amc/AMCManagementPage.js.map +1 -1
  19. package/dist/housingvista/pages/analytics/AnalyticsPage.js +113 -90
  20. package/dist/housingvista/pages/analytics/AnalyticsPage.js.map +1 -1
  21. package/dist/housingvista/pages/assets/AssetManagementPage.js +66 -60
  22. package/dist/housingvista/pages/assets/AssetManagementPage.js.map +1 -1
  23. package/dist/housingvista/pages/chat/ChatPage.js +60 -58
  24. package/dist/housingvista/pages/chat/ChatPage.js.map +1 -1
  25. package/dist/housingvista/pages/finance/HomeLoansPage.js +23 -20
  26. package/dist/housingvista/pages/finance/HomeLoansPage.js.map +1 -1
  27. package/dist/housingvista/pages/investments/InvestmentDetailPage.js +114 -81
  28. package/dist/housingvista/pages/investments/InvestmentDetailPage.js.map +1 -1
  29. package/dist/housingvista/pages/investments/InvestmentsPage.js +65 -59
  30. package/dist/housingvista/pages/investments/InvestmentsPage.js.map +1 -1
  31. package/dist/housingvista/pages/legal/LegalPage.js +17 -14
  32. package/dist/housingvista/pages/legal/LegalPage.js.map +1 -1
  33. package/dist/housingvista/pages/market/AreaInsightsPage.js +13 -10
  34. package/dist/housingvista/pages/market/AreaInsightsPage.js.map +1 -1
  35. package/dist/housingvista/pages/market/MarketAnalyticsPage.js +155 -123
  36. package/dist/housingvista/pages/market/MarketAnalyticsPage.js.map +1 -1
  37. package/dist/housingvista/pages/marketplace/MarketplacePage.js +15 -12
  38. package/dist/housingvista/pages/marketplace/MarketplacePage.js.map +1 -1
  39. package/dist/housingvista/pages/notifications/NotificationsPage.js +24 -21
  40. package/dist/housingvista/pages/notifications/NotificationsPage.js.map +1 -1
  41. package/dist/housingvista/pages/properties/AddPropertyPage.js +73 -71
  42. package/dist/housingvista/pages/properties/AddPropertyPage.js.map +1 -1
  43. package/dist/housingvista/pages/properties/PropertiesPage.js +141 -135
  44. package/dist/housingvista/pages/properties/PropertiesPage.js.map +1 -1
  45. package/dist/housingvista/pages/properties/PropertyDetailPage.js +192 -154
  46. package/dist/housingvista/pages/properties/PropertyDetailPage.js.map +1 -1
  47. package/dist/housingvista/pages/rental/RentalManagementPage.js +185 -160
  48. package/dist/housingvista/pages/rental/RentalManagementPage.js.map +1 -1
  49. package/dist/housingvista/pages/settings/ProfilePage.js +51 -49
  50. package/dist/housingvista/pages/settings/ProfilePage.js.map +1 -1
  51. package/dist/housingvista/pages/settings/SettingsPage.js +56 -54
  52. package/dist/housingvista/pages/settings/SettingsPage.js.map +1 -1
  53. package/dist/housingvista/pages/transactions/TransactionDetailPage.js +183 -142
  54. package/dist/housingvista/pages/transactions/TransactionDetailPage.js.map +1 -1
  55. package/dist/housingvista/pages/transactions/TransactionsPage.js +90 -84
  56. package/dist/housingvista/pages/transactions/TransactionsPage.js.map +1 -1
  57. package/dist/housingvista/pages/verification/VerificationPage.js +49 -42
  58. package/dist/housingvista/pages/verification/VerificationPage.js.map +1 -1
  59. package/package.json +2 -2
@@ -1,23 +1,25 @@
1
1
  import { cn as e } from "../utils/helpers.js";
2
2
  import { jsx as t, jsxs as n } from "react/jsx-runtime";
3
+ import { GlassCard as r } from "@burdenoff/fe-libs/ui";
3
4
  //#region src/housingvista/components/ChartCard.tsx
4
- var r = ({ title: r, description: i, actions: a, children: o, className: s }) => /* @__PURE__ */ n("div", {
5
- className: e("rounded-lg border border-border-subtle bg-bg-surface p-4 shadow-sm", s),
5
+ var i = ({ title: i, description: a, actions: o, children: s, className: c }) => /* @__PURE__ */ n(r, {
6
+ treatment: "insight",
7
+ className: e("border-border-seam p-4", c),
6
8
  children: [/* @__PURE__ */ n("div", {
7
9
  className: "mb-4 flex items-center justify-between",
8
10
  children: [/* @__PURE__ */ n("div", { children: [/* @__PURE__ */ t("h3", {
9
11
  className: "text-sm font-medium text-text-primary",
10
- children: r
11
- }), i && /* @__PURE__ */ t("p", {
12
- className: "text-xs text-text-secondary mt-0.5",
13
12
  children: i
14
- })] }), a && /* @__PURE__ */ t("div", {
15
- className: "flex items-center gap-2",
13
+ }), a && /* @__PURE__ */ t("p", {
14
+ className: "text-xs text-text-secondary mt-0.5",
16
15
  children: a
16
+ })] }), o && /* @__PURE__ */ t("div", {
17
+ className: "flex items-center gap-2",
18
+ children: o
17
19
  })]
18
- }), o]
20
+ }), s]
19
21
  });
20
22
  //#endregion
21
- export { r as ChartCard };
23
+ export { i as ChartCard };
22
24
 
23
25
  //# sourceMappingURL=ChartCard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChartCard.js","names":[],"sources":["../../../src/housingvista/components/ChartCard.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { cn } from '../utils/helpers';\n\ninterface ChartCardProps {\n title: string;\n description?: string;\n actions?: ReactNode;\n children: ReactNode;\n className?: string;\n}\n\nexport const ChartCard: FC<ChartCardProps> = ({ title, description, actions, children, className }) => {\n return (\n <div className={cn('rounded-lg border border-border-subtle bg-bg-surface p-4 shadow-sm', className)}>\n <div className=\"mb-4 flex items-center justify-between\">\n <div>\n <h3 className=\"text-sm font-medium text-text-primary\">{title}</h3>\n {description && <p className=\"text-xs text-text-secondary mt-0.5\">{description}</p>}\n </div>\n {actions && <div className=\"flex items-center gap-2\">{actions}</div>}\n </div>\n {children}\n </div>\n );\n};\n"],"mappings":";;;AAWA,IAAa,KAAiC,EAAE,UAAO,gBAAa,YAAS,aAAU,mBAEnF,kBAAC,OAAD;CAAK,WAAW,EAAG,sEAAsE,EAAU;WAAnG,CACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;GAAI,WAAU;aAAyC;GAAW,CAAA,EACjE,KAAe,kBAAC,KAAD;GAAG,WAAU;aAAsC;GAAgB,CAAA,CAC/E,EAAA,CAAA,EACL,KAAW,kBAAC,OAAD;GAAK,WAAU;aAA2B;GAAc,CAAA,CAChE;KACL,EACG"}
1
+ {"version":3,"file":"ChartCard.js","names":[],"sources":["../../../src/housingvista/components/ChartCard.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/helpers';\n\ninterface ChartCardProps {\n title: string;\n description?: string;\n actions?: ReactNode;\n children: ReactNode;\n className?: string;\n}\n\nexport const ChartCard: FC<ChartCardProps> = ({ title, description, actions, children, className }) => {\n return (\n <GlassCard treatment=\"insight\" className={cn('border-border-seam p-4', className)}>\n <div className=\"mb-4 flex items-center justify-between\">\n <div>\n <h3 className=\"text-sm font-medium text-text-primary\">{title}</h3>\n {description && <p className=\"text-xs text-text-secondary mt-0.5\">{description}</p>}\n </div>\n {actions && <div className=\"flex items-center gap-2\">{actions}</div>}\n </div>\n {children}\n </GlassCard>\n );\n};\n"],"mappings":";;;;AAYA,IAAa,KAAiC,EAAE,UAAO,gBAAa,YAAS,aAAU,mBAEnF,kBAAC,GAAD;CAAW,WAAU;CAAU,WAAW,EAAG,0BAA0B,EAAU;WAAjF,CACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;GAAI,WAAU;aAAyC;GAAW,CAAA,EACjE,KAAe,kBAAC,KAAD;GAAG,WAAU;aAAsC;GAAgB,CAAA,CAC/E,EAAA,CAAA,EACL,KAAW,kBAAC,OAAD;GAAK,WAAU;aAA2B;GAAc,CAAA,CAChE;KACL,EACS"}
@@ -2,80 +2,83 @@ import { cn as e } from "../utils/helpers.js";
2
2
  import { useCallback as t, useMemo as n, useState as r } from "react";
3
3
  import { ChevronDown as i, ChevronLeft as a, ChevronRight as o, ChevronUp as s, ChevronsUpDown as c } from "lucide-react";
4
4
  import { jsx as l, jsxs as u } from "react/jsx-runtime";
5
+ import { IllustratedEmptyState as d } from "@burdenoff/fe-libs/ui";
5
6
  //#region src/housingvista/components/DataTable.tsx
6
- function d({ columns: d, data: f, keyField: p, onRowClick: m, emptyMessage: h = "No data available", className: g, pageSize: _ = 10, selectable: v = !1, selectedKeys: y, onSelectionChange: b, serverPagination: x }) {
7
- let [S, C] = r(null), [w, T] = r(null), [E, D] = r(0), O = t((e) => {
8
- S === e ? w === "asc" ? T("desc") : w === "desc" ? (C(null), T(null)) : T("asc") : (C(e), T("asc")), D(0);
9
- }, [S, w]), k = n(() => !S || !w ? f : [...f].sort((e, t) => {
10
- let n = e[S], r = t[S];
7
+ function f({ columns: f, data: p, keyField: m, onRowClick: h, emptyMessage: g = "No data available", emptyDescription: _, emptyIllustration: v = "empty-data", emptyAction: y, className: b, pageSize: x = 10, selectable: S = !1, selectedKeys: C, onSelectionChange: w, serverPagination: T }) {
8
+ let [E, D] = r(null), [O, k] = r(null), [A, j] = r(0), M = t((e) => {
9
+ E === e ? O === "asc" ? k("desc") : O === "desc" ? (D(null), k(null)) : k("asc") : (D(e), k("asc")), j(0);
10
+ }, [E, O]), N = n(() => !E || !O ? p : [...p].sort((e, t) => {
11
+ let n = e[E], r = t[E];
11
12
  if (n == null && r == null) return 0;
12
13
  if (n == null) return 1;
13
14
  if (r == null) return -1;
14
15
  let i = typeof n == "number" && typeof r == "number" ? n - r : String(n).localeCompare(String(r));
15
- return w === "asc" ? i : -i;
16
+ return O === "asc" ? i : -i;
16
17
  }), [
17
- f,
18
- S,
19
- w
20
- ]), A = x?.pageSize ?? _, j = Math.max(1, Math.ceil(k.length / A)), M = x ? k : k.slice(E * A, (E + 1) * A), N = v && y ? M.every((e) => y.has(String(e[p]))) : !1, P = () => {
21
- if (!b || !y) return;
22
- let e = new Set(y);
23
- N ? M.forEach((t) => e.delete(String(t[p]))) : M.forEach((t) => e.add(String(t[p]))), b(e);
24
- }, F = (e) => {
25
- if (!b || !y) return;
26
- let t = String(e[p]), n = new Set(y);
27
- n.has(t) ? n.delete(t) : n.add(t), b(n);
18
+ p,
19
+ E,
20
+ O
21
+ ]), P = T?.pageSize ?? x, F = Math.max(1, Math.ceil(N.length / P)), I = T ? N : N.slice(A * P, (A + 1) * P), L = S && C ? I.every((e) => C.has(String(e[m]))) : !1, R = () => {
22
+ if (!w || !C) return;
23
+ let e = new Set(C);
24
+ L ? I.forEach((t) => e.delete(String(t[m]))) : I.forEach((t) => e.add(String(t[m]))), w(e);
25
+ }, z = (e) => {
26
+ if (!w || !C) return;
27
+ let t = String(e[m]), n = new Set(C);
28
+ n.has(t) ? n.delete(t) : n.add(t), w(n);
28
29
  };
29
- return f.length === 0 ? /* @__PURE__ */ l("div", {
30
- className: "flex items-center justify-center rounded-lg border border-border-subtle bg-bg-surface p-12",
31
- children: /* @__PURE__ */ l("p", {
32
- className: "text-sm text-text-secondary",
33
- children: h
30
+ return p.length === 0 ? /* @__PURE__ */ l("div", {
31
+ className: "rounded-card border border-dashed border-border-seam bg-bg-surface/50",
32
+ children: /* @__PURE__ */ l(d, {
33
+ illustration: v,
34
+ title: g,
35
+ description: _,
36
+ action: y
34
37
  })
35
38
  }) : /* @__PURE__ */ u("div", {
36
- className: e("space-y-3", g),
39
+ className: e("space-y-3", b),
37
40
  children: [/* @__PURE__ */ l("div", {
38
- className: "overflow-x-auto rounded-lg border border-border-subtle",
41
+ className: "overflow-x-auto rounded-card border border-border-seam bg-bg-surface shadow-elevation-1",
39
42
  children: /* @__PURE__ */ u("table", {
40
43
  className: "w-full",
41
44
  children: [/* @__PURE__ */ l("thead", { children: /* @__PURE__ */ u("tr", {
42
45
  className: "border-b border-border-subtle bg-bg-sunken/50",
43
- children: [v && /* @__PURE__ */ l("th", {
46
+ children: [S && /* @__PURE__ */ l("th", {
44
47
  className: "w-10 px-3 py-3",
45
48
  children: /* @__PURE__ */ l("input", {
46
49
  type: "checkbox",
47
- checked: N && M.length > 0,
48
- onChange: P,
50
+ checked: L && I.length > 0,
51
+ onChange: R,
49
52
  className: "h-4 w-4 rounded border-border-subtle"
50
53
  })
51
- }), d.map((t) => {
52
- let n = t.sortable !== !1, r = S === t.key;
54
+ }), f.map((t) => {
55
+ let n = t.sortable !== !1, r = E === t.key;
53
56
  return /* @__PURE__ */ l("th", {
54
57
  className: e("px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-text-secondary", n && "cursor-pointer select-none hover:text-text-primary", t.className),
55
- onClick: n ? () => O(t.key) : void 0,
58
+ onClick: n ? () => M(t.key) : void 0,
56
59
  children: /* @__PURE__ */ u("span", {
57
60
  className: "inline-flex items-center gap-1",
58
- children: [t.header, n && (r ? l(w === "asc" ? s : i, { className: "h-3 w-3" }) : /* @__PURE__ */ l(c, { className: "h-3 w-3 opacity-30" }))]
61
+ children: [t.header, n && (r ? l(O === "asc" ? s : i, { className: "h-3 w-3" }) : /* @__PURE__ */ l(c, { className: "h-3 w-3 opacity-30" }))]
59
62
  })
60
63
  }, t.key);
61
64
  })]
62
65
  }) }), /* @__PURE__ */ l("tbody", {
63
66
  className: "divide-y divide-border bg-bg-surface",
64
- children: M.map((t) => {
65
- let n = String(t[p]), r = v && y?.has(n);
67
+ children: I.map((t) => {
68
+ let n = String(t[m]), r = S && C?.has(n);
66
69
  return /* @__PURE__ */ u("tr", {
67
- className: e("transition-colors", m && "cursor-pointer hover:bg-bg-sunken/50", r && "bg-action-primary-bg/5"),
68
- onClick: () => m?.(t),
69
- children: [v && /* @__PURE__ */ l("td", {
70
+ className: e("transition-colors", h && "cursor-pointer hover:bg-bg-sunken/50", r && "bg-action-primary-bg/5"),
71
+ onClick: () => h?.(t),
72
+ children: [S && /* @__PURE__ */ l("td", {
70
73
  className: "w-10 px-3 py-3",
71
74
  onClick: (e) => e.stopPropagation(),
72
75
  children: /* @__PURE__ */ l("input", {
73
76
  type: "checkbox",
74
77
  checked: r ?? !1,
75
- onChange: () => F(t),
78
+ onChange: () => z(t),
76
79
  className: "h-4 w-4 rounded border-border-subtle"
77
80
  })
78
- }), d.map((n) => /* @__PURE__ */ l("td", {
81
+ }), f.map((n) => /* @__PURE__ */ l("td", {
79
82
  className: e("px-4 py-3 text-sm text-text-primary", n.className),
80
83
  children: n.render ? n.render(t) : String(t[n.key] ?? "")
81
84
  }, n.key))]
@@ -83,18 +86,18 @@ function d({ columns: d, data: f, keyField: p, onRowClick: m, emptyMessage: h =
83
86
  })
84
87
  })]
85
88
  })
86
- }), x ? /* @__PURE__ */ u("div", {
89
+ }), T ? /* @__PURE__ */ u("div", {
87
90
  className: "flex flex-col gap-3 rounded-lg border border-border-subtle bg-bg-surface p-3 text-sm sm:flex-row sm:items-center sm:justify-between",
88
91
  children: [/* @__PURE__ */ u("span", {
89
92
  className: "text-text-secondary",
90
- children: ["Page ", x.page]
93
+ children: ["Page ", T.page]
91
94
  }), /* @__PURE__ */ u("div", {
92
95
  className: "flex flex-wrap items-center gap-2",
93
96
  children: [
94
- x.onPageSizeChange ? /* @__PURE__ */ l("select", {
95
- value: x.pageSize,
96
- onChange: (e) => x.onPageSizeChange?.(Number(e.target.value)),
97
- disabled: x.loading,
97
+ T.onPageSizeChange ? /* @__PURE__ */ l("select", {
98
+ value: T.pageSize,
99
+ onChange: (e) => T.onPageSizeChange?.(Number(e.target.value)),
100
+ disabled: T.loading,
98
101
  className: "h-8 rounded-md border border-border-subtle bg-bg-surface px-2 text-sm text-text-primary disabled:opacity-50",
99
102
  "aria-label": "Rows per page",
100
103
  children: [
@@ -109,49 +112,49 @@ function d({ columns: d, data: f, keyField: p, onRowClick: m, emptyMessage: h =
109
112
  }) : null,
110
113
  /* @__PURE__ */ u("button", {
111
114
  type: "button",
112
- onClick: () => x.onPageChange(x.page - 1),
113
- disabled: x.loading || x.page <= 1,
115
+ onClick: () => T.onPageChange(T.page - 1),
116
+ disabled: T.loading || T.page <= 1,
114
117
  className: "inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40",
115
118
  children: [/* @__PURE__ */ l(a, { className: "h-4 w-4" }), "Previous"]
116
119
  }),
117
120
  /* @__PURE__ */ u("button", {
118
121
  type: "button",
119
- onClick: () => x.onPageChange(x.page + 1),
120
- disabled: x.loading || !(x.hasNextPage ?? k.length >= x.pageSize),
122
+ onClick: () => T.onPageChange(T.page + 1),
123
+ disabled: T.loading || !(T.hasNextPage ?? N.length >= T.pageSize),
121
124
  className: "inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40",
122
125
  children: ["Next", /* @__PURE__ */ l(o, { className: "h-4 w-4" })]
123
126
  })
124
127
  ]
125
128
  })]
126
- }) : k.length > _ ? /* @__PURE__ */ u("div", {
129
+ }) : N.length > x ? /* @__PURE__ */ u("div", {
127
130
  className: "flex items-center justify-between text-sm",
128
131
  children: [/* @__PURE__ */ u("span", {
129
132
  className: "text-text-secondary",
130
133
  children: [
131
134
  "Showing ",
132
- E * _ + 1,
135
+ A * x + 1,
133
136
  "-",
134
- Math.min((E + 1) * _, k.length),
137
+ Math.min((A + 1) * x, N.length),
135
138
  " of ",
136
- k.length
139
+ N.length
137
140
  ]
138
141
  }), /* @__PURE__ */ u("div", {
139
142
  className: "flex items-center gap-1",
140
143
  children: [
141
144
  /* @__PURE__ */ l("button", {
142
- onClick: () => D((e) => Math.max(0, e - 1)),
143
- disabled: E === 0,
145
+ onClick: () => j((e) => Math.max(0, e - 1)),
146
+ disabled: A === 0,
144
147
  className: "rounded-md border border-border-subtle p-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40",
145
148
  children: /* @__PURE__ */ l(a, { className: "h-4 w-4" })
146
149
  }),
147
- Array.from({ length: j }).map((t, n) => /* @__PURE__ */ l("button", {
148
- onClick: () => D(n),
149
- className: e("h-8 w-8 rounded-md text-xs font-medium transition-colors", n === E ? "bg-foreground text-background" : "text-text-secondary hover:bg-bg-sunken"),
150
+ Array.from({ length: F }).map((t, n) => /* @__PURE__ */ l("button", {
151
+ onClick: () => j(n),
152
+ className: e("h-8 w-8 rounded-md text-xs font-medium transition-colors", n === A ? "bg-foreground text-background" : "text-text-secondary hover:bg-bg-sunken"),
150
153
  children: n + 1
151
154
  }, n)),
152
155
  /* @__PURE__ */ l("button", {
153
- onClick: () => D((e) => Math.min(j - 1, e + 1)),
154
- disabled: E >= j - 1,
156
+ onClick: () => j((e) => Math.min(F - 1, e + 1)),
157
+ disabled: A >= F - 1,
155
158
  className: "rounded-md border border-border-subtle p-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40",
156
159
  children: /* @__PURE__ */ l(o, { className: "h-4 w-4" })
157
160
  })
@@ -161,6 +164,6 @@ function d({ columns: d, data: f, keyField: p, onRowClick: m, emptyMessage: h =
161
164
  });
162
165
  }
163
166
  //#endregion
164
- export { d as DataTable };
167
+ export { f as DataTable };
165
168
 
166
169
  //# sourceMappingURL=DataTable.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DataTable.js","names":[],"sources":["../../../src/housingvista/components/DataTable.tsx"],"sourcesContent":["import { useState, useMemo, useCallback, type ReactNode } from 'react';\nimport { ChevronUp, ChevronDown, ChevronsUpDown, ChevronLeft, ChevronRight } from 'lucide-react';\nimport { cn } from '../utils/helpers';\n\nexport interface Column<T> {\n key: string;\n header: string;\n render?: (item: T) => ReactNode;\n className?: string;\n sortable?: boolean;\n}\n\ntype SortDirection = 'asc' | 'desc' | null;\n\ninterface DataTableProps<T> {\n columns: Column<T>[];\n data: T[];\n keyField: string;\n onRowClick?: (item: T) => void;\n emptyMessage?: string;\n className?: string;\n pageSize?: number;\n selectable?: boolean;\n selectedKeys?: Set<string>;\n onSelectionChange?: (keys: Set<string>) => void;\n serverPagination?: {\n page: number;\n pageSize: number;\n hasNextPage?: boolean;\n loading?: boolean;\n onPageChange: (page: number) => void;\n onPageSizeChange?: (pageSize: number) => void;\n };\n}\n\nexport function DataTable<T extends object>({\n columns,\n data,\n keyField,\n onRowClick,\n emptyMessage = 'No data available',\n className,\n pageSize = 10,\n selectable = false,\n selectedKeys,\n onSelectionChange,\n serverPagination,\n}: DataTableProps<T>) {\n const [sortKey, setSortKey] = useState<string | null>(null);\n const [sortDir, setSortDir] = useState<SortDirection>(null);\n const [page, setPage] = useState(0);\n\n const handleSort = useCallback(\n (key: string) => {\n if (sortKey === key) {\n if (sortDir === 'asc') setSortDir('desc');\n else if (sortDir === 'desc') {\n setSortKey(null);\n setSortDir(null);\n } else setSortDir('asc');\n } else {\n setSortKey(key);\n setSortDir('asc');\n }\n setPage(0);\n },\n [sortKey, sortDir]\n );\n\n const sorted = useMemo(() => {\n if (!sortKey || !sortDir) return data;\n return [...data].sort((a, b) => {\n const av = (a as Record<string, unknown>)[sortKey];\n const bv = (b as Record<string, unknown>)[sortKey];\n if (av == null && bv == null) return 0;\n if (av == null) return 1;\n if (bv == null) return -1;\n const cmp = typeof av === 'number' && typeof bv === 'number' ? av - bv : String(av).localeCompare(String(bv));\n return sortDir === 'asc' ? cmp : -cmp;\n });\n }, [data, sortKey, sortDir]);\n\n const effectivePageSize = serverPagination?.pageSize ?? pageSize;\n const totalPages = Math.max(1, Math.ceil(sorted.length / effectivePageSize));\n const paged = serverPagination ? sorted : sorted.slice(page * effectivePageSize, (page + 1) * effectivePageSize);\n\n const allPageKeysSelected =\n selectable && selectedKeys\n ? paged.every((item) => selectedKeys.has(String((item as Record<string, unknown>)[keyField])))\n : false;\n\n const handleSelectAll = () => {\n if (!onSelectionChange || !selectedKeys) return;\n const keys = new Set(selectedKeys);\n if (allPageKeysSelected) {\n paged.forEach((item) => keys.delete(String((item as Record<string, unknown>)[keyField])));\n } else {\n paged.forEach((item) => keys.add(String((item as Record<string, unknown>)[keyField])));\n }\n onSelectionChange(keys);\n };\n\n const handleSelectRow = (item: T) => {\n if (!onSelectionChange || !selectedKeys) return;\n const key = String((item as Record<string, unknown>)[keyField]);\n const keys = new Set(selectedKeys);\n if (keys.has(key)) keys.delete(key);\n else keys.add(key);\n onSelectionChange(keys);\n };\n\n if (data.length === 0) {\n return (\n <div className=\"flex items-center justify-center rounded-lg border border-border-subtle bg-bg-surface p-12\">\n <p className=\"text-sm text-text-secondary\">{emptyMessage}</p>\n </div>\n );\n }\n\n return (\n <div className={cn('space-y-3', className)}>\n <div className=\"overflow-x-auto rounded-lg border border-border-subtle\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border-subtle bg-bg-sunken/50\">\n {selectable && (\n <th className=\"w-10 px-3 py-3\">\n <input\n type=\"checkbox\"\n checked={allPageKeysSelected && paged.length > 0}\n onChange={handleSelectAll}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </th>\n )}\n {columns.map((col) => {\n const isSortable = col.sortable !== false;\n const isActive = sortKey === col.key;\n return (\n <th\n key={col.key}\n className={cn(\n 'px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-text-secondary',\n isSortable && 'cursor-pointer select-none hover:text-text-primary',\n col.className\n )}\n onClick={isSortable ? () => handleSort(col.key) : undefined}\n >\n <span className=\"inline-flex items-center gap-1\">\n {col.header}\n {isSortable &&\n (isActive ? (\n sortDir === 'asc' ? (\n <ChevronUp className=\"h-3 w-3\" />\n ) : (\n <ChevronDown className=\"h-3 w-3\" />\n )\n ) : (\n <ChevronsUpDown className=\"h-3 w-3 opacity-30\" />\n ))}\n </span>\n </th>\n );\n })}\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border bg-bg-surface\">\n {paged.map((item) => {\n const rowKey = String((item as Record<string, unknown>)[keyField]);\n const isSelected = selectable && selectedKeys?.has(rowKey);\n return (\n <tr\n key={rowKey}\n className={cn(\n 'transition-colors',\n onRowClick && 'cursor-pointer hover:bg-bg-sunken/50',\n isSelected && 'bg-action-primary-bg/5'\n )}\n onClick={() => onRowClick?.(item)}\n >\n {selectable && (\n <td className=\"w-10 px-3 py-3\" onClick={(e) => e.stopPropagation()}>\n <input\n type=\"checkbox\"\n checked={isSelected ?? false}\n onChange={() => handleSelectRow(item)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </td>\n )}\n {columns.map((col) => (\n <td key={col.key} className={cn('px-4 py-3 text-sm text-text-primary', col.className)}>\n {col.render ? col.render(item) : String((item as Record<string, unknown>)[col.key] ?? '')}\n </td>\n ))}\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n\n {serverPagination ? (\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-subtle bg-bg-surface p-3 text-sm sm:flex-row sm:items-center sm:justify-between\">\n <span className=\"text-text-secondary\">Page {serverPagination.page}</span>\n <div className=\"flex flex-wrap items-center gap-2\">\n {serverPagination.onPageSizeChange ? (\n <select\n value={serverPagination.pageSize}\n onChange={(event) => serverPagination.onPageSizeChange?.(Number(event.target.value))}\n disabled={serverPagination.loading}\n className=\"h-8 rounded-md border border-border-subtle bg-bg-surface px-2 text-sm text-text-primary disabled:opacity-50\"\n aria-label=\"Rows per page\"\n >\n {[10, 25, 50, 100].map((option) => (\n <option key={option} value={option}>\n {option} / page\n </option>\n ))}\n </select>\n ) : null}\n <button\n type=\"button\"\n onClick={() => serverPagination.onPageChange(serverPagination.page - 1)}\n disabled={serverPagination.loading || serverPagination.page <= 1}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n Previous\n </button>\n <button\n type=\"button\"\n onClick={() => serverPagination.onPageChange(serverPagination.page + 1)}\n disabled={\n serverPagination.loading ||\n !(serverPagination.hasNextPage ?? sorted.length >= serverPagination.pageSize)\n }\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n Next\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ) : sorted.length > pageSize ? (\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">\n Showing {page * pageSize + 1}-{Math.min((page + 1) * pageSize, sorted.length)} of {sorted.length}\n </span>\n <div className=\"flex items-center gap-1\">\n <button\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n disabled={page === 0}\n className=\"rounded-md border border-border-subtle p-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n </button>\n {Array.from({ length: totalPages }).map((_, i) => (\n <button\n key={i}\n onClick={() => setPage(i)}\n className={cn(\n 'h-8 w-8 rounded-md text-xs font-medium transition-colors',\n i === page ? 'bg-foreground text-background' : 'text-text-secondary hover:bg-bg-sunken'\n )}\n >\n {i + 1}\n </button>\n ))}\n <button\n onClick={() => setPage((p) => Math.min(totalPages - 1, p + 1))}\n disabled={page >= totalPages - 1}\n className=\"rounded-md border border-border-subtle p-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DataTable.js","names":[],"sources":["../../../src/housingvista/components/DataTable.tsx"],"sourcesContent":["import { useState, useMemo, useCallback, type ReactNode } from 'react';\nimport { ChevronUp, ChevronDown, ChevronsUpDown, ChevronLeft, ChevronRight } from 'lucide-react';\nimport { IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/helpers';\n\nexport interface Column<T> {\n key: string;\n header: string;\n render?: (item: T) => ReactNode;\n className?: string;\n sortable?: boolean;\n}\n\ntype SortDirection = 'asc' | 'desc' | null;\n\ninterface DataTableProps<T> {\n columns: Column<T>[];\n data: T[];\n keyField: string;\n onRowClick?: (item: T) => void;\n emptyMessage?: string;\n /** Optional secondary line under the empty title. */\n emptyDescription?: string;\n /**\n * Boff UI 2.0 — on-brand illustration name (served from `/illustrations/<name>.webp`)\n * shown in the zero state. Standard names: empty-data, empty-search, empty-files,\n * empty-people, empty-notifications, empty-generic. Defaults to `empty-data`.\n */\n emptyIllustration?: string;\n /** Optional action rendered in the empty state (e.g. a create button). */\n emptyAction?: ReactNode;\n className?: string;\n pageSize?: number;\n selectable?: boolean;\n selectedKeys?: Set<string>;\n onSelectionChange?: (keys: Set<string>) => void;\n serverPagination?: {\n page: number;\n pageSize: number;\n hasNextPage?: boolean;\n loading?: boolean;\n onPageChange: (page: number) => void;\n onPageSizeChange?: (pageSize: number) => void;\n };\n}\n\nexport function DataTable<T extends object>({\n columns,\n data,\n keyField,\n onRowClick,\n emptyMessage = 'No data available',\n emptyDescription,\n emptyIllustration = 'empty-data',\n emptyAction,\n className,\n pageSize = 10,\n selectable = false,\n selectedKeys,\n onSelectionChange,\n serverPagination,\n}: DataTableProps<T>) {\n const [sortKey, setSortKey] = useState<string | null>(null);\n const [sortDir, setSortDir] = useState<SortDirection>(null);\n const [page, setPage] = useState(0);\n\n const handleSort = useCallback(\n (key: string) => {\n if (sortKey === key) {\n if (sortDir === 'asc') setSortDir('desc');\n else if (sortDir === 'desc') {\n setSortKey(null);\n setSortDir(null);\n } else setSortDir('asc');\n } else {\n setSortKey(key);\n setSortDir('asc');\n }\n setPage(0);\n },\n [sortKey, sortDir]\n );\n\n const sorted = useMemo(() => {\n if (!sortKey || !sortDir) return data;\n return [...data].sort((a, b) => {\n const av = (a as Record<string, unknown>)[sortKey];\n const bv = (b as Record<string, unknown>)[sortKey];\n if (av == null && bv == null) return 0;\n if (av == null) return 1;\n if (bv == null) return -1;\n const cmp = typeof av === 'number' && typeof bv === 'number' ? av - bv : String(av).localeCompare(String(bv));\n return sortDir === 'asc' ? cmp : -cmp;\n });\n }, [data, sortKey, sortDir]);\n\n const effectivePageSize = serverPagination?.pageSize ?? pageSize;\n const totalPages = Math.max(1, Math.ceil(sorted.length / effectivePageSize));\n const paged = serverPagination ? sorted : sorted.slice(page * effectivePageSize, (page + 1) * effectivePageSize);\n\n const allPageKeysSelected =\n selectable && selectedKeys\n ? paged.every((item) => selectedKeys.has(String((item as Record<string, unknown>)[keyField])))\n : false;\n\n const handleSelectAll = () => {\n if (!onSelectionChange || !selectedKeys) return;\n const keys = new Set(selectedKeys);\n if (allPageKeysSelected) {\n paged.forEach((item) => keys.delete(String((item as Record<string, unknown>)[keyField])));\n } else {\n paged.forEach((item) => keys.add(String((item as Record<string, unknown>)[keyField])));\n }\n onSelectionChange(keys);\n };\n\n const handleSelectRow = (item: T) => {\n if (!onSelectionChange || !selectedKeys) return;\n const key = String((item as Record<string, unknown>)[keyField]);\n const keys = new Set(selectedKeys);\n if (keys.has(key)) keys.delete(key);\n else keys.add(key);\n onSelectionChange(keys);\n };\n\n if (data.length === 0) {\n return (\n <div className=\"rounded-card border border-dashed border-border-seam bg-bg-surface/50\">\n <IllustratedEmptyState\n illustration={emptyIllustration}\n title={emptyMessage}\n description={emptyDescription}\n action={emptyAction}\n />\n </div>\n );\n }\n\n return (\n <div className={cn('space-y-3', className)}>\n <div className=\"overflow-x-auto rounded-card border border-border-seam bg-bg-surface shadow-elevation-1\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border-subtle bg-bg-sunken/50\">\n {selectable && (\n <th className=\"w-10 px-3 py-3\">\n <input\n type=\"checkbox\"\n checked={allPageKeysSelected && paged.length > 0}\n onChange={handleSelectAll}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </th>\n )}\n {columns.map((col) => {\n const isSortable = col.sortable !== false;\n const isActive = sortKey === col.key;\n return (\n <th\n key={col.key}\n className={cn(\n 'px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-text-secondary',\n isSortable && 'cursor-pointer select-none hover:text-text-primary',\n col.className\n )}\n onClick={isSortable ? () => handleSort(col.key) : undefined}\n >\n <span className=\"inline-flex items-center gap-1\">\n {col.header}\n {isSortable &&\n (isActive ? (\n sortDir === 'asc' ? (\n <ChevronUp className=\"h-3 w-3\" />\n ) : (\n <ChevronDown className=\"h-3 w-3\" />\n )\n ) : (\n <ChevronsUpDown className=\"h-3 w-3 opacity-30\" />\n ))}\n </span>\n </th>\n );\n })}\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border bg-bg-surface\">\n {paged.map((item) => {\n const rowKey = String((item as Record<string, unknown>)[keyField]);\n const isSelected = selectable && selectedKeys?.has(rowKey);\n return (\n <tr\n key={rowKey}\n className={cn(\n 'transition-colors',\n onRowClick && 'cursor-pointer hover:bg-bg-sunken/50',\n isSelected && 'bg-action-primary-bg/5'\n )}\n onClick={() => onRowClick?.(item)}\n >\n {selectable && (\n <td className=\"w-10 px-3 py-3\" onClick={(e) => e.stopPropagation()}>\n <input\n type=\"checkbox\"\n checked={isSelected ?? false}\n onChange={() => handleSelectRow(item)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </td>\n )}\n {columns.map((col) => (\n <td key={col.key} className={cn('px-4 py-3 text-sm text-text-primary', col.className)}>\n {col.render ? col.render(item) : String((item as Record<string, unknown>)[col.key] ?? '')}\n </td>\n ))}\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n\n {serverPagination ? (\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-subtle bg-bg-surface p-3 text-sm sm:flex-row sm:items-center sm:justify-between\">\n <span className=\"text-text-secondary\">Page {serverPagination.page}</span>\n <div className=\"flex flex-wrap items-center gap-2\">\n {serverPagination.onPageSizeChange ? (\n <select\n value={serverPagination.pageSize}\n onChange={(event) => serverPagination.onPageSizeChange?.(Number(event.target.value))}\n disabled={serverPagination.loading}\n className=\"h-8 rounded-md border border-border-subtle bg-bg-surface px-2 text-sm text-text-primary disabled:opacity-50\"\n aria-label=\"Rows per page\"\n >\n {[10, 25, 50, 100].map((option) => (\n <option key={option} value={option}>\n {option} / page\n </option>\n ))}\n </select>\n ) : null}\n <button\n type=\"button\"\n onClick={() => serverPagination.onPageChange(serverPagination.page - 1)}\n disabled={serverPagination.loading || serverPagination.page <= 1}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n Previous\n </button>\n <button\n type=\"button\"\n onClick={() => serverPagination.onPageChange(serverPagination.page + 1)}\n disabled={\n serverPagination.loading ||\n !(serverPagination.hasNextPage ?? sorted.length >= serverPagination.pageSize)\n }\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n Next\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ) : sorted.length > pageSize ? (\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">\n Showing {page * pageSize + 1}-{Math.min((page + 1) * pageSize, sorted.length)} of {sorted.length}\n </span>\n <div className=\"flex items-center gap-1\">\n <button\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n disabled={page === 0}\n className=\"rounded-md border border-border-subtle p-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n </button>\n {Array.from({ length: totalPages }).map((_, i) => (\n <button\n key={i}\n onClick={() => setPage(i)}\n className={cn(\n 'h-8 w-8 rounded-md text-xs font-medium transition-colors',\n i === page ? 'bg-foreground text-background' : 'text-text-secondary hover:bg-bg-sunken'\n )}\n >\n {i + 1}\n </button>\n ))}\n <button\n onClick={() => setPage((p) => Math.min(totalPages - 1, p + 1))}\n disabled={page >= totalPages - 1}\n className=\"rounded-md border border-border-subtle p-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,29 +1,36 @@
1
1
  import { cn as e } from "../utils/helpers.js";
2
2
  import { Inbox as t } from "lucide-react";
3
3
  import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
+ import { IllustratedEmptyState as i } from "@burdenoff/fe-libs/ui";
4
5
  //#region src/housingvista/components/EmptyState.tsx
5
- var i = ({ icon: i, title: a, description: o, action: s, className: c }) => /* @__PURE__ */ r("div", {
6
- className: e("flex flex-col items-center justify-center rounded-lg border border-dashed border-border-subtle bg-bg-surface/50 p-12 text-center", c),
6
+ var a = ({ icon: a, title: o, description: s, action: c, className: l, illustration: u }) => u ? /* @__PURE__ */ n(i, {
7
+ illustration: u,
8
+ title: o,
9
+ description: s,
10
+ action: c,
11
+ className: e("rounded-card border border-dashed border-border-seam bg-bg-surface/50 p-12", l)
12
+ }) : /* @__PURE__ */ r("div", {
13
+ className: e("flex flex-col items-center justify-center rounded-card border border-dashed border-border-seam bg-bg-surface/50 p-12 text-center", l),
7
14
  children: [
8
15
  /* @__PURE__ */ n("div", {
9
16
  className: "mb-4 text-text-secondary",
10
- children: i || /* @__PURE__ */ n(t, { className: "h-12 w-12" })
17
+ children: a || /* @__PURE__ */ n(t, { className: "h-12 w-12" })
11
18
  }),
12
19
  /* @__PURE__ */ n("h3", {
13
20
  className: "text-lg font-medium text-text-primary",
14
- children: a
21
+ children: o
15
22
  }),
16
- o && /* @__PURE__ */ n("p", {
23
+ s && /* @__PURE__ */ n("p", {
17
24
  className: "mt-1 text-sm text-text-secondary max-w-md",
18
- children: o
25
+ children: s
19
26
  }),
20
- s && /* @__PURE__ */ n("div", {
27
+ c && /* @__PURE__ */ n("div", {
21
28
  className: "mt-4",
22
- children: s
29
+ children: c
23
30
  })
24
31
  ]
25
32
  });
26
33
  //#endregion
27
- export { i as EmptyState };
34
+ export { a as EmptyState };
28
35
 
29
36
  //# sourceMappingURL=EmptyState.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"EmptyState.js","names":[],"sources":["../../../src/housingvista/components/EmptyState.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { Inbox } from 'lucide-react';\nimport { cn } from '../utils/helpers';\n\ninterface EmptyStateProps {\n icon?: ReactNode;\n title: string;\n description?: string;\n action?: ReactNode;\n className?: string;\n}\n\nexport const EmptyState: FC<EmptyStateProps> = ({ icon, title, description, action, className }) => {\n return (\n <div\n className={cn(\n 'flex flex-col items-center justify-center rounded-lg border border-dashed border-border-subtle bg-bg-surface/50 p-12 text-center',\n className\n )}\n >\n <div className=\"mb-4 text-text-secondary\">{icon || <Inbox className=\"h-12 w-12\" />}</div>\n <h3 className=\"text-lg font-medium text-text-primary\">{title}</h3>\n {description && <p className=\"mt-1 text-sm text-text-secondary max-w-md\">{description}</p>}\n {action && <div className=\"mt-4\">{action}</div>}\n </div>\n );\n};\n"],"mappings":";;;;AAYA,IAAa,KAAmC,EAAE,SAAM,UAAO,gBAAa,WAAQ,mBAEhF,kBAAC,OAAD;CACE,WAAW,EACT,oIACA,EACD;WAJH;EAME,kBAAC,OAAD;GAAK,WAAU;aAA4B,KAAQ,kBAAC,GAAD,EAAO,WAAU,aAAc,CAAA;GAAO,CAAA;EACzF,kBAAC,MAAD;GAAI,WAAU;aAAyC;GAAW,CAAA;EACjE,KAAe,kBAAC,KAAD;GAAG,WAAU;aAA6C;GAAgB,CAAA;EACzF,KAAU,kBAAC,OAAD;GAAK,WAAU;aAAQ;GAAa,CAAA;EAC3C"}
1
+ {"version":3,"file":"EmptyState.js","names":[],"sources":["../../../src/housingvista/components/EmptyState.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { Inbox } from 'lucide-react';\nimport { IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/helpers';\n\ninterface EmptyStateProps {\n icon?: ReactNode;\n title: string;\n description?: string;\n action?: ReactNode;\n className?: string;\n /**\n * Boff UI 2.0 — on-brand illustration name served by the host shell from\n * `/illustrations/<name>.webp`. When provided, renders the IllustratedEmptyState\n * primitive (preferred). Standard names: empty-data, empty-search, empty-files,\n * empty-people, empty-notifications, onboarding, empty-generic.\n */\n illustration?: string;\n}\n\nexport const EmptyState: FC<EmptyStateProps> = ({\n icon,\n title,\n description,\n action,\n className,\n illustration,\n}) => {\n if (illustration) {\n return (\n <IllustratedEmptyState\n illustration={illustration}\n title={title}\n description={description}\n action={action}\n className={cn('rounded-card border border-dashed border-border-seam bg-bg-surface/50 p-12', className)}\n />\n );\n }\n\n return (\n <div\n className={cn(\n 'flex flex-col items-center justify-center rounded-card border border-dashed border-border-seam bg-bg-surface/50 p-12 text-center',\n className\n )}\n >\n <div className=\"mb-4 text-text-secondary\">{icon || <Inbox className=\"h-12 w-12\" />}</div>\n <h3 className=\"text-lg font-medium text-text-primary\">{title}</h3>\n {description && <p className=\"mt-1 text-sm text-text-secondary max-w-md\">{description}</p>}\n {action && <div className=\"mt-4\">{action}</div>}\n </div>\n );\n};\n"],"mappings":";;;;;AAoBA,IAAa,KAAmC,EAC9C,SACA,UACA,gBACA,WACA,cACA,sBAEI,IAEA,kBAAC,GAAD;CACgB;CACP;CACM;CACL;CACR,WAAW,EAAG,8EAA8E,EAAU;CACtG,CAAA,GAKJ,kBAAC,OAAD;CACE,WAAW,EACT,oIACA,EACD;WAJH;EAME,kBAAC,OAAD;GAAK,WAAU;aAA4B,KAAQ,kBAAC,GAAD,EAAO,WAAU,aAAc,CAAA;GAAO,CAAA;EACzF,kBAAC,MAAD;GAAI,WAAU;aAAyC;GAAW,CAAA;EACjE,KAAe,kBAAC,KAAD;GAAG,WAAU;aAA6C;GAAgB,CAAA;EACzF,KAAU,kBAAC,OAAD;GAAK,WAAU;aAAQ;GAAa,CAAA;EAC3C"}
@@ -1,23 +1,30 @@
1
1
  import { cn as e } from "../utils/helpers.js";
2
2
  import { jsx as t, jsxs as n } from "react/jsx-runtime";
3
+ import { GradientText as r, PagePurpose as i } from "@burdenoff/fe-libs/ui";
3
4
  //#region src/housingvista/components/PageLayout.tsx
4
- var r = ({ title: r, description: i, actions: a, children: o, className: s }) => /* @__PURE__ */ n("div", {
5
- className: e("flex flex-col gap-6 p-6", s),
5
+ var a = ({ title: a, description: o, actions: s, children: c, className: l, purpose: u }) => /* @__PURE__ */ n("div", {
6
+ className: e("flex flex-col gap-6 p-6", l),
6
7
  children: [/* @__PURE__ */ n("div", {
7
- className: "flex items-start justify-between gap-4",
8
- children: [/* @__PURE__ */ n("div", { children: [/* @__PURE__ */ t("h1", {
9
- className: "text-2xl font-semibold text-text-primary",
10
- children: r
11
- }), i && /* @__PURE__ */ t("p", {
12
- className: "mt-1 text-sm text-text-secondary",
13
- children: i
14
- })] }), a && /* @__PURE__ */ t("div", {
15
- className: "flex items-center gap-2 shrink-0",
16
- children: a
8
+ className: "flex flex-col gap-2",
9
+ children: [/* @__PURE__ */ n("div", {
10
+ className: "flex items-start justify-between gap-4",
11
+ children: [/* @__PURE__ */ n("div", { children: [/* @__PURE__ */ t("h1", {
12
+ className: "text-2xl font-semibold text-text-primary",
13
+ children: /* @__PURE__ */ t(r, { children: a })
14
+ }), o && /* @__PURE__ */ t("p", {
15
+ className: "mt-1 text-sm text-text-secondary",
16
+ children: o
17
+ })] }), s && /* @__PURE__ */ t("div", {
18
+ className: "flex items-center gap-2 shrink-0",
19
+ children: s
20
+ })]
21
+ }), u && /* @__PURE__ */ t(i, {
22
+ className: "max-w-3xl",
23
+ children: u
17
24
  })]
18
- }), o]
25
+ }), c]
19
26
  });
20
27
  //#endregion
21
- export { r as PageLayout };
28
+ export { a as PageLayout };
22
29
 
23
30
  //# sourceMappingURL=PageLayout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/housingvista/components/PageLayout.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { cn } from '../utils/helpers';\n\ninterface PageLayoutProps {\n title: string;\n description?: string;\n actions?: ReactNode;\n children: ReactNode;\n className?: string;\n}\n\nexport const PageLayout: FC<PageLayoutProps> = ({ title, description, actions, children, className }) => {\n return (\n <div className={cn('flex flex-col gap-6 p-6', className)}>\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl font-semibold text-text-primary\">{title}</h1>\n {description && <p className=\"mt-1 text-sm text-text-secondary\">{description}</p>}\n </div>\n {actions && <div className=\"flex items-center gap-2 shrink-0\">{actions}</div>}\n </div>\n {children}\n </div>\n );\n};\n"],"mappings":";;;AAWA,IAAa,KAAmC,EAAE,UAAO,gBAAa,YAAS,aAAU,mBAErF,kBAAC,OAAD;CAAK,WAAW,EAAG,2BAA2B,EAAU;WAAxD,CACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;GAAI,WAAU;aAA4C;GAAW,CAAA,EACpE,KAAe,kBAAC,KAAD;GAAG,WAAU;aAAoC;GAAgB,CAAA,CAC7E,EAAA,CAAA,EACL,KAAW,kBAAC,OAAD;GAAK,WAAU;aAAoC;GAAc,CAAA,CACzE;KACL,EACG"}
1
+ {"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/housingvista/components/PageLayout.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { GradientText, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/helpers';\n\ninterface PageLayoutProps {\n title: string;\n description?: string;\n actions?: ReactNode;\n children: ReactNode;\n className?: string;\n /**\n * Boff UI 2.0 — a calm one/two-line \"story\" explaining WHAT this page is for\n * and the use-case, so a new user understands it at a glance. Rendered with\n * the {@link PagePurpose} primitive directly under the title.\n */\n purpose?: ReactNode;\n}\n\nexport const PageLayout: FC<PageLayoutProps> = ({\n title,\n description,\n actions,\n children,\n className,\n purpose,\n}) => {\n return (\n <div className={cn('flex flex-col gap-6 p-6', className)}>\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl font-semibold text-text-primary\">\n <GradientText>{title}</GradientText>\n </h1>\n {description && <p className=\"mt-1 text-sm text-text-secondary\">{description}</p>}\n </div>\n {actions && <div className=\"flex items-center gap-2 shrink-0\">{actions}</div>}\n </div>\n {purpose && <PagePurpose className=\"max-w-3xl\">{purpose}</PagePurpose>}\n </div>\n {children}\n </div>\n );\n};\n"],"mappings":";;;;AAkBA,IAAa,KAAmC,EAC9C,UACA,gBACA,YACA,aACA,cACA,iBAGE,kBAAC,OAAD;CAAK,WAAW,EAAG,2BAA2B,EAAU;WAAxD,CACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,GAAD,EAAA,UAAe,GAAqB,CAAA;IACjC,CAAA,EACJ,KAAe,kBAAC,KAAD;IAAG,WAAU;cAAoC;IAAgB,CAAA,CAC7E,EAAA,CAAA,EACL,KAAW,kBAAC,OAAD;IAAK,WAAU;cAAoC;IAAc,CAAA,CACzE;MACL,KAAW,kBAAC,GAAD;GAAa,WAAU;aAAa;GAAsB,CAAA,CAClE;KACL,EACG"}
@@ -19,7 +19,7 @@ var f = {
19
19
  }, p = ({ id: p, title: m, price: h, priceType: g, pricePeriod: _, location: v, bedrooms: y, bathrooms: b, area: x, imageUrl: S, status: C, type: w, onClick: T, className: E }) => {
20
20
  let [D, O] = i(!1), k = g === "rent" ? `${t(h)}${_ ? `/${_}` : "/mo"}` : t(h), A = f[C.toLowerCase()] ?? "default";
21
21
  return /* @__PURE__ */ d("div", {
22
- className: e("group overflow-hidden rounded-lg border border-border-subtle bg-bg-surface shadow-sm transition-shadow hover:shadow-md", T && "cursor-pointer", E),
22
+ className: e("group 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", T && "cursor-pointer", E),
23
23
  onClick: T,
24
24
  children: [/* @__PURE__ */ d("div", {
25
25
  className: "relative aspect-[16/10] overflow-hidden",
@@ -27,7 +27,7 @@ var f = {
27
27
  S ? /* @__PURE__ */ u("img", {
28
28
  src: S,
29
29
  alt: m,
30
- className: "h-full w-full object-cover transition-transform duration-300"
30
+ className: "h-full w-full object-cover"
31
31
  }) : /* @__PURE__ */ u("div", {
32
32
  className: "flex h-full w-full items-center justify-center bg-gradient-to-br from-muted to-muted/60",
33
33
  children: /* @__PURE__ */ u(l, { className: "h-10 w-10 text-text-secondary/40" })
@@ -1 +1 @@
1
- {"version":3,"file":"PropertyCard.js","names":[],"sources":["../../../src/housingvista/components/PropertyCard.tsx"],"sourcesContent":["import { useState, type FC } from 'react';\nimport { MapPin, Bed, Bath, Square, Heart } from 'lucide-react';\nimport { cn, formatCurrency, formatNumber } from '../utils/helpers';\nimport { StatusBadge } from './StatusBadge';\n\ninterface PropertyCardProps {\n id: string;\n title: string;\n price: number;\n priceType: 'sale' | 'rent';\n pricePeriod?: string;\n 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}\n\nconst statusVariantMap: Record<string, 'success' | 'warning' | 'error' | 'info' | 'default' | 'muted'> = {\n available: 'success',\n active: 'success',\n listed: 'success',\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\nexport const PropertyCard: FC<PropertyCardProps> = ({\n id,\n title,\n price,\n priceType,\n pricePeriod,\n location,\n bedrooms,\n bathrooms,\n area,\n imageUrl,\n status,\n type,\n onClick,\n className,\n}) => {\n const [isFavorited, setIsFavorited] = useState(false);\n\n const priceDisplay =\n priceType === 'rent' ? `${formatCurrency(price)}${pricePeriod ? `/${pricePeriod}` : '/mo'}` : formatCurrency(price);\n\n const badgeVariant = statusVariantMap[status.toLowerCase()] ?? 'default';\n\n return (\n <div\n className={cn(\n 'group overflow-hidden rounded-lg border border-border-subtle bg-bg-surface shadow-sm transition-shadow hover:shadow-md',\n onClick && 'cursor-pointer',\n className\n )}\n onClick={onClick}\n >\n {/* Image Section */}\n <div className=\"relative aspect-[16/10] overflow-hidden\">\n {imageUrl ? (\n <img\n src={imageUrl}\n alt={title}\n className=\"h-full w-full object-cover transition-transform duration-300\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center bg-gradient-to-br from-muted to-muted/60\">\n <Square className=\"h-10 w-10 text-text-secondary/40\" />\n </div>\n )}\n\n {/* Overlays */}\n <div className=\"absolute left-3 top-3\">\n <StatusBadge label={status} variant={badgeVariant} dot />\n </div>\n <button\n onClick={(e) => {\n e.stopPropagation();\n setIsFavorited((prev) => !prev);\n }}\n className={cn(\n 'absolute right-3 top-3 rounded-full bg-bg-surface/80 p-1.5 backdrop-blur-sm transition-colors hover:bg-bg-surface',\n isFavorited ? 'text-red-500' : 'text-text-secondary'\n )}\n aria-label={isFavorited ? 'Remove from favorites' : 'Add to favorites'}\n >\n <Heart className={cn('h-4 w-4', isFavorited && 'fill-current')} />\n </button>\n <div className=\"absolute bottom-3 left-3\">\n <span className=\"rounded-md bg-bg-surface/80 px-2 py-1 text-xs font-medium text-text-primary backdrop-blur-sm\">\n {type}\n </span>\n </div>\n </div>\n\n {/* Content Section */}\n <div className=\"p-4\">\n {/* Price */}\n <div className=\"flex items-baseline justify-between gap-2\">\n <span className=\"text-lg font-bold text-text-primary\">{priceDisplay}</span>\n {priceType === 'sale' && <span className=\"text-xs font-medium text-text-secondary\">For Sale</span>}\n </div>\n\n {/* Title */}\n <h3 className=\"mt-1 truncate text-sm font-medium text-text-primary\">{title}</h3>\n\n {/* Location */}\n <div className=\"mt-1.5 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-3 border-t border-border-subtle pt-3\">\n {bedrooms != null && (\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Bed className=\"h-3.5 w-3.5\" />\n <span>\n {bedrooms} {bedrooms === 1 ? 'Bed' : 'Beds'}\n </span>\n </div>\n )}\n {bathrooms != null && (\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Bath className=\"h-3.5 w-3.5\" />\n <span>\n {bathrooms} {bathrooms === 1 ? 'Bath' : 'Baths'}\n </span>\n </div>\n )}\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Square className=\"h-3.5 w-3.5\" />\n <span>{formatNumber(area)} sq ft</span>\n </div>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;AAsBA,IAAM,IAAmG;CACvG,WAAW;CACX,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,kBAAkB;CAClB,eAAe;CACf,MAAM;CACN,QAAQ;CACR,YAAY;CACZ,cAAc;CACd,OAAO;CACR,EAEY,KAAuC,EAClD,OACA,UACA,UACA,cACA,gBACA,aACA,aACA,cACA,SACA,aACA,WACA,SACA,YACA,mBACI;CACJ,IAAM,CAAC,GAAa,KAAkB,EAAS,GAAM,EAE/C,IACJ,MAAc,SAAS,GAAG,EAAe,EAAM,GAAG,IAAc,IAAI,MAAgB,UAAU,EAAe,EAAM,EAE/G,IAAe,EAAiB,EAAO,aAAa,KAAK;AAE/D,QACE,kBAAC,OAAD;EACE,WAAW,EACT,0HACA,KAAW,kBACX,EACD;EACQ;YANX,CASE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,IACC,kBAAC,OAAD;KACE,KAAK;KACL,KAAK;KACL,WAAU;KACV,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAQ,WAAU,oCAAqC,CAAA;KACnD,CAAA;IAIR,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAa,OAAO;MAAQ,SAAS;MAAc,KAAA;MAAM,CAAA;KACrD,CAAA;IACN,kBAAC,UAAD;KACE,UAAU,MAAM;AAEd,MADA,EAAE,iBAAiB,EACnB,GAAgB,MAAS,CAAC,EAAK;;KAEjC,WAAW,EACT,qHACA,IAAc,iBAAiB,sBAChC;KACD,cAAY,IAAc,0BAA0B;eAEpD,kBAAC,GAAD,EAAO,WAAW,EAAG,WAAW,KAAe,eAAe,EAAI,CAAA;KAC3D,CAAA;IACT,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,CACE,kBAAC,QAAD;MAAM,WAAU;gBAAuC;MAAoB,CAAA,EAC1E,MAAc,UAAU,kBAAC,QAAD;MAAM,WAAU;gBAA0C;MAAe,CAAA,CAC9F;;IAGN,kBAAC,MAAD;KAAI,WAAU;eAAuD;KAAW,CAAA;IAGhF,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,eAAgB,CAAA,EAC/B,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,eAAgB,CAAA,EAChC,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,eAAgB,CAAA,EAClC,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 { useState, type FC } from 'react';\nimport { MapPin, Bed, Bath, Square, Heart } from 'lucide-react';\nimport { cn, formatCurrency, formatNumber } from '../utils/helpers';\nimport { StatusBadge } from './StatusBadge';\n\ninterface PropertyCardProps {\n id: string;\n title: string;\n price: number;\n priceType: 'sale' | 'rent';\n pricePeriod?: string;\n 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}\n\nconst statusVariantMap: Record<string, 'success' | 'warning' | 'error' | 'info' | 'default' | 'muted'> = {\n available: 'success',\n active: 'success',\n listed: 'success',\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\nexport const PropertyCard: FC<PropertyCardProps> = ({\n id,\n title,\n price,\n priceType,\n pricePeriod,\n location,\n bedrooms,\n bathrooms,\n area,\n imageUrl,\n status,\n type,\n onClick,\n className,\n}) => {\n const [isFavorited, setIsFavorited] = useState(false);\n\n const priceDisplay =\n priceType === 'rent' ? `${formatCurrency(price)}${pricePeriod ? `/${pricePeriod}` : '/mo'}` : formatCurrency(price);\n\n const badgeVariant = statusVariantMap[status.toLowerCase()] ?? 'default';\n\n return (\n <div\n className={cn(\n 'group 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',\n onClick && 'cursor-pointer',\n className\n )}\n onClick={onClick}\n >\n {/* Image Section */}\n <div className=\"relative aspect-[16/10] overflow-hidden\">\n {imageUrl ? (\n <img\n src={imageUrl}\n alt={title}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center bg-gradient-to-br from-muted to-muted/60\">\n <Square className=\"h-10 w-10 text-text-secondary/40\" />\n </div>\n )}\n\n {/* Overlays */}\n <div className=\"absolute left-3 top-3\">\n <StatusBadge label={status} variant={badgeVariant} dot />\n </div>\n <button\n onClick={(e) => {\n e.stopPropagation();\n setIsFavorited((prev) => !prev);\n }}\n className={cn(\n 'absolute right-3 top-3 rounded-full bg-bg-surface/80 p-1.5 backdrop-blur-sm transition-colors hover:bg-bg-surface',\n isFavorited ? 'text-red-500' : 'text-text-secondary'\n )}\n aria-label={isFavorited ? 'Remove from favorites' : 'Add to favorites'}\n >\n <Heart className={cn('h-4 w-4', isFavorited && 'fill-current')} />\n </button>\n <div className=\"absolute bottom-3 left-3\">\n <span className=\"rounded-md bg-bg-surface/80 px-2 py-1 text-xs font-medium text-text-primary backdrop-blur-sm\">\n {type}\n </span>\n </div>\n </div>\n\n {/* Content Section */}\n <div className=\"p-4\">\n {/* Price */}\n <div className=\"flex items-baseline justify-between gap-2\">\n <span className=\"text-lg font-bold text-text-primary\">{priceDisplay}</span>\n {priceType === 'sale' && <span className=\"text-xs font-medium text-text-secondary\">For Sale</span>}\n </div>\n\n {/* Title */}\n <h3 className=\"mt-1 truncate text-sm font-medium text-text-primary\">{title}</h3>\n\n {/* Location */}\n <div className=\"mt-1.5 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-3 border-t border-border-subtle pt-3\">\n {bedrooms != null && (\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Bed className=\"h-3.5 w-3.5\" />\n <span>\n {bedrooms} {bedrooms === 1 ? 'Bed' : 'Beds'}\n </span>\n </div>\n )}\n {bathrooms != null && (\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Bath className=\"h-3.5 w-3.5\" />\n <span>\n {bathrooms} {bathrooms === 1 ? 'Bath' : 'Baths'}\n </span>\n </div>\n )}\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Square className=\"h-3.5 w-3.5\" />\n <span>{formatNumber(area)} sq ft</span>\n </div>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;AAsBA,IAAM,IAAmG;CACvG,WAAW;CACX,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,kBAAkB;CAClB,eAAe;CACf,MAAM;CACN,QAAQ;CACR,YAAY;CACZ,cAAc;CACd,OAAO;CACR,EAEY,KAAuC,EAClD,OACA,UACA,UACA,cACA,gBACA,aACA,aACA,cACA,SACA,aACA,WACA,SACA,YACA,mBACI;CACJ,IAAM,CAAC,GAAa,KAAkB,EAAS,GAAM,EAE/C,IACJ,MAAc,SAAS,GAAG,EAAe,EAAM,GAAG,IAAc,IAAI,MAAgB,UAAU,EAAe,EAAM,EAE/G,IAAe,EAAiB,EAAO,aAAa,KAAK;AAE/D,QACE,kBAAC,OAAD;EACE,WAAW,EACT,0LACA,KAAW,kBACX,EACD;EACQ;YANX,CASE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,IACC,kBAAC,OAAD;KACE,KAAK;KACL,KAAK;KACL,WAAU;KACV,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAQ,WAAU,oCAAqC,CAAA;KACnD,CAAA;IAIR,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAa,OAAO;MAAQ,SAAS;MAAc,KAAA;MAAM,CAAA;KACrD,CAAA;IACN,kBAAC,UAAD;KACE,UAAU,MAAM;AAEd,MADA,EAAE,iBAAiB,EACnB,GAAgB,MAAS,CAAC,EAAK;;KAEjC,WAAW,EACT,qHACA,IAAc,iBAAiB,sBAChC;KACD,cAAY,IAAc,0BAA0B;eAEpD,kBAAC,GAAD,EAAO,WAAW,EAAG,WAAW,KAAe,eAAe,EAAI,CAAA;KAC3D,CAAA;IACT,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,CACE,kBAAC,QAAD;MAAM,WAAU;gBAAuC;MAAoB,CAAA,EAC1E,MAAc,UAAU,kBAAC,QAAD;MAAM,WAAU;gBAA0C;MAAe,CAAA,CAC9F;;IAGN,kBAAC,MAAD;KAAI,WAAU;eAAuD;KAAW,CAAA;IAGhF,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,eAAgB,CAAA,EAC/B,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,eAAgB,CAAA,EAChC,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,eAAgB,CAAA,EAClC,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAa,EAAK,EAAC,SAAa,EAAA,CAAA,CACnC;;MACF;;IACF;KACF"}
@@ -1,40 +1,43 @@
1
1
  import { cn as e, formatCurrency as t, formatNumber as n } from "../utils/helpers.js";
2
2
  import { Minus as r, TrendingDown as i, TrendingUp as a } from "lucide-react";
3
3
  import { jsx as o, jsxs as s } from "react/jsx-runtime";
4
+ import { GlassCard as c } from "@burdenoff/fe-libs/ui";
4
5
  //#region src/housingvista/components/StatCard.tsx
5
- var c = ({ label: c, value: l, previousValue: u, unit: d, prefix: f, icon: p, trend: m, change: h, className: g }) => {
6
- let _ = h ?? (u ? Math.round(((typeof l == "number" ? l : 0) - u) / u * 100) : 0), v = m ?? (_ > 0 ? "up" : _ < 0 ? "down" : "stable"), y = typeof l == "string" ? l : f === "$" ? t(l) : `${n(l)}${d ? ` ${d}` : ""}`;
7
- return /* @__PURE__ */ s("div", {
8
- className: e("rounded-lg border border-border-subtle bg-bg-surface p-4 shadow-sm", g),
6
+ var l = ({ label: l, value: u, previousValue: d, unit: f, prefix: p, icon: m, trend: h, change: g, className: _, featured: v = !1 }) => {
7
+ let y = g ?? (d ? Math.round(((typeof u == "number" ? u : 0) - d) / d * 100) : 0), b = h ?? (y > 0 ? "up" : y < 0 ? "down" : "stable"), x = typeof u == "string" ? u : p === "$" ? t(u) : `${n(u)}${f ? ` ${f}` : ""}`;
8
+ return /* @__PURE__ */ s(c, {
9
+ treatment: v ? "glass" : "metric",
10
+ glow: v,
11
+ className: e("border-border-seam p-4", _),
9
12
  children: [
10
13
  /* @__PURE__ */ s("div", {
11
14
  className: "flex items-center justify-between",
12
15
  children: [/* @__PURE__ */ o("span", {
13
16
  className: "text-sm font-medium text-text-secondary",
14
- children: c
15
- }), p && /* @__PURE__ */ o("span", {
17
+ children: l
18
+ }), m && /* @__PURE__ */ o("span", {
16
19
  className: "text-text-secondary",
17
- children: p
20
+ children: m
18
21
  })]
19
22
  }),
20
23
  /* @__PURE__ */ o("div", {
21
24
  className: "mt-2 flex items-baseline gap-2",
22
25
  children: /* @__PURE__ */ o("span", {
23
- className: "text-2xl font-bold text-text-primary",
24
- children: y
26
+ className: "text-2xl font-bold tabular-nums text-text-primary",
27
+ children: x
25
28
  })
26
29
  }),
27
- _ !== 0 && /* @__PURE__ */ s("div", {
30
+ y !== 0 && /* @__PURE__ */ s("div", {
28
31
  className: "mt-2 flex items-center gap-1 text-xs",
29
32
  children: [
30
- v === "up" && /* @__PURE__ */ o(a, { className: "h-3 w-3 text-emerald-600 dark:text-emerald-400" }),
31
- v === "down" && /* @__PURE__ */ o(i, { className: "h-3 w-3 text-red-600 dark:text-red-400" }),
32
- v === "stable" && /* @__PURE__ */ o(r, { className: "h-3 w-3 text-text-secondary" }),
33
+ b === "up" && /* @__PURE__ */ o(a, { className: "h-3 w-3 text-emerald-600 dark:text-emerald-400" }),
34
+ b === "down" && /* @__PURE__ */ o(i, { className: "h-3 w-3 text-red-600 dark:text-red-400" }),
35
+ b === "stable" && /* @__PURE__ */ o(r, { className: "h-3 w-3 text-text-secondary" }),
33
36
  /* @__PURE__ */ s("span", {
34
- className: e("font-medium", v === "up" && "text-emerald-600 dark:text-emerald-400", v === "down" && "text-red-600 dark:text-red-400", v === "stable" && "text-text-secondary"),
37
+ className: e("font-medium tabular-nums", b === "up" && "text-emerald-600 dark:text-emerald-400", b === "down" && "text-red-600 dark:text-red-400", b === "stable" && "text-text-secondary"),
35
38
  children: [
36
- _ > 0 ? "+" : "",
37
- _,
39
+ y > 0 ? "+" : "",
40
+ y,
38
41
  "%"
39
42
  ]
40
43
  }),
@@ -48,6 +51,6 @@ var c = ({ label: c, value: l, previousValue: u, unit: d, prefix: f, icon: p, tr
48
51
  });
49
52
  };
50
53
  //#endregion
51
- export { c as StatCard };
54
+ export { l as StatCard };
52
55
 
53
56
  //# sourceMappingURL=StatCard.js.map