@burdenoff/microfe-housingvista 2026.625.1 → 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.
- package/dist/housingvista/components/DataTable.js +62 -59
- package/dist/housingvista/components/DataTable.js.map +1 -1
- package/dist/housingvista/components/PageLayout.js +21 -15
- package/dist/housingvista/components/PageLayout.js.map +1 -1
- package/dist/housingvista/pages/HomePage.js +66 -39
- package/dist/housingvista/pages/HomePage.js.map +1 -1
- package/dist/housingvista/pages/amc/AMCManagementPage.js +58 -48
- package/dist/housingvista/pages/amc/AMCManagementPage.js.map +1 -1
- package/dist/housingvista/pages/analytics/AnalyticsPage.js +113 -90
- package/dist/housingvista/pages/analytics/AnalyticsPage.js.map +1 -1
- package/dist/housingvista/pages/assets/AssetManagementPage.js +65 -60
- package/dist/housingvista/pages/assets/AssetManagementPage.js.map +1 -1
- package/dist/housingvista/pages/chat/ChatPage.js +59 -58
- package/dist/housingvista/pages/chat/ChatPage.js.map +1 -1
- package/dist/housingvista/pages/finance/HomeLoansPage.js +22 -20
- package/dist/housingvista/pages/finance/HomeLoansPage.js.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentDetailPage.js +112 -81
- package/dist/housingvista/pages/investments/InvestmentDetailPage.js.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentsPage.js +64 -59
- package/dist/housingvista/pages/investments/InvestmentsPage.js.map +1 -1
- package/dist/housingvista/pages/legal/LegalPage.js +16 -14
- package/dist/housingvista/pages/legal/LegalPage.js.map +1 -1
- package/dist/housingvista/pages/market/AreaInsightsPage.js +12 -10
- package/dist/housingvista/pages/market/AreaInsightsPage.js.map +1 -1
- package/dist/housingvista/pages/market/MarketAnalyticsPage.js +155 -123
- package/dist/housingvista/pages/market/MarketAnalyticsPage.js.map +1 -1
- package/dist/housingvista/pages/marketplace/MarketplacePage.js +14 -12
- package/dist/housingvista/pages/marketplace/MarketplacePage.js.map +1 -1
- package/dist/housingvista/pages/notifications/NotificationsPage.js +23 -21
- package/dist/housingvista/pages/notifications/NotificationsPage.js.map +1 -1
- package/dist/housingvista/pages/properties/AddPropertyPage.js +73 -71
- package/dist/housingvista/pages/properties/AddPropertyPage.js.map +1 -1
- package/dist/housingvista/pages/properties/PropertiesPage.js +140 -135
- package/dist/housingvista/pages/properties/PropertiesPage.js.map +1 -1
- package/dist/housingvista/pages/properties/PropertyDetailPage.js +191 -154
- package/dist/housingvista/pages/properties/PropertyDetailPage.js.map +1 -1
- package/dist/housingvista/pages/rental/RentalManagementPage.js +185 -161
- package/dist/housingvista/pages/rental/RentalManagementPage.js.map +1 -1
- package/dist/housingvista/pages/settings/ProfilePage.js +51 -49
- package/dist/housingvista/pages/settings/ProfilePage.js.map +1 -1
- package/dist/housingvista/pages/settings/SettingsPage.js +56 -54
- package/dist/housingvista/pages/settings/SettingsPage.js.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionDetailPage.js +182 -142
- package/dist/housingvista/pages/transactions/TransactionDetailPage.js.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionsPage.js +89 -84
- package/dist/housingvista/pages/transactions/TransactionsPage.js.map +1 -1
- package/dist/housingvista/pages/verification/VerificationPage.js +49 -42
- package/dist/housingvista/pages/verification/VerificationPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -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
|
|
7
|
-
let [
|
|
8
|
-
|
|
9
|
-
}, [
|
|
10
|
-
let n = e[
|
|
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
|
|
16
|
+
return O === "asc" ? i : -i;
|
|
16
17
|
}), [
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
]),
|
|
21
|
-
if (!
|
|
22
|
-
let e = new Set(
|
|
23
|
-
|
|
24
|
-
},
|
|
25
|
-
if (!
|
|
26
|
-
let t = String(e[
|
|
27
|
-
n.has(t) ? n.delete(t) : n.add(t),
|
|
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
|
|
30
|
-
className: "
|
|
31
|
-
children: /* @__PURE__ */ l(
|
|
32
|
-
|
|
33
|
-
|
|
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",
|
|
39
|
+
className: e("space-y-3", b),
|
|
37
40
|
children: [/* @__PURE__ */ l("div", {
|
|
38
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: [
|
|
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:
|
|
48
|
-
onChange:
|
|
50
|
+
checked: L && I.length > 0,
|
|
51
|
+
onChange: R,
|
|
49
52
|
className: "h-4 w-4 rounded border-border-subtle"
|
|
50
53
|
})
|
|
51
|
-
}),
|
|
52
|
-
let n = t.sortable !== !1, r =
|
|
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 ? () =>
|
|
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(
|
|
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:
|
|
65
|
-
let n = String(t[
|
|
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",
|
|
68
|
-
onClick: () =>
|
|
69
|
-
children: [
|
|
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: () =>
|
|
78
|
+
onChange: () => z(t),
|
|
76
79
|
className: "h-4 w-4 rounded border-border-subtle"
|
|
77
80
|
})
|
|
78
|
-
}),
|
|
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
|
-
}),
|
|
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 ",
|
|
93
|
+
children: ["Page ", T.page]
|
|
91
94
|
}), /* @__PURE__ */ u("div", {
|
|
92
95
|
className: "flex flex-wrap items-center gap-2",
|
|
93
96
|
children: [
|
|
94
|
-
|
|
95
|
-
value:
|
|
96
|
-
onChange: (e) =>
|
|
97
|
-
disabled:
|
|
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: () =>
|
|
113
|
-
disabled:
|
|
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: () =>
|
|
120
|
-
disabled:
|
|
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
|
-
}) :
|
|
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
|
-
|
|
135
|
+
A * x + 1,
|
|
133
136
|
"-",
|
|
134
|
-
Math.min((
|
|
137
|
+
Math.min((A + 1) * x, N.length),
|
|
135
138
|
" of ",
|
|
136
|
-
|
|
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: () =>
|
|
143
|
-
disabled:
|
|
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:
|
|
148
|
-
onClick: () =>
|
|
149
|
-
className: e("h-8 w-8 rounded-md text-xs font-medium transition-colors", n ===
|
|
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: () =>
|
|
154
|
-
disabled:
|
|
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 {
|
|
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-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
|
+
{"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,24 +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 } from "@burdenoff/fe-libs/ui";
|
|
3
|
+
import { GradientText as r, PagePurpose as i } from "@burdenoff/fe-libs/ui";
|
|
4
4
|
//#region src/housingvista/components/PageLayout.tsx
|
|
5
|
-
var
|
|
6
|
-
className: e("flex flex-col gap-6 p-6",
|
|
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),
|
|
7
7
|
children: [/* @__PURE__ */ n("div", {
|
|
8
|
-
className: "flex
|
|
9
|
-
children: [/* @__PURE__ */ n("div", {
|
|
10
|
-
className: "
|
|
11
|
-
children: /* @__PURE__ */
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
18
24
|
})]
|
|
19
|
-
}),
|
|
25
|
+
}), c]
|
|
20
26
|
});
|
|
21
27
|
//#endregion
|
|
22
|
-
export {
|
|
28
|
+
export { a as PageLayout };
|
|
23
29
|
|
|
24
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 { GradientText } 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\nexport const PageLayout: FC<PageLayoutProps> = ({
|
|
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"}
|
|
@@ -8,9 +8,9 @@ import { ArrowRight as l, BarChart3 as u, Building as d, DollarSign as f, Eye as
|
|
|
8
8
|
import { useNavigate as v } from "react-router-dom";
|
|
9
9
|
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
10
10
|
import { useI18n as x } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
11
|
-
import { AuroraBackground as S, Button as C, EmphasisPanel as w, GlassCard as T, GradientText as E, IllustratedEmptyState as D } from "@burdenoff/fe-libs/ui";
|
|
11
|
+
import { AuroraBackground as S, Button as C, EmphasisPanel as w, GlassCard as T, GradientText as E, IllustratedEmptyState as D, NextSteps as O, PagePurpose as k } from "@burdenoff/fe-libs/ui";
|
|
12
12
|
//#region src/housingvista/pages/HomePage.tsx
|
|
13
|
-
var
|
|
13
|
+
var A = {
|
|
14
14
|
property_listed: h,
|
|
15
15
|
transaction_update: m,
|
|
16
16
|
payment_received: f,
|
|
@@ -19,7 +19,7 @@ var O = {
|
|
|
19
19
|
document_uploaded: m,
|
|
20
20
|
offer_received: _,
|
|
21
21
|
lease_signed: m
|
|
22
|
-
},
|
|
22
|
+
}, j = {
|
|
23
23
|
property_listed: "text-purple-600 dark:text-purple-400",
|
|
24
24
|
transaction_update: "text-blue-600 dark:text-blue-400",
|
|
25
25
|
payment_received: "text-emerald-600 dark:text-emerald-400",
|
|
@@ -28,36 +28,36 @@ var O = {
|
|
|
28
28
|
document_uploaded: "text-indigo-600 dark:text-indigo-400",
|
|
29
29
|
offer_received: "text-emerald-600 dark:text-emerald-400",
|
|
30
30
|
lease_signed: "text-teal-600 dark:text-teal-400"
|
|
31
|
-
},
|
|
32
|
-
let h = v(), { t:
|
|
33
|
-
let n =
|
|
31
|
+
}, M = () => {
|
|
32
|
+
let h = v(), { t: M } = x(), { data: N } = a(), { data: P } = s(), { data: F } = o(), I = (e, t) => {
|
|
33
|
+
let n = M(e);
|
|
34
34
|
return n === e ? t : n;
|
|
35
|
-
},
|
|
35
|
+
}, L = [
|
|
36
36
|
{
|
|
37
|
-
title:
|
|
38
|
-
description:
|
|
37
|
+
title: I("housingvista.home.quickActions.addProperty.title", "Add Property"),
|
|
38
|
+
description: I("housingvista.home.quickActions.addProperty.description", "List a new property"),
|
|
39
39
|
icon: g,
|
|
40
40
|
path: "properties/new"
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
|
-
title:
|
|
44
|
-
description:
|
|
43
|
+
title: I("housingvista.home.quickActions.viewAnalytics.title", "View Analytics"),
|
|
44
|
+
description: I("housingvista.home.quickActions.viewAnalytics.description", "Check performance metrics"),
|
|
45
45
|
icon: u,
|
|
46
46
|
path: "analytics"
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
|
-
title:
|
|
50
|
-
description:
|
|
49
|
+
title: I("housingvista.home.quickActions.manageTransactions.title", "Manage Transactions"),
|
|
50
|
+
description: I("housingvista.home.quickActions.manageTransactions.description", "Handle ongoing deals"),
|
|
51
51
|
icon: m,
|
|
52
52
|
path: "transactions"
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
|
-
title:
|
|
56
|
-
description:
|
|
55
|
+
title: I("housingvista.home.quickActions.browseProperties.title", "Browse Properties"),
|
|
56
|
+
description: I("housingvista.home.quickActions.browseProperties.description", "Explore listings"),
|
|
57
57
|
icon: d,
|
|
58
58
|
path: "properties"
|
|
59
59
|
}
|
|
60
|
-
],
|
|
60
|
+
], R = c(() => F.slice(0, 6), [F]);
|
|
61
61
|
return /* @__PURE__ */ b("div", {
|
|
62
62
|
className: "space-y-6 p-6",
|
|
63
63
|
children: [
|
|
@@ -67,45 +67,72 @@ var O = {
|
|
|
67
67
|
className: "relative flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between",
|
|
68
68
|
children: [/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("h1", {
|
|
69
69
|
className: "text-3xl font-bold text-text-primary",
|
|
70
|
-
children: /* @__PURE__ */ y(E, { children:
|
|
70
|
+
children: /* @__PURE__ */ y(E, { children: I("housingvista.home.header.title", "Welcome back!") })
|
|
71
71
|
}), /* @__PURE__ */ y("p", {
|
|
72
72
|
className: "mt-1 text-text-secondary",
|
|
73
|
-
children:
|
|
73
|
+
children: I("housingvista.home.header.description", "Here's what's happening with your real estate portfolio today.")
|
|
74
74
|
})] }), /* @__PURE__ */ b(C, {
|
|
75
75
|
onClick: () => h("properties/new"),
|
|
76
76
|
className: "gap-2 self-start lg:self-auto",
|
|
77
|
-
children: [/* @__PURE__ */ y(g, { className: "h-4 w-4" }),
|
|
77
|
+
children: [/* @__PURE__ */ y(g, { className: "h-4 w-4" }), I("housingvista.home.header.quickAdd", "Quick Add")]
|
|
78
78
|
})]
|
|
79
79
|
})]
|
|
80
80
|
}),
|
|
81
|
+
/* @__PURE__ */ y(k, {
|
|
82
|
+
className: "max-w-3xl",
|
|
83
|
+
children: I("housingvista.home.purpose", "Your real-estate command center. See your portfolio at a glance, jump to the things you do most, and pick up where you left off — from listing a property to tracking deals and analytics.")
|
|
84
|
+
}),
|
|
85
|
+
/* @__PURE__ */ y(O, {
|
|
86
|
+
storageKey: "housingvista-home",
|
|
87
|
+
steps: [
|
|
88
|
+
{
|
|
89
|
+
id: "add-property",
|
|
90
|
+
label: I("housingvista.home.nextSteps.addProperty", "List your first property"),
|
|
91
|
+
description: I("housingvista.home.nextSteps.addPropertyDesc", "Create a market-ready listing in a few guided steps."),
|
|
92
|
+
onClick: () => h("properties/new")
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: "browse",
|
|
96
|
+
label: I("housingvista.home.nextSteps.browse", "Browse properties"),
|
|
97
|
+
description: I("housingvista.home.nextSteps.browseDesc", "Explore listings and open one for full details."),
|
|
98
|
+
onClick: () => h("properties")
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: "analytics",
|
|
102
|
+
label: I("housingvista.home.nextSteps.analytics", "Review your analytics"),
|
|
103
|
+
description: I("housingvista.home.nextSteps.analyticsDesc", "Check portfolio performance and market insights."),
|
|
104
|
+
onClick: () => h("analytics")
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
}),
|
|
81
108
|
/* @__PURE__ */ b("div", {
|
|
82
109
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
83
110
|
children: [
|
|
84
111
|
/* @__PURE__ */ y(r, {
|
|
85
|
-
label:
|
|
86
|
-
value:
|
|
112
|
+
label: I("housingvista.home.stats.totalProperties", "Total Properties"),
|
|
113
|
+
value: N.totalProperties,
|
|
87
114
|
icon: /* @__PURE__ */ y(d, { className: "h-5 w-5" }),
|
|
88
115
|
trend: "up",
|
|
89
116
|
change: 12,
|
|
90
117
|
featured: !0
|
|
91
118
|
}),
|
|
92
119
|
/* @__PURE__ */ y(r, {
|
|
93
|
-
label:
|
|
94
|
-
value:
|
|
120
|
+
label: I("housingvista.home.stats.activeListings", "Active Listings"),
|
|
121
|
+
value: N.activeTransactions,
|
|
95
122
|
icon: /* @__PURE__ */ y(_, { className: "h-5 w-5" }),
|
|
96
123
|
trend: "up",
|
|
97
124
|
change: 8
|
|
98
125
|
}),
|
|
99
126
|
/* @__PURE__ */ y(r, {
|
|
100
|
-
label:
|
|
127
|
+
label: I("housingvista.home.stats.totalViews", "Total Views"),
|
|
101
128
|
value: t(12540),
|
|
102
129
|
icon: /* @__PURE__ */ y(p, { className: "h-5 w-5" }),
|
|
103
130
|
trend: "down",
|
|
104
131
|
change: -2
|
|
105
132
|
}),
|
|
106
133
|
/* @__PURE__ */ y(r, {
|
|
107
|
-
label:
|
|
108
|
-
value:
|
|
134
|
+
label: I("housingvista.home.stats.monthlyRevenue", "Monthly Revenue"),
|
|
135
|
+
value: N.monthlyRevenue,
|
|
109
136
|
prefix: "$",
|
|
110
137
|
icon: /* @__PURE__ */ y(f, { className: "h-5 w-5" }),
|
|
111
138
|
trend: "up",
|
|
@@ -124,11 +151,11 @@ var O = {
|
|
|
124
151
|
className: "border-b border-border-seam px-5 py-4",
|
|
125
152
|
children: /* @__PURE__ */ y("h2", {
|
|
126
153
|
className: "text-base font-semibold text-text-primary",
|
|
127
|
-
children:
|
|
154
|
+
children: I("housingvista.home.quickActions.title", "Quick Actions")
|
|
128
155
|
})
|
|
129
156
|
}), /* @__PURE__ */ y("div", {
|
|
130
157
|
className: "space-y-2 p-4",
|
|
131
|
-
children:
|
|
158
|
+
children: L.map((e) => {
|
|
132
159
|
let t = e.icon;
|
|
133
160
|
return /* @__PURE__ */ b("button", {
|
|
134
161
|
onClick: () => h(e.path),
|
|
@@ -158,15 +185,15 @@ var O = {
|
|
|
158
185
|
className: "border-b border-border-seam px-5 py-4",
|
|
159
186
|
children: /* @__PURE__ */ y("h2", {
|
|
160
187
|
className: "text-base font-semibold text-text-primary",
|
|
161
|
-
children:
|
|
188
|
+
children: I("housingvista.home.recentActivity.title", "Recent Activity")
|
|
162
189
|
})
|
|
163
190
|
}), /* @__PURE__ */ y("div", {
|
|
164
191
|
className: "space-y-2 p-4",
|
|
165
|
-
children:
|
|
192
|
+
children: P.slice(0, 6).map((t) => /* @__PURE__ */ b("div", {
|
|
166
193
|
className: "flex items-start gap-4 rounded-lg border border-border-seam bg-bg-surface/60 p-4 transition-colors hover:bg-bg-sunken/50",
|
|
167
194
|
children: [/* @__PURE__ */ y("div", {
|
|
168
195
|
className: "rounded-lg bg-bg-sunken p-2",
|
|
169
|
-
children: /* @__PURE__ */ y(
|
|
196
|
+
children: /* @__PURE__ */ y(A[t.type] ?? p, { className: e("h-5 w-5", j[t.type] ?? "text-text-secondary") })
|
|
170
197
|
}), /* @__PURE__ */ b("div", {
|
|
171
198
|
className: "flex-1 min-w-0",
|
|
172
199
|
children: [
|
|
@@ -196,27 +223,27 @@ var O = {
|
|
|
196
223
|
className: "flex items-center justify-between border-b border-border-seam px-5 py-4",
|
|
197
224
|
children: [/* @__PURE__ */ y("h2", {
|
|
198
225
|
className: "text-base font-semibold text-text-primary",
|
|
199
|
-
children:
|
|
226
|
+
children: I("housingvista.home.recentProperties.title", "Recent Properties")
|
|
200
227
|
}), /* @__PURE__ */ b("button", {
|
|
201
228
|
onClick: () => h("properties"),
|
|
202
229
|
className: "inline-flex min-h-[44px] items-center gap-1 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] rounded-md px-1",
|
|
203
|
-
children: [
|
|
230
|
+
children: [I("housingvista.home.recentProperties.viewAll", "View All"), /* @__PURE__ */ y(l, { className: "h-4 w-4" })]
|
|
204
231
|
})]
|
|
205
|
-
}),
|
|
232
|
+
}), R.length === 0 ? /* @__PURE__ */ y("div", {
|
|
206
233
|
className: "p-4",
|
|
207
234
|
children: /* @__PURE__ */ y(D, {
|
|
208
235
|
illustration: "empty-data",
|
|
209
|
-
title:
|
|
210
|
-
description:
|
|
236
|
+
title: I("housingvista.home.recentProperties.empty.title", "No properties yet"),
|
|
237
|
+
description: I("housingvista.home.recentProperties.empty.description", "List your first property to see it appear here."),
|
|
211
238
|
action: /* @__PURE__ */ b(C, {
|
|
212
239
|
onClick: () => h("properties/new"),
|
|
213
240
|
className: "gap-2",
|
|
214
|
-
children: [/* @__PURE__ */ y(g, { className: "h-4 w-4" }),
|
|
241
|
+
children: [/* @__PURE__ */ y(g, { className: "h-4 w-4" }), I("housingvista.home.header.quickAdd", "Quick Add")]
|
|
215
242
|
})
|
|
216
243
|
})
|
|
217
244
|
}) : /* @__PURE__ */ y("div", {
|
|
218
245
|
className: "grid grid-cols-1 gap-4 p-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
219
|
-
children:
|
|
246
|
+
children: R.map((e) => /* @__PURE__ */ y(i, {
|
|
220
247
|
id: e.id,
|
|
221
248
|
title: e.title,
|
|
222
249
|
price: e.price.amount,
|
|
@@ -237,6 +264,6 @@ var O = {
|
|
|
237
264
|
});
|
|
238
265
|
};
|
|
239
266
|
//#endregion
|
|
240
|
-
export {
|
|
267
|
+
export { M as HomePage };
|
|
241
268
|
|
|
242
269
|
//# sourceMappingURL=HomePage.js.map
|