@burdenoff/microfe-housingvista 2026.625.2 → 2026.626.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/pages/amc/AMCManagementPage.js +109 -82
- package/dist/housingvista/pages/amc/AMCManagementPage.js.map +1 -1
- package/dist/housingvista/pages/assets/AssetManagementPage.js +57 -31
- package/dist/housingvista/pages/assets/AssetManagementPage.js.map +1 -1
- package/dist/housingvista/pages/finance/HomeLoansPage.js +190 -162
- package/dist/housingvista/pages/finance/HomeLoansPage.js.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentsPage.js +57 -31
- package/dist/housingvista/pages/investments/InvestmentsPage.js.map +1 -1
- package/dist/housingvista/pages/legal/LegalPage.js +36 -13
- package/dist/housingvista/pages/legal/LegalPage.js.map +1 -1
- package/dist/housingvista/pages/marketplace/MarketplacePage.js +36 -11
- package/dist/housingvista/pages/marketplace/MarketplacePage.js.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionsPage.js +95 -69
- package/dist/housingvista/pages/transactions/TransactionsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarketplacePage.js","names":[],"sources":["../../../../src/housingvista/pages/marketplace/MarketplacePage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Star,\n Phone,\n MapPin,\n Shield,\n Wrench,\n Zap,\n SprayCanIcon,\n Paintbrush,\n ShieldCheck,\n Trees,\n CheckCircle,\n Clock,\n MessageCircle,\n} from 'lucide-react';\nimport { EmphasisPanel } from '@burdenoff/fe-libs/ui';\nimport { PageLayout, FilterBar, EmptyState } from '../../components';\nimport { useMarketplaceServices } from '../../hooks';\nimport { cn, formatCurrency } from '../../utils/helpers';\nimport type { MarketplaceService } from '../../types';\n\nconst categories = [\n { value: 'all', label: 'All' },\n { value: 'repair', label: 'Plumbing' },\n { value: 'maintenance', label: 'Electrical' },\n { value: 'cleaning', label: 'Cleaning' },\n { value: 'renovation', label: 'Painting' },\n { value: 'security', label: 'Security' },\n { value: 'landscaping', label: 'Landscaping' },\n] as const;\n\nfunction getCategoryIcon(category: string) {\n switch (category) {\n case 'repair':\n return <Wrench className=\"h-4 w-4\" />;\n case 'maintenance':\n return <Zap className=\"h-4 w-4\" />;\n case 'cleaning':\n return <SprayCanIcon className=\"h-4 w-4\" />;\n case 'renovation':\n return <Paintbrush className=\"h-4 w-4\" />;\n case 'security':\n return <Shield className=\"h-4 w-4\" />;\n case 'landscaping':\n return <Trees className=\"h-4 w-4\" />;\n case 'pest_control':\n return <ShieldCheck className=\"h-4 w-4\" />;\n default:\n return <Wrench className=\"h-4 w-4\" />;\n }\n}\n\nfunction RatingStars({ rating }: { rating: number }) {\n return (\n <div className=\"flex items-center gap-0.5\">\n {Array.from({ length: 5 }).map((_, i) => (\n <Star\n key={i}\n className={cn(\n 'h-3.5 w-3.5',\n i < Math.floor(rating)\n ? 'fill-amber-400 text-amber-400'\n : i < rating\n ? 'fill-amber-400/50 text-amber-400'\n : 'text-text-secondary/30'\n )}\n />\n ))}\n </div>\n );\n}\n\nfunction ServiceCard({ service }: { service: MarketplaceService }) {\n const navigate = useNavigate();\n\n return (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5 shadow-sm transition-shadow hover:shadow-md\">\n {/* Image + Featured Badge */}\n <div className=\"relative mb-4 h-40 overflow-hidden rounded-md bg-bg-sunken\">\n <img src={service.imageUrl} alt={service.name} className=\"h-full w-full object-cover\" />\n {service.featured && (\n <span className=\"absolute left-2 top-2 rounded-full bg-amber-500 px-2 py-0.5 text-xs font-semibold text-white\">\n Featured\n </span>\n )}\n </div>\n\n {/* Header */}\n <div className=\"mb-2 flex items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">{service.name}</h3>\n <p className=\"text-xs text-text-secondary\">{service.providerName}</p>\n </div>\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary capitalize\">\n {getCategoryIcon(service.category)}\n {service.category.replace('_', ' ')}\n </span>\n </div>\n\n {/* Rating */}\n <div className=\"mb-3 flex items-center gap-2\">\n <RatingStars rating={service.rating} />\n <span className=\"text-xs font-medium text-text-primary\">{service.rating}</span>\n <span className=\"text-xs text-text-secondary\">({service.reviewCount} reviews)</span>\n </div>\n\n {/* Description */}\n <p className=\"mb-3 line-clamp-2 text-xs text-text-secondary\">{service.description}</p>\n\n {/* Price + Response Time */}\n <div className=\"mb-3 flex items-center justify-between text-xs\">\n <span className=\"font-semibold text-text-primary\">{service.priceRange}</span>\n <span className=\"flex items-center gap-1 text-text-secondary\">\n <Clock className=\"h-3 w-3\" />\n {service.responseTime}\n </span>\n </div>\n\n {/* Availability + Verified */}\n <div className=\"mb-4 flex items-center gap-2\">\n {service.verified && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-emerald-100 px-2 py-0.5 text-xs font-medium text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400\">\n <CheckCircle className=\"h-3 w-3\" />\n Verified\n </span>\n )}\n <span className=\"inline-flex items-center gap-1 rounded-full bg-blue-100 px-2 py-0.5 text-xs text-blue-700 dark:bg-blue-900/30 dark:text-blue-400\">\n <MapPin className=\"h-3 w-3\" />\n {service.availableAreas.length} areas\n </span>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => navigate(`/marketplace/${service.id}`)}\n className=\"flex-1 rounded-md bg-foreground px-3 py-2 text-xs font-medium text-background transition-colors hover:bg-foreground/90\"\n >\n <Phone className=\"mr-1 inline h-3 w-3\" />\n Contact\n </button>\n <button\n onClick={() => navigate(`/chat?provider=${service.providerName}`)}\n className=\"rounded-md border border-border-subtle px-3 py-2 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <MessageCircle className=\"h-3 w-3\" />\n </button>\n </div>\n </div>\n );\n}\n\nexport function MarketplacePage() {\n const { data: services } = useMarketplaceServices();\n const [search, setSearch] = useState('');\n const [activeCategory, setActiveCategory] = useState<string>('all');\n\n const filtered = useMemo(() => {\n return services.filter((s) => {\n const matchesSearch =\n !search ||\n s.name.toLowerCase().includes(search.toLowerCase()) ||\n s.providerName.toLowerCase().includes(search.toLowerCase()) ||\n s.description.toLowerCase().includes(search.toLowerCase());\n\n const matchesCategory = activeCategory === 'all' || s.category === activeCategory;\n return matchesSearch && matchesCategory;\n });\n }, [services, search, activeCategory]);\n\n return (\n <PageLayout\n title=\"Service Marketplace\"\n description=\"Find trusted service providers for your property needs\"\n purpose=\"Hire vetted professionals — plumbers, electricians, cleaners, painters, security, and landscapers — to keep your properties in top shape. Browse by category, compare ratings and response times, then contact or message a provider directly.\"\n >\n {/* Category Tabs — primary control zone (one EmphasisPanel per page) */}\n <EmphasisPanel className=\"flex flex-wrap items-center gap-2 p-3\">\n {categories.map((cat) => (\n <button\n key={cat.value}\n onClick={() => setActiveCategory(cat.value)}\n className={cn(\n 'rounded-full px-4 py-1.5 text-sm font-medium transition-colors',\n activeCategory === cat.value\n ? 'bg-foreground text-background'\n : 'border border-border-subtle bg-bg-surface text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n )}\n >\n {cat.label}\n </button>\n ))}\n </EmphasisPanel>\n\n {/* Search */}\n <FilterBar searchValue={search} onSearchChange={setSearch} searchPlaceholder=\"Search services, providers...\" />\n\n {/* Results Count */}\n <p className=\"text-sm text-text-secondary\">\n {filtered.length} service{filtered.length !== 1 ? 's' : ''} found\n </p>\n\n {/* Service Grid */}\n {filtered.length > 0 ? (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((service) => (\n <ServiceCard key={service.id} service={service} />\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-search\"\n title=\"No services found\"\n description=\"Try adjusting your search or category filter to find what you need.\"\n />\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MarketplacePage.js","names":[],"sources":["../../../../src/housingvista/pages/marketplace/MarketplacePage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Star,\n Phone,\n MapPin,\n Shield,\n Wrench,\n Zap,\n SprayCanIcon,\n Paintbrush,\n ShieldCheck,\n Trees,\n CheckCircle,\n Clock,\n MessageCircle,\n} from 'lucide-react';\nimport { EmphasisPanel, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { PageLayout, FilterBar, EmptyState } from '../../components';\nimport { useMarketplaceServices } from '../../hooks';\nimport { cn, formatCurrency } from '../../utils/helpers';\nimport type { MarketplaceService } from '../../types';\n\nconst categories = [\n { value: 'all', label: 'All' },\n { value: 'repair', label: 'Plumbing' },\n { value: 'maintenance', label: 'Electrical' },\n { value: 'cleaning', label: 'Cleaning' },\n { value: 'renovation', label: 'Painting' },\n { value: 'security', label: 'Security' },\n { value: 'landscaping', label: 'Landscaping' },\n] as const;\n\nfunction getCategoryIcon(category: string) {\n switch (category) {\n case 'repair':\n return <Wrench className=\"h-4 w-4\" />;\n case 'maintenance':\n return <Zap className=\"h-4 w-4\" />;\n case 'cleaning':\n return <SprayCanIcon className=\"h-4 w-4\" />;\n case 'renovation':\n return <Paintbrush className=\"h-4 w-4\" />;\n case 'security':\n return <Shield className=\"h-4 w-4\" />;\n case 'landscaping':\n return <Trees className=\"h-4 w-4\" />;\n case 'pest_control':\n return <ShieldCheck className=\"h-4 w-4\" />;\n default:\n return <Wrench className=\"h-4 w-4\" />;\n }\n}\n\nfunction RatingStars({ rating }: { rating: number }) {\n return (\n <div className=\"flex items-center gap-0.5\">\n {Array.from({ length: 5 }).map((_, i) => (\n <Star\n key={i}\n className={cn(\n 'h-3.5 w-3.5',\n i < Math.floor(rating)\n ? 'fill-amber-400 text-amber-400'\n : i < rating\n ? 'fill-amber-400/50 text-amber-400'\n : 'text-text-secondary/30'\n )}\n />\n ))}\n </div>\n );\n}\n\nfunction ServiceCard({ service }: { service: MarketplaceService }) {\n const navigate = useNavigate();\n\n return (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5 shadow-sm transition-shadow hover:shadow-md\">\n {/* Image + Featured Badge */}\n <div className=\"relative mb-4 h-40 overflow-hidden rounded-md bg-bg-sunken\">\n <img src={service.imageUrl} alt={service.name} className=\"h-full w-full object-cover\" />\n {service.featured && (\n <span className=\"absolute left-2 top-2 rounded-full bg-amber-500 px-2 py-0.5 text-xs font-semibold text-white\">\n Featured\n </span>\n )}\n </div>\n\n {/* Header */}\n <div className=\"mb-2 flex items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">{service.name}</h3>\n <p className=\"text-xs text-text-secondary\">{service.providerName}</p>\n </div>\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary capitalize\">\n {getCategoryIcon(service.category)}\n {service.category.replace('_', ' ')}\n </span>\n </div>\n\n {/* Rating */}\n <div className=\"mb-3 flex items-center gap-2\">\n <RatingStars rating={service.rating} />\n <span className=\"text-xs font-medium text-text-primary\">{service.rating}</span>\n <span className=\"text-xs text-text-secondary\">({service.reviewCount} reviews)</span>\n </div>\n\n {/* Description */}\n <p className=\"mb-3 line-clamp-2 text-xs text-text-secondary\">{service.description}</p>\n\n {/* Price + Response Time */}\n <div className=\"mb-3 flex items-center justify-between text-xs\">\n <span className=\"font-semibold text-text-primary\">{service.priceRange}</span>\n <span className=\"flex items-center gap-1 text-text-secondary\">\n <Clock className=\"h-3 w-3\" />\n {service.responseTime}\n </span>\n </div>\n\n {/* Availability + Verified */}\n <div className=\"mb-4 flex items-center gap-2\">\n {service.verified && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-emerald-100 px-2 py-0.5 text-xs font-medium text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400\">\n <CheckCircle className=\"h-3 w-3\" />\n Verified\n </span>\n )}\n <span className=\"inline-flex items-center gap-1 rounded-full bg-blue-100 px-2 py-0.5 text-xs text-blue-700 dark:bg-blue-900/30 dark:text-blue-400\">\n <MapPin className=\"h-3 w-3\" />\n {service.availableAreas.length} areas\n </span>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => navigate(`/marketplace/${service.id}`)}\n className=\"flex-1 rounded-md bg-foreground px-3 py-2 text-xs font-medium text-background transition-colors hover:bg-foreground/90\"\n >\n <Phone className=\"mr-1 inline h-3 w-3\" />\n Contact\n </button>\n <button\n onClick={() => navigate(`/chat?provider=${service.providerName}`)}\n className=\"rounded-md border border-border-subtle px-3 py-2 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <MessageCircle className=\"h-3 w-3\" />\n </button>\n </div>\n </div>\n );\n}\n\nexport function MarketplacePage() {\n const { data: services } = useMarketplaceServices();\n const [search, setSearch] = useState('');\n const [activeCategory, setActiveCategory] = useState<string>('all');\n\n const filtered = useMemo(() => {\n return services.filter((s) => {\n const matchesSearch =\n !search ||\n s.name.toLowerCase().includes(search.toLowerCase()) ||\n s.providerName.toLowerCase().includes(search.toLowerCase()) ||\n s.description.toLowerCase().includes(search.toLowerCase());\n\n const matchesCategory = activeCategory === 'all' || s.category === activeCategory;\n return matchesSearch && matchesCategory;\n });\n }, [services, search, activeCategory]);\n\n return (\n <PageLayout\n title=\"Service Marketplace\"\n description=\"Find trusted service providers for your property needs\"\n purpose=\"Hire vetted professionals — plumbers, electricians, cleaners, painters, security, and landscapers — to keep your properties in top shape. Browse by category, compare ratings and response times, then contact or message a provider directly.\"\n >\n {/* Category Tabs — primary control zone (one EmphasisPanel per page) */}\n <EmphasisPanel className=\"flex flex-wrap items-center gap-2 p-3\">\n {categories.map((cat) => (\n <button\n key={cat.value}\n onClick={() => setActiveCategory(cat.value)}\n className={cn(\n 'rounded-full px-4 py-1.5 text-sm font-medium transition-colors',\n activeCategory === cat.value\n ? 'bg-foreground text-background'\n : 'border border-border-subtle bg-bg-surface text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n )}\n >\n {cat.label}\n </button>\n ))}\n </EmphasisPanel>\n\n {/* Search */}\n <FilterBar searchValue={search} onSearchChange={setSearch} searchPlaceholder=\"Search services, providers...\" />\n\n <NextSteps\n storageKey=\"housingvista-marketplace\"\n steps={[\n {\n id: 'book-repair',\n label: 'Fix something now',\n description: 'Find a plumber or repair pro for an urgent issue.',\n onClick: () => setActiveCategory('repair'),\n },\n {\n id: 'book-cleaning',\n label: 'Schedule a cleaning',\n description: 'Get a property move-in or turnover ready.',\n onClick: () => setActiveCategory('cleaning'),\n },\n {\n id: 'see-featured',\n label: 'Browse all providers',\n description: 'Clear filters to compare every vetted professional.',\n onClick: () => {\n setActiveCategory('all');\n setSearch('');\n },\n },\n ]}\n />\n\n {/* Results Count */}\n <p className=\"text-sm text-text-secondary\">\n {filtered.length} service{filtered.length !== 1 ? 's' : ''} found\n </p>\n\n {/* Service Grid */}\n {filtered.length > 0 ? (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((service) => (\n <ServiceCard key={service.id} service={service} />\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-search\"\n title=\"No services found\"\n description=\"Try adjusting your search or category filter to find what you need.\"\n />\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -13,9 +13,9 @@ import { CheckCircle as f, DollarSign as p, Eye as m, FileText as h, Plus as g,
|
|
|
13
13
|
import { useNavigate as v } from "react-router-dom";
|
|
14
14
|
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
15
15
|
import { useI18n as x } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
16
|
-
import { EmphasisPanel as S } from "@burdenoff/fe-libs/ui";
|
|
16
|
+
import { EmphasisPanel as S, NextSteps as C } from "@burdenoff/fe-libs/ui";
|
|
17
17
|
//#region src/housingvista/pages/transactions/TransactionsPage.tsx
|
|
18
|
-
var
|
|
18
|
+
var w = {
|
|
19
19
|
draft: "muted",
|
|
20
20
|
negotiation: "info",
|
|
21
21
|
under_contract: "warning",
|
|
@@ -25,51 +25,51 @@ var C = {
|
|
|
25
25
|
completed: "success",
|
|
26
26
|
cancelled: "error",
|
|
27
27
|
disputed: "error"
|
|
28
|
-
},
|
|
29
|
-
let
|
|
30
|
-
let n =
|
|
28
|
+
}, T = () => {
|
|
29
|
+
let T = v(), { t: E } = x(), D = l(10), { data: O, loading: k } = c(D.pagination), A = (e, t) => {
|
|
30
|
+
let n = E(e);
|
|
31
31
|
return n === e ? t : n;
|
|
32
|
-
},
|
|
32
|
+
}, j = [
|
|
33
33
|
{
|
|
34
34
|
value: "all",
|
|
35
|
-
label:
|
|
35
|
+
label: A("housingvista.transactions.statusTabs.all", "All")
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
38
|
value: "in_progress",
|
|
39
|
-
label:
|
|
39
|
+
label: A("housingvista.transactions.statusTabs.inProgress", "In Progress")
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
value: "completed",
|
|
43
|
-
label:
|
|
43
|
+
label: A("housingvista.transactions.statusTabs.completed", "Completed")
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
value: "cancelled",
|
|
47
|
-
label:
|
|
47
|
+
label: A("housingvista.transactions.statusTabs.cancelled", "Cancelled")
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
value: "negotiation",
|
|
51
|
-
label:
|
|
51
|
+
label: A("housingvista.transactions.statusTabs.negotiation", "Negotiation")
|
|
52
52
|
},
|
|
53
53
|
{
|
|
54
54
|
value: "pending_approval",
|
|
55
|
-
label:
|
|
55
|
+
label: A("housingvista.transactions.statusTabs.pending", "Pending")
|
|
56
56
|
}
|
|
57
|
-
], [
|
|
58
|
-
let t = !
|
|
57
|
+
], [M, N] = d(""), [P, F] = d("all"), I = u(() => O.filter((e) => {
|
|
58
|
+
let t = !M || e.property.title.toLowerCase().includes(M.toLowerCase()) || e.id.toLowerCase().includes(M.toLowerCase()), n = P === "all" || e.status === P;
|
|
59
59
|
return t && n;
|
|
60
60
|
}), [
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
]),
|
|
65
|
-
total:
|
|
66
|
-
totalValue:
|
|
67
|
-
active:
|
|
68
|
-
completed:
|
|
69
|
-
}), [
|
|
61
|
+
O,
|
|
62
|
+
M,
|
|
63
|
+
P
|
|
64
|
+
]), L = u(() => ({
|
|
65
|
+
total: I.length,
|
|
66
|
+
totalValue: I.reduce((e, t) => e + t.amount, 0),
|
|
67
|
+
active: I.filter((e) => e.status === "in_progress" || e.status === "negotiation" || e.status === "pending_approval").length,
|
|
68
|
+
completed: I.filter((e) => e.status === "completed").length
|
|
69
|
+
}), [I]), R = [
|
|
70
70
|
{
|
|
71
71
|
key: "id",
|
|
72
|
-
header:
|
|
72
|
+
header: A("housingvista.transactions.table.id", "ID"),
|
|
73
73
|
render: (e) => /* @__PURE__ */ b("span", {
|
|
74
74
|
className: "font-medium text-text-primary",
|
|
75
75
|
children: ["#", e.id.split("-")[1]]
|
|
@@ -77,7 +77,7 @@ var C = {
|
|
|
77
77
|
},
|
|
78
78
|
{
|
|
79
79
|
key: "property",
|
|
80
|
-
header:
|
|
80
|
+
header: A("housingvista.transactions.table.property", "Property"),
|
|
81
81
|
render: (e) => /* @__PURE__ */ b("div", {
|
|
82
82
|
className: "max-w-[200px]",
|
|
83
83
|
children: [/* @__PURE__ */ y("div", {
|
|
@@ -95,7 +95,7 @@ var C = {
|
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
97
|
key: "type",
|
|
98
|
-
header:
|
|
98
|
+
header: A("housingvista.transactions.table.type", "Type"),
|
|
99
99
|
render: (e) => /* @__PURE__ */ y("span", {
|
|
100
100
|
className: "capitalize text-sm text-text-primary",
|
|
101
101
|
children: e.type
|
|
@@ -103,16 +103,16 @@ var C = {
|
|
|
103
103
|
},
|
|
104
104
|
{
|
|
105
105
|
key: "status",
|
|
106
|
-
header:
|
|
106
|
+
header: A("housingvista.transactions.table.status", "Status"),
|
|
107
107
|
render: (e) => /* @__PURE__ */ y(a, {
|
|
108
108
|
label: e.status.replace(/_/g, " "),
|
|
109
|
-
variant:
|
|
109
|
+
variant: w[e.status] ?? "default",
|
|
110
110
|
dot: !0
|
|
111
111
|
})
|
|
112
112
|
},
|
|
113
113
|
{
|
|
114
114
|
key: "amount",
|
|
115
|
-
header:
|
|
115
|
+
header: A("housingvista.transactions.table.amount", "Amount"),
|
|
116
116
|
sortable: !0,
|
|
117
117
|
render: (t) => /* @__PURE__ */ y("span", {
|
|
118
118
|
className: "font-semibold text-text-primary",
|
|
@@ -121,7 +121,7 @@ var C = {
|
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
key: "createdAt",
|
|
124
|
-
header:
|
|
124
|
+
header: A("housingvista.transactions.table.date", "Date"),
|
|
125
125
|
sortable: !0,
|
|
126
126
|
render: (e) => /* @__PURE__ */ y("span", {
|
|
127
127
|
className: "text-sm text-text-secondary",
|
|
@@ -134,20 +134,20 @@ var C = {
|
|
|
134
134
|
sortable: !1,
|
|
135
135
|
render: (e) => /* @__PURE__ */ b("button", {
|
|
136
136
|
onClick: (t) => {
|
|
137
|
-
t.stopPropagation(),
|
|
137
|
+
t.stopPropagation(), T(e.id);
|
|
138
138
|
},
|
|
139
139
|
className: "inline-flex items-center gap-1 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary",
|
|
140
|
-
children: [/* @__PURE__ */ y(m, { className: "h-3.5 w-3.5" }),
|
|
140
|
+
children: [/* @__PURE__ */ y(m, { className: "h-3.5 w-3.5" }), A("housingvista.transactions.table.view", "View")]
|
|
141
141
|
})
|
|
142
142
|
}
|
|
143
143
|
];
|
|
144
144
|
return /* @__PURE__ */ b(n, {
|
|
145
|
-
title:
|
|
146
|
-
description:
|
|
147
|
-
purpose:
|
|
145
|
+
title: A("housingvista.transactions.title", "Transactions"),
|
|
146
|
+
description: A("housingvista.transactions.description", "Manage your property transactions, escrow, and deals"),
|
|
147
|
+
purpose: A("housingvista.transactions.purpose", "Track every buy, sell, and lease deal end to end — from offer and negotiation through escrow, approvals, and closing. Use this page to see what money is in motion, which deals need action, and the full history of each transaction."),
|
|
148
148
|
actions: /* @__PURE__ */ b("button", {
|
|
149
149
|
className: "inline-flex items-center gap-2 rounded-md bg-foreground px-4 py-2 text-sm font-medium text-background transition-colors hover:bg-foreground/90",
|
|
150
|
-
children: [/* @__PURE__ */ y(g, { className: "h-4 w-4" }),
|
|
150
|
+
children: [/* @__PURE__ */ y(g, { className: "h-4 w-4" }), A("housingvista.transactions.actions.newTransaction", "New Transaction")]
|
|
151
151
|
}),
|
|
152
152
|
children: [
|
|
153
153
|
/* @__PURE__ */ y(S, {
|
|
@@ -156,84 +156,110 @@ var C = {
|
|
|
156
156
|
className: "grid grid-cols-2 gap-4 lg:grid-cols-4",
|
|
157
157
|
children: [
|
|
158
158
|
/* @__PURE__ */ y(r, {
|
|
159
|
-
label:
|
|
160
|
-
value:
|
|
159
|
+
label: A("housingvista.transactions.stats.totalTransactions", "Total Transactions"),
|
|
160
|
+
value: L.total,
|
|
161
161
|
icon: /* @__PURE__ */ y(h, { className: "h-5 w-5" })
|
|
162
162
|
}),
|
|
163
163
|
/* @__PURE__ */ y(r, {
|
|
164
|
-
label:
|
|
165
|
-
value:
|
|
164
|
+
label: A("housingvista.transactions.stats.totalValue", "Total Value"),
|
|
165
|
+
value: L.totalValue,
|
|
166
166
|
prefix: "$",
|
|
167
167
|
icon: /* @__PURE__ */ y(p, { className: "h-5 w-5" })
|
|
168
168
|
}),
|
|
169
169
|
/* @__PURE__ */ y(r, {
|
|
170
|
-
label:
|
|
171
|
-
value:
|
|
170
|
+
label: A("housingvista.transactions.stats.active", "Active"),
|
|
171
|
+
value: L.active,
|
|
172
172
|
icon: /* @__PURE__ */ y(_, { className: "h-5 w-5" })
|
|
173
173
|
}),
|
|
174
174
|
/* @__PURE__ */ y(r, {
|
|
175
|
-
label:
|
|
176
|
-
value:
|
|
175
|
+
label: A("housingvista.transactions.stats.completed", "Completed"),
|
|
176
|
+
value: L.completed,
|
|
177
177
|
icon: /* @__PURE__ */ y(f, { className: "h-5 w-5" })
|
|
178
178
|
})
|
|
179
179
|
]
|
|
180
180
|
})
|
|
181
181
|
}),
|
|
182
|
+
/* @__PURE__ */ y(C, {
|
|
183
|
+
storageKey: "housingvista-transactions",
|
|
184
|
+
steps: [
|
|
185
|
+
{
|
|
186
|
+
id: "focus-active",
|
|
187
|
+
label: A("housingvista.transactions.nextSteps.active", "See deals that need attention"),
|
|
188
|
+
description: A("housingvista.transactions.nextSteps.activeDesc", "Filter to in-progress deals where money is moving."),
|
|
189
|
+
onClick: () => F("in_progress")
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
id: "open-detail",
|
|
193
|
+
label: A("housingvista.transactions.nextSteps.open", "Open a transaction"),
|
|
194
|
+
description: A("housingvista.transactions.nextSteps.openDesc", "Drill into escrow, approvals, and the full timeline."),
|
|
195
|
+
onClick: () => {
|
|
196
|
+
let e = O[0];
|
|
197
|
+
e && T(`/transactions/${e.id}`);
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: "browse-properties",
|
|
202
|
+
label: A("housingvista.transactions.nextSteps.browse", "Start a new deal from a listing"),
|
|
203
|
+
description: A("housingvista.transactions.nextSteps.browseDesc", "Find a property to make an offer on."),
|
|
204
|
+
onClick: () => T("/properties")
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
}),
|
|
182
208
|
/* @__PURE__ */ y(s, {
|
|
183
|
-
searchValue:
|
|
184
|
-
onSearchChange:
|
|
185
|
-
searchPlaceholder:
|
|
209
|
+
searchValue: M,
|
|
210
|
+
onSearchChange: N,
|
|
211
|
+
searchPlaceholder: A("housingvista.transactions.search.placeholder", "Search by property or transaction ID...")
|
|
186
212
|
}),
|
|
187
213
|
/* @__PURE__ */ y("div", {
|
|
188
214
|
className: "flex flex-wrap items-center gap-2",
|
|
189
|
-
children:
|
|
190
|
-
onClick: () =>
|
|
191
|
-
className: `rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${
|
|
215
|
+
children: j.map((e) => /* @__PURE__ */ y("button", {
|
|
216
|
+
onClick: () => F(e.value),
|
|
217
|
+
className: `rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${P === e.value ? "bg-foreground text-background" : "text-text-secondary hover:bg-bg-sunken hover:text-text-primary"}`,
|
|
192
218
|
children: e.label
|
|
193
219
|
}, e.value))
|
|
194
220
|
}),
|
|
195
221
|
/* @__PURE__ */ b("p", {
|
|
196
222
|
className: "text-sm text-text-secondary",
|
|
197
223
|
children: [
|
|
198
|
-
|
|
224
|
+
A("housingvista.transactions.results.showing", "Showing"),
|
|
199
225
|
" ",
|
|
200
|
-
|
|
226
|
+
I.length,
|
|
201
227
|
" ",
|
|
202
|
-
|
|
228
|
+
A("housingvista.transactions.results.of", "of"),
|
|
203
229
|
" ",
|
|
204
|
-
|
|
230
|
+
O.length,
|
|
205
231
|
" ",
|
|
206
|
-
|
|
232
|
+
A("housingvista.transactions.results.transactions", "transactions")
|
|
207
233
|
]
|
|
208
234
|
}),
|
|
209
|
-
|
|
210
|
-
columns:
|
|
211
|
-
data:
|
|
235
|
+
I.length > 0 ? /* @__PURE__ */ y(i, {
|
|
236
|
+
columns: R,
|
|
237
|
+
data: I,
|
|
212
238
|
keyField: "id",
|
|
213
|
-
onRowClick: (e) =>
|
|
239
|
+
onRowClick: (e) => T(e.id),
|
|
214
240
|
pageSize: 10,
|
|
215
241
|
serverPagination: {
|
|
216
|
-
page:
|
|
217
|
-
pageSize:
|
|
218
|
-
hasNextPage:
|
|
219
|
-
loading:
|
|
220
|
-
onPageChange:
|
|
221
|
-
onPageSizeChange:
|
|
242
|
+
page: D.page,
|
|
243
|
+
pageSize: D.pageSize,
|
|
244
|
+
hasNextPage: O.length >= D.pageSize,
|
|
245
|
+
loading: k,
|
|
246
|
+
onPageChange: D.setPage,
|
|
247
|
+
onPageSizeChange: D.setPageSize
|
|
222
248
|
}
|
|
223
249
|
}) : /* @__PURE__ */ y(o, {
|
|
224
250
|
illustration: "empty-search",
|
|
225
251
|
icon: /* @__PURE__ */ y(h, { className: "h-12 w-12" }),
|
|
226
|
-
title:
|
|
227
|
-
description:
|
|
252
|
+
title: A("housingvista.transactions.empty.title", "No transactions found"),
|
|
253
|
+
description: A("housingvista.transactions.empty.description", "Try adjusting your search criteria or create a new transaction."),
|
|
228
254
|
action: /* @__PURE__ */ b("button", {
|
|
229
255
|
className: "rounded-md bg-foreground px-4 py-2 text-sm font-medium text-background",
|
|
230
|
-
children: [/* @__PURE__ */ y(g, { className: "mr-2 inline h-4 w-4" }),
|
|
256
|
+
children: [/* @__PURE__ */ y(g, { className: "mr-2 inline h-4 w-4" }), A("housingvista.transactions.empty.createNewTransaction", "Create New Transaction")]
|
|
231
257
|
})
|
|
232
258
|
})
|
|
233
259
|
]
|
|
234
260
|
});
|
|
235
261
|
};
|
|
236
262
|
//#endregion
|
|
237
|
-
export {
|
|
263
|
+
export { T as TransactionsPage };
|
|
238
264
|
|
|
239
265
|
//# sourceMappingURL=TransactionsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransactionsPage.js","names":[],"sources":["../../../../src/housingvista/pages/transactions/TransactionsPage.tsx"],"sourcesContent":["/**\n * HousingVista Transactions Listing Page\n * Migrated from legacy housingvista-frontend transactions.tsx\n * @module housingvista\n */\n\nimport { useState, useMemo } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Plus, FileText, DollarSign, TrendingUp, CheckCircle, Eye } from 'lucide-react';\nimport { EmphasisPanel } from '@burdenoff/fe-libs/ui';\nimport { PageLayout, FilterBar, DataTable, StatusBadge, EmptyState, StatCard } from '../../components';\nimport type { Column } from '../../components';\nimport { useTransactions } from '../../hooks';\nimport { useServerPagination } from '../../hooks/useServerPagination';\nimport { formatCurrency, formatDate } from '../../utils/helpers';\nimport type { Transaction, TransactionStatus, TransactionType } from '../../types';\n\n// ---- Status badge variant map ----\nconst statusVariantMap: Record<string, 'success' | 'warning' | 'error' | 'info' | 'default' | 'muted'> = {\n draft: 'muted',\n negotiation: 'info',\n under_contract: 'warning',\n pending_approval: 'warning',\n approved: 'info',\n in_progress: 'info',\n completed: 'success',\n cancelled: 'error',\n disputed: 'error',\n};\n\nexport const TransactionsPage = () => {\n const navigate = useNavigate();\n const { t } = useI18n();\n const pagination = useServerPagination(10);\n const { data: transactions, loading } = useTransactions(pagination.pagination);\n\n const tr = (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n\n const statusTabs: { value: TransactionStatus | 'all'; label: string }[] = [\n { value: 'all', label: tr('housingvista.transactions.statusTabs.all', 'All') },\n { value: 'in_progress', label: tr('housingvista.transactions.statusTabs.inProgress', 'In Progress') },\n { value: 'completed', label: tr('housingvista.transactions.statusTabs.completed', 'Completed') },\n { value: 'cancelled', label: tr('housingvista.transactions.statusTabs.cancelled', 'Cancelled') },\n { value: 'negotiation', label: tr('housingvista.transactions.statusTabs.negotiation', 'Negotiation') },\n { value: 'pending_approval', label: tr('housingvista.transactions.statusTabs.pending', 'Pending') },\n ];\n\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedStatus, setSelectedStatus] = useState<TransactionStatus | 'all'>('all');\n\n const filteredTransactions = useMemo(() => {\n return transactions.filter((t) => {\n const matchesSearch =\n !searchQuery ||\n t.property.title.toLowerCase().includes(searchQuery.toLowerCase()) ||\n t.id.toLowerCase().includes(searchQuery.toLowerCase());\n\n const matchesStatus = selectedStatus === 'all' || t.status === selectedStatus;\n\n return matchesSearch && matchesStatus;\n });\n }, [transactions, searchQuery, selectedStatus]);\n\n const stats = useMemo(() => {\n const total = filteredTransactions.length;\n const totalValue = filteredTransactions.reduce((sum, t) => sum + t.amount, 0);\n const active = filteredTransactions.filter(\n (t) => t.status === 'in_progress' || t.status === 'negotiation' || t.status === 'pending_approval'\n ).length;\n const completed = filteredTransactions.filter((t) => t.status === 'completed').length;\n return { total, totalValue, active, completed };\n }, [filteredTransactions]);\n\n // ---- Table columns ----\n const columns: Column<Transaction>[] = [\n {\n key: 'id',\n header: tr('housingvista.transactions.table.id', 'ID'),\n render: (t) => <span className=\"font-medium text-text-primary\">#{t.id.split('-')[1]}</span>,\n },\n {\n key: 'property',\n header: tr('housingvista.transactions.table.property', 'Property'),\n render: (t) => (\n <div className=\"max-w-[200px]\">\n <div className=\"truncate text-sm font-medium text-text-primary\">{t.property.title}</div>\n <div className=\"truncate text-xs text-text-secondary\">\n {t.property.location.city}, {t.property.location.state}\n </div>\n </div>\n ),\n },\n {\n key: 'type',\n header: tr('housingvista.transactions.table.type', 'Type'),\n render: (t) => <span className=\"capitalize text-sm text-text-primary\">{t.type}</span>,\n },\n {\n key: 'status',\n header: tr('housingvista.transactions.table.status', 'Status'),\n render: (t) => (\n <StatusBadge label={t.status.replace(/_/g, ' ')} variant={statusVariantMap[t.status] ?? 'default'} dot />\n ),\n },\n {\n key: 'amount',\n header: tr('housingvista.transactions.table.amount', 'Amount'),\n sortable: true,\n render: (t) => <span className=\"font-semibold text-text-primary\">{formatCurrency(t.amount, t.currency)}</span>,\n },\n {\n key: 'createdAt',\n header: tr('housingvista.transactions.table.date', 'Date'),\n sortable: true,\n render: (t) => <span className=\"text-sm text-text-secondary\">{formatDate(t.createdAt.toISOString())}</span>,\n },\n {\n key: 'actions',\n header: '',\n sortable: false,\n render: (t) => (\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(t.id);\n }}\n className=\"inline-flex items-center gap-1 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n <Eye className=\"h-3.5 w-3.5\" />\n {tr('housingvista.transactions.table.view', 'View')}\n </button>\n ),\n },\n ];\n\n return (\n <PageLayout\n title={tr('housingvista.transactions.title', 'Transactions')}\n description={tr('housingvista.transactions.description', 'Manage your property transactions, escrow, and deals')}\n purpose={tr(\n 'housingvista.transactions.purpose',\n 'Track every buy, sell, and lease deal end to end — from offer and negotiation through escrow, approvals, and closing. Use this page to see what money is in motion, which deals need action, and the full history of each transaction.'\n )}\n actions={\n <button className=\"inline-flex items-center gap-2 rounded-md bg-foreground px-4 py-2 text-sm font-medium text-background transition-colors hover:bg-foreground/90\">\n <Plus className=\"h-4 w-4\" />\n {tr('housingvista.transactions.actions.newTransaction', 'New Transaction')}\n </button>\n }\n >\n {/* Stats — primary insight zone (one EmphasisPanel per page) */}\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <StatCard\n label={tr('housingvista.transactions.stats.totalTransactions', 'Total Transactions')}\n value={stats.total}\n icon={<FileText className=\"h-5 w-5\" />}\n />\n <StatCard\n label={tr('housingvista.transactions.stats.totalValue', 'Total Value')}\n value={stats.totalValue}\n prefix=\"$\"\n icon={<DollarSign className=\"h-5 w-5\" />}\n />\n <StatCard\n label={tr('housingvista.transactions.stats.active', 'Active')}\n value={stats.active}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n />\n <StatCard\n label={tr('housingvista.transactions.stats.completed', 'Completed')}\n value={stats.completed}\n icon={<CheckCircle className=\"h-5 w-5\" />}\n />\n </div>\n </EmphasisPanel>\n\n {/* Search */}\n <FilterBar\n searchValue={searchQuery}\n onSearchChange={setSearchQuery}\n searchPlaceholder={tr(\n 'housingvista.transactions.search.placeholder',\n 'Search by property or transaction ID...'\n )}\n />\n\n {/* Status Tabs */}\n <div className=\"flex flex-wrap items-center gap-2\">\n {statusTabs.map((tab) => (\n <button\n key={tab.value}\n onClick={() => setSelectedStatus(tab.value)}\n className={`rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${\n selectedStatus === tab.value\n ? 'bg-foreground text-background'\n : 'text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n }`}\n >\n {tab.label}\n </button>\n ))}\n </div>\n\n {/* Results info */}\n <p className=\"text-sm text-text-secondary\">\n {tr('housingvista.transactions.results.showing', 'Showing')} {filteredTransactions.length}{' '}\n {tr('housingvista.transactions.results.of', 'of')} {transactions.length}{' '}\n {tr('housingvista.transactions.results.transactions', 'transactions')}\n </p>\n\n {/* Table / Empty */}\n {filteredTransactions.length > 0 ? (\n <DataTable\n columns={columns}\n data={filteredTransactions}\n keyField=\"id\"\n onRowClick={(t) => navigate(t.id)}\n pageSize={10}\n serverPagination={{\n page: pagination.page,\n pageSize: pagination.pageSize,\n hasNextPage: transactions.length >= pagination.pageSize,\n loading,\n onPageChange: pagination.setPage,\n onPageSizeChange: pagination.setPageSize,\n }}\n />\n ) : (\n <EmptyState\n illustration=\"empty-search\"\n icon={<FileText className=\"h-12 w-12\" />}\n title={tr('housingvista.transactions.empty.title', 'No transactions found')}\n description={tr(\n 'housingvista.transactions.empty.description',\n 'Try adjusting your search criteria or create a new transaction.'\n )}\n action={\n <button className=\"rounded-md bg-foreground px-4 py-2 text-sm font-medium text-background\">\n <Plus className=\"mr-2 inline h-4 w-4\" />\n {tr('housingvista.transactions.empty.createNewTransaction', 'Create New Transaction')}\n </button>\n }\n />\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TransactionsPage.js","names":[],"sources":["../../../../src/housingvista/pages/transactions/TransactionsPage.tsx"],"sourcesContent":["/**\n * HousingVista Transactions Listing Page\n * Migrated from legacy housingvista-frontend transactions.tsx\n * @module housingvista\n */\n\nimport { useState, useMemo } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Plus, FileText, DollarSign, TrendingUp, CheckCircle, Eye } from 'lucide-react';\nimport { EmphasisPanel, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { PageLayout, FilterBar, DataTable, StatusBadge, EmptyState, StatCard } from '../../components';\nimport type { Column } from '../../components';\nimport { useTransactions } from '../../hooks';\nimport { useServerPagination } from '../../hooks/useServerPagination';\nimport { formatCurrency, formatDate } from '../../utils/helpers';\nimport type { Transaction, TransactionStatus, TransactionType } from '../../types';\n\n// ---- Status badge variant map ----\nconst statusVariantMap: Record<string, 'success' | 'warning' | 'error' | 'info' | 'default' | 'muted'> = {\n draft: 'muted',\n negotiation: 'info',\n under_contract: 'warning',\n pending_approval: 'warning',\n approved: 'info',\n in_progress: 'info',\n completed: 'success',\n cancelled: 'error',\n disputed: 'error',\n};\n\nexport const TransactionsPage = () => {\n const navigate = useNavigate();\n const { t } = useI18n();\n const pagination = useServerPagination(10);\n const { data: transactions, loading } = useTransactions(pagination.pagination);\n\n const tr = (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n\n const statusTabs: { value: TransactionStatus | 'all'; label: string }[] = [\n { value: 'all', label: tr('housingvista.transactions.statusTabs.all', 'All') },\n { value: 'in_progress', label: tr('housingvista.transactions.statusTabs.inProgress', 'In Progress') },\n { value: 'completed', label: tr('housingvista.transactions.statusTabs.completed', 'Completed') },\n { value: 'cancelled', label: tr('housingvista.transactions.statusTabs.cancelled', 'Cancelled') },\n { value: 'negotiation', label: tr('housingvista.transactions.statusTabs.negotiation', 'Negotiation') },\n { value: 'pending_approval', label: tr('housingvista.transactions.statusTabs.pending', 'Pending') },\n ];\n\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedStatus, setSelectedStatus] = useState<TransactionStatus | 'all'>('all');\n\n const filteredTransactions = useMemo(() => {\n return transactions.filter((t) => {\n const matchesSearch =\n !searchQuery ||\n t.property.title.toLowerCase().includes(searchQuery.toLowerCase()) ||\n t.id.toLowerCase().includes(searchQuery.toLowerCase());\n\n const matchesStatus = selectedStatus === 'all' || t.status === selectedStatus;\n\n return matchesSearch && matchesStatus;\n });\n }, [transactions, searchQuery, selectedStatus]);\n\n const stats = useMemo(() => {\n const total = filteredTransactions.length;\n const totalValue = filteredTransactions.reduce((sum, t) => sum + t.amount, 0);\n const active = filteredTransactions.filter(\n (t) => t.status === 'in_progress' || t.status === 'negotiation' || t.status === 'pending_approval'\n ).length;\n const completed = filteredTransactions.filter((t) => t.status === 'completed').length;\n return { total, totalValue, active, completed };\n }, [filteredTransactions]);\n\n // ---- Table columns ----\n const columns: Column<Transaction>[] = [\n {\n key: 'id',\n header: tr('housingvista.transactions.table.id', 'ID'),\n render: (t) => <span className=\"font-medium text-text-primary\">#{t.id.split('-')[1]}</span>,\n },\n {\n key: 'property',\n header: tr('housingvista.transactions.table.property', 'Property'),\n render: (t) => (\n <div className=\"max-w-[200px]\">\n <div className=\"truncate text-sm font-medium text-text-primary\">{t.property.title}</div>\n <div className=\"truncate text-xs text-text-secondary\">\n {t.property.location.city}, {t.property.location.state}\n </div>\n </div>\n ),\n },\n {\n key: 'type',\n header: tr('housingvista.transactions.table.type', 'Type'),\n render: (t) => <span className=\"capitalize text-sm text-text-primary\">{t.type}</span>,\n },\n {\n key: 'status',\n header: tr('housingvista.transactions.table.status', 'Status'),\n render: (t) => (\n <StatusBadge label={t.status.replace(/_/g, ' ')} variant={statusVariantMap[t.status] ?? 'default'} dot />\n ),\n },\n {\n key: 'amount',\n header: tr('housingvista.transactions.table.amount', 'Amount'),\n sortable: true,\n render: (t) => <span className=\"font-semibold text-text-primary\">{formatCurrency(t.amount, t.currency)}</span>,\n },\n {\n key: 'createdAt',\n header: tr('housingvista.transactions.table.date', 'Date'),\n sortable: true,\n render: (t) => <span className=\"text-sm text-text-secondary\">{formatDate(t.createdAt.toISOString())}</span>,\n },\n {\n key: 'actions',\n header: '',\n sortable: false,\n render: (t) => (\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(t.id);\n }}\n className=\"inline-flex items-center gap-1 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n <Eye className=\"h-3.5 w-3.5\" />\n {tr('housingvista.transactions.table.view', 'View')}\n </button>\n ),\n },\n ];\n\n return (\n <PageLayout\n title={tr('housingvista.transactions.title', 'Transactions')}\n description={tr('housingvista.transactions.description', 'Manage your property transactions, escrow, and deals')}\n purpose={tr(\n 'housingvista.transactions.purpose',\n 'Track every buy, sell, and lease deal end to end — from offer and negotiation through escrow, approvals, and closing. Use this page to see what money is in motion, which deals need action, and the full history of each transaction.'\n )}\n actions={\n <button className=\"inline-flex items-center gap-2 rounded-md bg-foreground px-4 py-2 text-sm font-medium text-background transition-colors hover:bg-foreground/90\">\n <Plus className=\"h-4 w-4\" />\n {tr('housingvista.transactions.actions.newTransaction', 'New Transaction')}\n </button>\n }\n >\n {/* Stats — primary insight zone (one EmphasisPanel per page) */}\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <StatCard\n label={tr('housingvista.transactions.stats.totalTransactions', 'Total Transactions')}\n value={stats.total}\n icon={<FileText className=\"h-5 w-5\" />}\n />\n <StatCard\n label={tr('housingvista.transactions.stats.totalValue', 'Total Value')}\n value={stats.totalValue}\n prefix=\"$\"\n icon={<DollarSign className=\"h-5 w-5\" />}\n />\n <StatCard\n label={tr('housingvista.transactions.stats.active', 'Active')}\n value={stats.active}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n />\n <StatCard\n label={tr('housingvista.transactions.stats.completed', 'Completed')}\n value={stats.completed}\n icon={<CheckCircle className=\"h-5 w-5\" />}\n />\n </div>\n </EmphasisPanel>\n\n <NextSteps\n storageKey=\"housingvista-transactions\"\n steps={[\n {\n id: 'focus-active',\n label: tr('housingvista.transactions.nextSteps.active', 'See deals that need attention'),\n description: tr(\n 'housingvista.transactions.nextSteps.activeDesc',\n 'Filter to in-progress deals where money is moving.'\n ),\n onClick: () => setSelectedStatus('in_progress'),\n },\n {\n id: 'open-detail',\n label: tr('housingvista.transactions.nextSteps.open', 'Open a transaction'),\n description: tr(\n 'housingvista.transactions.nextSteps.openDesc',\n 'Drill into escrow, approvals, and the full timeline.'\n ),\n onClick: () => {\n const first = transactions[0];\n if (first) navigate(`/transactions/${first.id}`);\n },\n },\n {\n id: 'browse-properties',\n label: tr('housingvista.transactions.nextSteps.browse', 'Start a new deal from a listing'),\n description: tr(\n 'housingvista.transactions.nextSteps.browseDesc',\n 'Find a property to make an offer on.'\n ),\n onClick: () => navigate('/properties'),\n },\n ]}\n />\n\n {/* Search */}\n <FilterBar\n searchValue={searchQuery}\n onSearchChange={setSearchQuery}\n searchPlaceholder={tr(\n 'housingvista.transactions.search.placeholder',\n 'Search by property or transaction ID...'\n )}\n />\n\n {/* Status Tabs */}\n <div className=\"flex flex-wrap items-center gap-2\">\n {statusTabs.map((tab) => (\n <button\n key={tab.value}\n onClick={() => setSelectedStatus(tab.value)}\n className={`rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${\n selectedStatus === tab.value\n ? 'bg-foreground text-background'\n : 'text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n }`}\n >\n {tab.label}\n </button>\n ))}\n </div>\n\n {/* Results info */}\n <p className=\"text-sm text-text-secondary\">\n {tr('housingvista.transactions.results.showing', 'Showing')} {filteredTransactions.length}{' '}\n {tr('housingvista.transactions.results.of', 'of')} {transactions.length}{' '}\n {tr('housingvista.transactions.results.transactions', 'transactions')}\n </p>\n\n {/* Table / Empty */}\n {filteredTransactions.length > 0 ? (\n <DataTable\n columns={columns}\n data={filteredTransactions}\n keyField=\"id\"\n onRowClick={(t) => navigate(t.id)}\n pageSize={10}\n serverPagination={{\n page: pagination.page,\n pageSize: pagination.pageSize,\n hasNextPage: transactions.length >= pagination.pageSize,\n loading,\n onPageChange: pagination.setPage,\n onPageSizeChange: pagination.setPageSize,\n }}\n />\n ) : (\n <EmptyState\n illustration=\"empty-search\"\n icon={<FileText className=\"h-12 w-12\" />}\n title={tr('housingvista.transactions.empty.title', 'No transactions found')}\n description={tr(\n 'housingvista.transactions.empty.description',\n 'Try adjusting your search criteria or create a new transaction.'\n )}\n action={\n <button className=\"rounded-md bg-foreground px-4 py-2 text-sm font-medium text-background\">\n <Plus className=\"mr-2 inline h-4 w-4\" />\n {tr('housingvista.transactions.empty.createNewTransaction', 'Create New Transaction')}\n </button>\n }\n />\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-housingvista",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.626.2",
|
|
4
4
|
"description": "Burdenoff HousingVista Microfrontend - Comprehensive real estate platform for property management, investment, and transactions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|