@burdenoff/microfe-housingvista 2026.709.2 → 2026.709.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/housingvista/HousingVistaRoutes.d.ts.map +1 -1
  2. package/dist/housingvista/HousingVistaRoutes.js +136 -37
  3. package/dist/housingvista/HousingVistaRoutes.js.map +1 -1
  4. package/dist/housingvista/hooks/useHousingVistaAnalytics.d.ts +1 -1
  5. package/dist/housingvista/hooks/useHousingVistaAnalytics.d.ts.map +1 -1
  6. package/dist/housingvista/hooks/useHousingVistaAnalytics.js +4 -4
  7. package/dist/housingvista/hooks/useHousingVistaAnalytics.js.map +1 -1
  8. package/dist/housingvista/pages/amc/AMCManagementPage.d.ts.map +1 -1
  9. package/dist/housingvista/pages/amc/AMCManagementPage.js +62 -60
  10. package/dist/housingvista/pages/amc/AMCManagementPage.js.map +1 -1
  11. package/dist/housingvista/pages/assets/AssetDetailPage.d.ts.map +1 -1
  12. package/dist/housingvista/pages/assets/AssetDetailPage.js +141 -138
  13. package/dist/housingvista/pages/assets/AssetDetailPage.js.map +1 -1
  14. package/dist/housingvista/pages/assets/AssetManagementPage.d.ts.map +1 -1
  15. package/dist/housingvista/pages/assets/AssetManagementPage.js +193 -191
  16. package/dist/housingvista/pages/assets/AssetManagementPage.js.map +1 -1
  17. package/dist/housingvista/pages/chat/ChatPage.d.ts.map +1 -1
  18. package/dist/housingvista/pages/chat/ChatPage.js +115 -113
  19. package/dist/housingvista/pages/chat/ChatPage.js.map +1 -1
  20. package/dist/housingvista/pages/finance/HomeLoanDetailPage.d.ts.map +1 -1
  21. package/dist/housingvista/pages/finance/HomeLoanDetailPage.js +152 -149
  22. package/dist/housingvista/pages/finance/HomeLoanDetailPage.js.map +1 -1
  23. package/dist/housingvista/pages/finance/HomeLoansPage.d.ts.map +1 -1
  24. package/dist/housingvista/pages/finance/HomeLoansPage.js +141 -139
  25. package/dist/housingvista/pages/finance/HomeLoansPage.js.map +1 -1
  26. package/dist/housingvista/pages/investments/InvestmentDetailPage.d.ts.map +1 -1
  27. package/dist/housingvista/pages/investments/InvestmentDetailPage.js +290 -287
  28. package/dist/housingvista/pages/investments/InvestmentDetailPage.js.map +1 -1
  29. package/dist/housingvista/pages/investments/InvestmentsPage.d.ts.map +1 -1
  30. package/dist/housingvista/pages/investments/InvestmentsPage.js +64 -62
  31. package/dist/housingvista/pages/investments/InvestmentsPage.js.map +1 -1
  32. package/dist/housingvista/pages/legal/LegalPage.d.ts.map +1 -1
  33. package/dist/housingvista/pages/legal/LegalPage.js +95 -93
  34. package/dist/housingvista/pages/legal/LegalPage.js.map +1 -1
  35. package/dist/housingvista/pages/legal/LegalServiceDetailPage.d.ts.map +1 -1
  36. package/dist/housingvista/pages/legal/LegalServiceDetailPage.js +129 -126
  37. package/dist/housingvista/pages/legal/LegalServiceDetailPage.js.map +1 -1
  38. package/dist/housingvista/pages/market/AreaInsightsPage.d.ts.map +1 -1
  39. package/dist/housingvista/pages/market/AreaInsightsPage.js +99 -97
  40. package/dist/housingvista/pages/market/AreaInsightsPage.js.map +1 -1
  41. package/dist/housingvista/pages/market/MarketAnalyticsPage.d.ts.map +1 -1
  42. package/dist/housingvista/pages/market/MarketAnalyticsPage.js +138 -136
  43. package/dist/housingvista/pages/market/MarketAnalyticsPage.js.map +1 -1
  44. package/dist/housingvista/pages/marketplace/MarketplacePage.d.ts.map +1 -1
  45. package/dist/housingvista/pages/marketplace/MarketplacePage.js +99 -97
  46. package/dist/housingvista/pages/marketplace/MarketplacePage.js.map +1 -1
  47. package/dist/housingvista/pages/marketplace/ServiceDetailPage.d.ts.map +1 -1
  48. package/dist/housingvista/pages/marketplace/ServiceDetailPage.js +137 -134
  49. package/dist/housingvista/pages/marketplace/ServiceDetailPage.js.map +1 -1
  50. package/dist/housingvista/pages/offers/OffersPage.d.ts.map +1 -1
  51. package/dist/housingvista/pages/offers/OffersPage.js +94 -92
  52. package/dist/housingvista/pages/offers/OffersPage.js.map +1 -1
  53. package/dist/housingvista/pages/properties/AddPropertyPage.d.ts.map +1 -1
  54. package/dist/housingvista/pages/properties/AddPropertyPage.js +280 -279
  55. package/dist/housingvista/pages/properties/AddPropertyPage.js.map +1 -1
  56. package/dist/housingvista/pages/rental/RentalManagementPage.d.ts.map +1 -1
  57. package/dist/housingvista/pages/rental/RentalManagementPage.js +153 -151
  58. package/dist/housingvista/pages/rental/RentalManagementPage.js.map +1 -1
  59. package/dist/housingvista/pages/settings/ProfilePage.d.ts.map +1 -1
  60. package/dist/housingvista/pages/settings/ProfilePage.js +52 -50
  61. package/dist/housingvista/pages/settings/ProfilePage.js.map +1 -1
  62. package/dist/housingvista/pages/settings/SettingsPage.d.ts.map +1 -1
  63. package/dist/housingvista/pages/settings/SettingsPage.js +54 -52
  64. package/dist/housingvista/pages/settings/SettingsPage.js.map +1 -1
  65. package/dist/housingvista/pages/shortlist/ShortlistPage.d.ts.map +1 -1
  66. package/dist/housingvista/pages/shortlist/ShortlistPage.js +43 -41
  67. package/dist/housingvista/pages/shortlist/ShortlistPage.js.map +1 -1
  68. package/dist/housingvista/pages/transactions/TransactionDetailPage.d.ts.map +1 -1
  69. package/dist/housingvista/pages/transactions/TransactionDetailPage.js +211 -208
  70. package/dist/housingvista/pages/transactions/TransactionDetailPage.js.map +1 -1
  71. package/dist/housingvista/pages/transactions/TransactionsPage.d.ts.map +1 -1
  72. package/dist/housingvista/pages/transactions/TransactionsPage.js +85 -83
  73. package/dist/housingvista/pages/transactions/TransactionsPage.js.map +1 -1
  74. package/dist/housingvista/pages/verification/VerificationDetailPage.d.ts.map +1 -1
  75. package/dist/housingvista/pages/verification/VerificationDetailPage.js +25 -22
  76. package/dist/housingvista/pages/verification/VerificationDetailPage.js.map +1 -1
  77. package/dist/housingvista/pages/verification/VerificationPage.d.ts.map +1 -1
  78. package/dist/housingvista/pages/verification/VerificationPage.js +123 -121
  79. package/dist/housingvista/pages/verification/VerificationPage.js.map +1 -1
  80. package/dist/housingvista/pages/viewings/ViewingsPage.d.ts.map +1 -1
  81. package/dist/housingvista/pages/viewings/ViewingsPage.js +128 -126
  82. package/dist/housingvista/pages/viewings/ViewingsPage.js.map +1 -1
  83. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AssetManagementPage.js","names":[],"sources":["../../../../src/housingvista/pages/assets/AssetManagementPage.tsx"],"sourcesContent":["import { useState, useMemo, useEffect, type FormEvent } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Building,\n DollarSign,\n TrendingUp,\n BarChart3,\n Plus,\n Download,\n ArrowUpRight,\n ArrowDownRight,\n MapPin,\n Eye,\n ImageOff,\n Percent,\n Target,\n} from 'lucide-react';\nimport { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts';\nimport {\n EmphasisPanel,\n NextSteps,\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components';\nimport { StatCard } from '../../components';\nimport { ChartCard } from '../../components';\nimport { DataTable, type Column } from '../../components';\nimport { StatusBadge } from '../../components';\nimport { FilterBar } from '../../components';\nimport { EmptyState } from '../../components';\nimport { useAssets, useCreateAsset } from '../../hooks';\nimport { useServerPagination } from '../../hooks/useServerPagination';\nimport { notify } from '../../lib/toast';\nimport { cn, formatCurrency } from '../../utils/helpers';\nimport type { Asset, PropertyType, PropertyCategory } from '../../types';\n\nconst inputClass =\n 'h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\nconst CHART_COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n];\nconst assetTooltipStyle: React.CSSProperties = {\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-subtle)',\n borderRadius: 10,\n fontSize: 12,\n boxShadow: '0 8px 24px rgb(0 0 0 / 0.12)',\n};\n\n/** Branded image with a tasteful gradient placeholder when no image is available. */\nfunction AssetImage({ src, alt, className }: { src?: string | null; alt: string; className?: string }) {\n if (src) {\n return <img src={src} alt={alt} className={cn('object-cover', className)} />;\n }\n return (\n <div\n className={cn('flex items-center justify-center bg-gradient-to-br from-bg-sunken to-bg-sunken/50', className)}\n aria-label={`${alt} — no image`}\n role=\"img\"\n >\n <ImageOff className=\"h-8 w-8 text-text-secondary/40\" />\n </div>\n );\n}\n\nconst assetTypeOptions: { value: PropertyType; label: string }[] = [\n { value: 'residential', label: 'Residential' },\n { value: 'commercial', label: 'Commercial' },\n { value: 'industrial', label: 'Industrial' },\n { value: 'agricultural', label: 'Agricultural' },\n { value: 'mixed_use', label: 'Mixed use' },\n];\n\nconst assetCategoryOptions: { value: PropertyCategory; label: string }[] = [\n { value: 'apartment', label: 'Apartment' },\n { value: 'house', label: 'House' },\n { value: 'villa', label: 'Villa' },\n { value: 'office', label: 'Office' },\n { value: 'retail', label: 'Retail' },\n { value: 'warehouse', label: 'Warehouse' },\n { value: 'land', label: 'Land' },\n { value: 'plot', label: 'Plot' },\n];\n\nconst assetStatusOptions: { value: Asset['status']; label: string }[] = [\n { value: 'active', label: 'Active' },\n { value: 'under_renovation', label: 'Under renovation' },\n { value: 'pending_sale', label: 'Pending sale' },\n { value: 'sold', label: 'Sold' },\n];\n\nfunction getStatusVariant(status: Asset['status']): 'success' | 'warning' | 'info' | 'muted' {\n switch (status) {\n case 'active':\n return 'success';\n case 'under_renovation':\n return 'warning';\n case 'pending_sale':\n return 'info';\n case 'sold':\n return 'muted';\n default:\n return 'muted';\n }\n}\n\nexport function AssetManagementPage() {\n const navigate = useNavigate();\n const pagination = useServerPagination(10);\n const [search, setSearch] = useState('');\n const [debouncedSearch, setDebouncedSearch] = useState('');\n\n // Debounce the raw search input into the value that feeds the server filter.\n useEffect(() => {\n const timer = setTimeout(() => setDebouncedSearch(search), 300);\n return () => clearTimeout(timer);\n }, [search]);\n\n const { data: assets, loading } = useAssets({\n filter: { search: debouncedSearch.trim() || undefined },\n pagination: pagination.pagination,\n });\n const { createAsset, loading: creating } = useCreateAsset();\n\n const handleSearchChange = (value: string) => {\n setSearch(value);\n pagination.setPage(1);\n };\n\n // Add-asset dialog state\n const [createOpen, setCreateOpen] = useState(false);\n const [title, setTitle] = useState('');\n const [type, setType] = useState<PropertyType>('residential');\n const [category, setCategory] = useState<PropertyCategory>('apartment');\n const [location, setLocation] = useState('');\n const [currentValue, setCurrentValue] = useState('');\n const [purchasePrice, setPurchasePrice] = useState('');\n const [purchaseDate, setPurchaseDate] = useState('');\n const [rentalYield, setRentalYield] = useState('');\n const [imageUrl, setImageUrl] = useState('');\n const [status, setStatus] = useState<Asset['status']>('active');\n\n const resetForm = () => {\n setTitle('');\n setType('residential');\n setCategory('apartment');\n setLocation('');\n setCurrentValue('');\n setPurchasePrice('');\n setPurchaseDate('');\n setRentalYield('');\n setImageUrl('');\n setStatus('active');\n };\n\n const openCreate = () => {\n resetForm();\n setCreateOpen(true);\n };\n\n const handleCreate = async (e: FormEvent) => {\n e.preventDefault();\n if (!title.trim()) {\n notify.warning('Enter an asset title');\n return;\n }\n if (!location.trim()) {\n notify.warning('Enter a location');\n return;\n }\n const current = Number(currentValue);\n const purchase = Number(purchasePrice);\n if (!Number.isFinite(current) || current <= 0) {\n notify.warning('Enter a valid current value');\n return;\n }\n if (!Number.isFinite(purchase) || purchase <= 0) {\n notify.warning('Enter a valid purchase price');\n return;\n }\n const yieldValue = rentalYield.trim() ? Number(rentalYield) : undefined;\n if (yieldValue !== undefined && !Number.isFinite(yieldValue)) {\n notify.warning('Enter a valid rental yield');\n return;\n }\n // Appreciation is derived from the real purchase vs. current value, never fabricated.\n const appreciationPercent = purchase > 0 ? ((current - purchase) / purchase) * 100 : 0;\n try {\n await createAsset({\n title: title.trim(),\n type,\n category,\n location: location.trim(),\n currentValue: current,\n purchasePrice: purchase,\n purchaseDate: purchaseDate ? new Date(purchaseDate).toISOString() : undefined,\n appreciationPercent: Number(appreciationPercent.toFixed(2)),\n rentalYield: yieldValue,\n status,\n imageUrl: imageUrl.trim() || undefined,\n });\n notify.success('Asset added', `${title.trim()} was added to your portfolio.`);\n setCreateOpen(false);\n resetForm();\n } catch (err) {\n notify.error('Could not add asset', (err as Error).message);\n }\n };\n\n const handleExport = () => {\n if (assets.length === 0) {\n notify.warning('Nothing to export', 'There are no assets to export yet.');\n return;\n }\n const escape = (value: string) => `\"${value.replace(/\"/g, '\"\"')}\"`;\n const headers = [\n 'Title',\n 'Type',\n 'Category',\n 'Location',\n 'Purchase Price',\n 'Current Value',\n 'Appreciation %',\n 'Rental Yield %',\n 'Status',\n ];\n const rows = assets.map((a) =>\n [\n a.title,\n a.type,\n a.category,\n a.location,\n String(a.purchasePrice),\n String(a.currentValue),\n String(a.appreciationPercent),\n a.rentalYield != null ? String(a.rentalYield) : '',\n a.status,\n ]\n .map((cell) => escape(String(cell)))\n .join(',')\n );\n const csv = [headers.map(escape).join(','), ...rows].join('\\n');\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = `housingvista-assets-${new Date().toISOString().slice(0, 10)}.csv`;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n notify.success('Export ready', `${assets.length} assets exported to CSV.`);\n };\n\n const portfolioStats = useMemo(() => {\n const totalValue = assets.reduce((sum, a) => sum + a.currentValue, 0);\n const totalMonthlyIncome = assets\n .filter((a) => a.rentalYield)\n .reduce((sum, a) => sum + (a.currentValue * (a.rentalYield || 0)) / 100 / 12, 0);\n const avgAppreciation =\n assets.length > 0 ? assets.reduce((sum, a) => sum + a.appreciationPercent, 0) / assets.length : 0;\n const totalPurchasePrice = assets.reduce((sum, a) => sum + a.purchasePrice, 0);\n const totalROI = totalPurchasePrice > 0 ? ((totalValue - totalPurchasePrice) / totalPurchasePrice) * 100 : 0;\n return { totalValue, totalMonthlyIncome, avgAppreciation, totalROI };\n }, [assets]);\n\n // Portfolio value split by property type — real current values, never fabricated.\n const valueByType = useMemo(() => {\n const byType = new Map<string, number>();\n for (const asset of assets) {\n byType.set(asset.type, (byType.get(asset.type) ?? 0) + asset.currentValue);\n }\n const rows = Array.from(byType.entries())\n .filter(([, value]) => value > 0)\n .map(([type, value], idx) => ({\n type,\n label: type.charAt(0).toUpperCase() + type.slice(1).replace('_', ' '),\n value,\n color: CHART_COLORS[idx % CHART_COLORS.length],\n }))\n .sort((a, b) => b.value - a.value);\n const total = rows.reduce((s, r) => s + r.value, 0);\n return { rows, total };\n }, [assets]);\n\n const assetColumns: Column<Asset>[] = [\n {\n key: 'title',\n header: 'Property',\n render: (item) => (\n <div className=\"flex items-center gap-3\">\n <AssetImage src={item.imageUrl} alt={item.title} className=\"h-10 w-10 shrink-0 rounded-md\" />\n <div>\n <div className=\"font-medium text-text-primary\">{item.title}</div>\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <MapPin className=\"h-3 w-3\" />\n {item.location}\n </div>\n </div>\n </div>\n ),\n },\n {\n key: 'type',\n header: 'Type',\n render: (item) => <span className=\"text-sm capitalize text-text-primary\">{item.type}</span>,\n },\n {\n key: 'purchasePrice',\n header: 'Purchase Price',\n render: (item) => <span className=\"text-sm text-text-primary\">{formatCurrency(item.purchasePrice)}</span>,\n },\n {\n key: 'currentValue',\n header: 'Current Value',\n render: (item) => (\n <span className=\"text-sm font-medium text-text-primary\">{formatCurrency(item.currentValue)}</span>\n ),\n },\n {\n key: 'appreciationPercent',\n header: 'Change',\n render: (item) => {\n const change = item.currentValue - item.purchasePrice;\n const isPositive = change >= 0;\n return (\n <div\n className={cn(\n 'flex items-center gap-1 text-sm font-medium',\n isPositive ? 'text-status-success-text' : 'text-status-error-text'\n )}\n >\n {isPositive ? <ArrowUpRight className=\"h-3 w-3\" /> : <ArrowDownRight className=\"h-3 w-3\" />}\n {formatCurrency(Math.abs(change))}\n <span className=\"text-xs\">({item.appreciationPercent}%)</span>\n </div>\n );\n },\n },\n {\n key: 'status',\n header: 'Status',\n render: (item) => <StatusBadge label={item.status.replace('_', ' ')} variant={getStatusVariant(item.status)} />,\n },\n {\n key: 'actions',\n header: '',\n sortable: false,\n render: (item) => (\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(`../assets/${item.id}`);\n }}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-2 py-1 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Eye className=\"h-3 w-3\" />\n View\n </button>\n ),\n },\n ];\n\n return (\n <PageLayout\n title=\"Asset Management\"\n description=\"Monitor and manage your property portfolio performance\"\n purpose=\"See how every property you own is performing as an investment — current value, appreciation, rental yield, and overall ROI in one portfolio view. Use it to decide what to hold, renovate, or sell.\"\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n onClick={handleExport}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Download className=\"h-4 w-4\" />\n Export\n </button>\n <button\n onClick={openCreate}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg-hover\"\n >\n <Plus className=\"h-4 w-4\" />\n Add Asset\n </button>\n </div>\n }\n >\n {/* Portfolio Value Stats — primary insight zone (one EmphasisPanel per page) */}\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n label=\"Total Value\"\n value={portfolioStats.totalValue}\n prefix=\"$\"\n icon={<DollarSign className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Monthly Income\"\n value={Math.round(portfolioStats.totalMonthlyIncome)}\n prefix=\"$\"\n icon={<TrendingUp className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Avg. Appreciation\"\n value={`${portfolioStats.avgAppreciation.toFixed(1)}%`}\n icon={<BarChart3 className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Overall ROI\"\n value={`${portfolioStats.totalROI.toFixed(1)}%`}\n icon={<Building className=\"h-4 w-4\" />}\n />\n </div>\n </EmphasisPanel>\n\n <NextSteps\n storageKey=\"housingvista-assets\"\n steps={[\n {\n id: 'open-asset',\n label: 'Open an asset to see full performance',\n description: 'Drill into value history, yield, and ROI for any property.',\n onClick: () => {\n const first = assets[0];\n if (first) navigate(`../assets/${first.id}`);\n },\n },\n {\n id: 'compare-market',\n label: 'Compare against market trends',\n description: 'Check market analytics to benchmark your portfolio.',\n onClick: () => navigate('../market-analytics'),\n },\n {\n id: 'add-asset',\n label: 'Add a property you already own',\n description: 'Bring an existing holding into your portfolio view.',\n onClick: openCreate,\n },\n ]}\n />\n\n {/* Portfolio overview: rich asset cards (left) + value-by-type donut (aside) */}\n {assets.length > 0 && (\n <div className=\"grid grid-cols-1 gap-6 xl:grid-cols-3\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:col-span-2\">\n {assets.map((asset) => {\n const change = asset.currentValue - asset.purchasePrice;\n const isPositive = change >= 0;\n const roi = asset.purchasePrice > 0 ? (change / asset.purchasePrice) * 100 : 0;\n return (\n <button\n key={asset.id}\n type=\"button\"\n onClick={() => navigate(`../assets/${asset.id}`)}\n className=\"group overflow-hidden rounded-card border border-border-seam bg-bg-surface text-left shadow-elevation-1 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-elevation-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <div className=\"relative aspect-[16/9] overflow-hidden\">\n <AssetImage src={asset.imageUrl} alt={asset.title} className=\"h-full w-full\" />\n <div className=\"absolute left-3 top-3\">\n <StatusBadge\n label={asset.status.replace('_', ' ')}\n variant={getStatusVariant(asset.status)}\n dot\n />\n </div>\n <div\n className={cn(\n 'absolute right-3 top-3 inline-flex items-center gap-0.5 rounded-full px-2 py-0.5 text-xs font-semibold backdrop-blur-sm',\n isPositive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-status-error-bg-subtle text-status-error-text'\n )}\n >\n {isPositive ? <ArrowUpRight className=\"h-3 w-3\" /> : <ArrowDownRight className=\"h-3 w-3\" />}\n {asset.appreciationPercent}%\n </div>\n </div>\n <div className=\"p-4\">\n <h3 className=\"truncate font-medium text-text-primary\">{asset.title}</h3>\n <p className=\"mt-0.5 flex items-center gap-1 text-xs text-text-secondary\">\n <MapPin className=\"h-3 w-3 shrink-0\" />\n <span className=\"truncate\">{asset.location}</span>\n </p>\n <div className=\"mt-3 flex items-end justify-between\">\n <div>\n <p className=\"text-xs text-text-secondary\">Current Value</p>\n <p className=\"text-lg font-bold text-text-primary\">{formatCurrency(asset.currentValue)}</p>\n </div>\n <span className=\"rounded-md bg-bg-sunken px-2 py-1 text-xs font-medium capitalize text-text-secondary\">\n {asset.category}\n </span>\n </div>\n <div className=\"mt-3 grid grid-cols-2 gap-3 border-t border-border-subtle pt-3 text-xs\">\n <div className=\"flex items-center gap-1.5 text-text-secondary\">\n <Percent className=\"h-3.5 w-3.5\" />\n <span>Yield</span>\n <span className=\"ml-auto font-semibold text-text-primary\">\n {asset.rentalYield != null ? `${asset.rentalYield}%` : '—'}\n </span>\n </div>\n <div className=\"flex items-center gap-1.5 text-text-secondary\">\n <Target className=\"h-3.5 w-3.5\" />\n <span>ROI</span>\n <span\n className={cn(\n 'ml-auto font-semibold',\n roi >= 0 ? 'text-status-success-text' : 'text-status-error-text'\n )}\n >\n {roi.toFixed(1)}%\n </span>\n </div>\n </div>\n </div>\n </button>\n );\n })}\n </div>\n\n {valueByType.rows.length > 0 && (\n <aside className=\"xl:col-span-1\">\n <ChartCard title=\"Portfolio by Type\" description=\"Current value split across property types\">\n <div className=\"relative h-52 w-full\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <PieChart>\n <Pie\n data={valueByType.rows}\n dataKey=\"value\"\n nameKey=\"label\"\n innerRadius={56}\n outerRadius={82}\n paddingAngle={2}\n stroke=\"var(--color-bg-surface)\"\n strokeWidth={2}\n >\n {valueByType.rows.map((entry) => (\n <Cell key={entry.type} fill={entry.color} />\n ))}\n </Pie>\n <Tooltip\n contentStyle={assetTooltipStyle}\n formatter={(value: number, _n, item) => [formatCurrency(value), item?.payload?.label]}\n />\n </PieChart>\n </ResponsiveContainer>\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center\">\n <span className=\"text-xs text-text-secondary\">Total value</span>\n <span className=\"text-lg font-bold text-text-primary\">{formatCurrency(valueByType.total)}</span>\n </div>\n </div>\n <ul className=\"mt-4 space-y-2.5\">\n {valueByType.rows.map((entry) => (\n <li key={entry.type} className=\"flex items-center justify-between text-sm\">\n <span className=\"flex items-center gap-2\">\n <span className=\"h-2.5 w-2.5 rounded-full\" style={{ background: entry.color }} />\n <span className=\"text-text-primary\">{entry.label}</span>\n </span>\n <span className=\"font-medium text-text-secondary\">\n {valueByType.total > 0 ? Math.round((entry.value / valueByType.total) * 100) : 0}%\n </span>\n </li>\n ))}\n </ul>\n </ChartCard>\n </aside>\n )}\n </div>\n )}\n\n {/* Asset Table */}\n <div>\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">All Assets</h2>\n <FilterBar\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder=\"Search assets by name, location, or type...\"\n />\n <div className=\"mt-4\">\n {assets.length > 0 ? (\n <DataTable\n columns={assetColumns}\n data={assets}\n keyField=\"id\"\n onRowClick={(item) => navigate(`../assets/${item.id}`)}\n pageSize={10}\n serverPagination={{\n page: pagination.page,\n pageSize: pagination.pageSize,\n hasNextPage: assets.length >= pagination.pageSize,\n loading,\n onPageChange: pagination.setPage,\n onPageSizeChange: pagination.setPageSize,\n }}\n />\n ) : (\n <EmptyState\n illustration=\"empty-search\"\n icon={<Building className=\"h-12 w-12\" />}\n title=\"No assets found\"\n description=\"Try adjusting your search criteria or add a new asset.\"\n action={\n <button\n onClick={openCreate}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg-hover\"\n >\n <Plus className=\"h-4 w-4\" />\n Add Asset\n </button>\n }\n />\n )}\n </div>\n </div>\n\n {/* Add Asset dialog */}\n <ResponsiveDialog open={createOpen} onOpenChange={setCreateOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <form onSubmit={handleCreate}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add asset</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Bring a property you own into your portfolio to track its value and performance.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Title *</label>\n <input\n type=\"text\"\n required\n placeholder=\"e.g. Riverside Villa\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Location *</label>\n <input\n type=\"text\"\n required\n placeholder=\"City, State\"\n value={location}\n onChange={(e) => setLocation(e.target.value)}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Type</label>\n <select value={type} onChange={(e) => setType(e.target.value as PropertyType)} className={inputClass}>\n {assetTypeOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Category</label>\n <select\n value={category}\n onChange={(e) => setCategory(e.target.value as PropertyCategory)}\n className={inputClass}\n >\n {assetCategoryOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Purchase price (USD) *</label>\n <input\n type=\"number\"\n required\n min={1}\n step=\"any\"\n placeholder=\"0\"\n value={purchasePrice}\n onChange={(e) => setPurchasePrice(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Current value (USD) *</label>\n <input\n type=\"number\"\n required\n min={1}\n step=\"any\"\n placeholder=\"0\"\n value={currentValue}\n onChange={(e) => setCurrentValue(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Purchase date</label>\n <input\n type=\"date\"\n value={purchaseDate}\n onChange={(e) => setPurchaseDate(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Rental yield (%)</label>\n <input\n type=\"number\"\n min={0}\n step=\"any\"\n placeholder=\"0\"\n value={rentalYield}\n onChange={(e) => setRentalYield(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Status</label>\n <select\n value={status}\n onChange={(e) => setStatus(e.target.value as Asset['status'])}\n className={inputClass}\n >\n {assetStatusOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Image URL</label>\n <input\n type=\"url\"\n placeholder=\"https://…\"\n value={imageUrl}\n onChange={(e) => setImageUrl(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setCreateOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={creating || !title.trim() || !location.trim()}>\n {creating ? 'Adding…' : 'Add asset'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AssetManagementPage.js","names":[],"sources":["../../../../src/housingvista/pages/assets/AssetManagementPage.tsx"],"sourcesContent":["import { useState, useMemo, useEffect, type FormEvent } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Building,\n DollarSign,\n TrendingUp,\n BarChart3,\n Plus,\n Download,\n ArrowUpRight,\n ArrowDownRight,\n MapPin,\n Eye,\n ImageOff,\n Percent,\n Target,\n} from 'lucide-react';\nimport { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts';\nimport {\n EmphasisPanel,\n NextSteps,\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components';\nimport { StatCard } from '../../components';\nimport { ChartCard } from '../../components';\nimport { DataTable, type Column } from '../../components';\nimport { StatusBadge } from '../../components';\nimport { FilterBar } from '../../components';\nimport { EmptyState } from '../../components';\nimport { useAssets, useCreateAsset } from '../../hooks';\nimport { useServerPagination } from '../../hooks/useServerPagination';\nimport { notify } from '../../lib/toast';\nimport { cn, formatCurrency } from '../../utils/helpers';\nimport type { Asset, PropertyType, PropertyCategory } from '../../types';\nimport { useTrackPageView } from '../../hooks/useHousingVistaAnalytics';\n\nconst inputClass =\n 'h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\nconst CHART_COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n];\nconst assetTooltipStyle: React.CSSProperties = {\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-subtle)',\n borderRadius: 10,\n fontSize: 12,\n boxShadow: '0 8px 24px rgb(0 0 0 / 0.12)',\n};\n\n/** Branded image with a tasteful gradient placeholder when no image is available. */\nfunction AssetImage({ src, alt, className }: { src?: string | null; alt: string; className?: string }) {\n if (src) {\n return <img src={src} alt={alt} className={cn('object-cover', className)} />;\n }\n return (\n <div\n className={cn('flex items-center justify-center bg-gradient-to-br from-bg-sunken to-bg-sunken/50', className)}\n aria-label={`${alt} — no image`}\n role=\"img\"\n >\n <ImageOff className=\"h-8 w-8 text-text-secondary/40\" />\n </div>\n );\n}\n\nconst assetTypeOptions: { value: PropertyType; label: string }[] = [\n { value: 'residential', label: 'Residential' },\n { value: 'commercial', label: 'Commercial' },\n { value: 'industrial', label: 'Industrial' },\n { value: 'agricultural', label: 'Agricultural' },\n { value: 'mixed_use', label: 'Mixed use' },\n];\n\nconst assetCategoryOptions: { value: PropertyCategory; label: string }[] = [\n { value: 'apartment', label: 'Apartment' },\n { value: 'house', label: 'House' },\n { value: 'villa', label: 'Villa' },\n { value: 'office', label: 'Office' },\n { value: 'retail', label: 'Retail' },\n { value: 'warehouse', label: 'Warehouse' },\n { value: 'land', label: 'Land' },\n { value: 'plot', label: 'Plot' },\n];\n\nconst assetStatusOptions: { value: Asset['status']; label: string }[] = [\n { value: 'active', label: 'Active' },\n { value: 'under_renovation', label: 'Under renovation' },\n { value: 'pending_sale', label: 'Pending sale' },\n { value: 'sold', label: 'Sold' },\n];\n\nfunction getStatusVariant(status: Asset['status']): 'success' | 'warning' | 'info' | 'muted' {\n switch (status) {\n case 'active':\n return 'success';\n case 'under_renovation':\n return 'warning';\n case 'pending_sale':\n return 'info';\n case 'sold':\n return 'muted';\n default:\n return 'muted';\n }\n}\n\nexport function AssetManagementPage() {\n useTrackPageView('housingvista.asset.viewed');\n const navigate = useNavigate();\n const pagination = useServerPagination(10);\n const [search, setSearch] = useState('');\n const [debouncedSearch, setDebouncedSearch] = useState('');\n\n // Debounce the raw search input into the value that feeds the server filter.\n useEffect(() => {\n const timer = setTimeout(() => setDebouncedSearch(search), 300);\n return () => clearTimeout(timer);\n }, [search]);\n\n const { data: assets, loading } = useAssets({\n filter: { search: debouncedSearch.trim() || undefined },\n pagination: pagination.pagination,\n });\n const { createAsset, loading: creating } = useCreateAsset();\n\n const handleSearchChange = (value: string) => {\n setSearch(value);\n pagination.setPage(1);\n };\n\n // Add-asset dialog state\n const [createOpen, setCreateOpen] = useState(false);\n const [title, setTitle] = useState('');\n const [type, setType] = useState<PropertyType>('residential');\n const [category, setCategory] = useState<PropertyCategory>('apartment');\n const [location, setLocation] = useState('');\n const [currentValue, setCurrentValue] = useState('');\n const [purchasePrice, setPurchasePrice] = useState('');\n const [purchaseDate, setPurchaseDate] = useState('');\n const [rentalYield, setRentalYield] = useState('');\n const [imageUrl, setImageUrl] = useState('');\n const [status, setStatus] = useState<Asset['status']>('active');\n\n const resetForm = () => {\n setTitle('');\n setType('residential');\n setCategory('apartment');\n setLocation('');\n setCurrentValue('');\n setPurchasePrice('');\n setPurchaseDate('');\n setRentalYield('');\n setImageUrl('');\n setStatus('active');\n };\n\n const openCreate = () => {\n resetForm();\n setCreateOpen(true);\n };\n\n const handleCreate = async (e: FormEvent) => {\n e.preventDefault();\n if (!title.trim()) {\n notify.warning('Enter an asset title');\n return;\n }\n if (!location.trim()) {\n notify.warning('Enter a location');\n return;\n }\n const current = Number(currentValue);\n const purchase = Number(purchasePrice);\n if (!Number.isFinite(current) || current <= 0) {\n notify.warning('Enter a valid current value');\n return;\n }\n if (!Number.isFinite(purchase) || purchase <= 0) {\n notify.warning('Enter a valid purchase price');\n return;\n }\n const yieldValue = rentalYield.trim() ? Number(rentalYield) : undefined;\n if (yieldValue !== undefined && !Number.isFinite(yieldValue)) {\n notify.warning('Enter a valid rental yield');\n return;\n }\n // Appreciation is derived from the real purchase vs. current value, never fabricated.\n const appreciationPercent = purchase > 0 ? ((current - purchase) / purchase) * 100 : 0;\n try {\n await createAsset({\n title: title.trim(),\n type,\n category,\n location: location.trim(),\n currentValue: current,\n purchasePrice: purchase,\n purchaseDate: purchaseDate ? new Date(purchaseDate).toISOString() : undefined,\n appreciationPercent: Number(appreciationPercent.toFixed(2)),\n rentalYield: yieldValue,\n status,\n imageUrl: imageUrl.trim() || undefined,\n });\n notify.success('Asset added', `${title.trim()} was added to your portfolio.`);\n setCreateOpen(false);\n resetForm();\n } catch (err) {\n notify.error('Could not add asset', (err as Error).message);\n }\n };\n\n const handleExport = () => {\n if (assets.length === 0) {\n notify.warning('Nothing to export', 'There are no assets to export yet.');\n return;\n }\n const escape = (value: string) => `\"${value.replace(/\"/g, '\"\"')}\"`;\n const headers = [\n 'Title',\n 'Type',\n 'Category',\n 'Location',\n 'Purchase Price',\n 'Current Value',\n 'Appreciation %',\n 'Rental Yield %',\n 'Status',\n ];\n const rows = assets.map((a) =>\n [\n a.title,\n a.type,\n a.category,\n a.location,\n String(a.purchasePrice),\n String(a.currentValue),\n String(a.appreciationPercent),\n a.rentalYield != null ? String(a.rentalYield) : '',\n a.status,\n ]\n .map((cell) => escape(String(cell)))\n .join(',')\n );\n const csv = [headers.map(escape).join(','), ...rows].join('\\n');\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = `housingvista-assets-${new Date().toISOString().slice(0, 10)}.csv`;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n notify.success('Export ready', `${assets.length} assets exported to CSV.`);\n };\n\n const portfolioStats = useMemo(() => {\n const totalValue = assets.reduce((sum, a) => sum + a.currentValue, 0);\n const totalMonthlyIncome = assets\n .filter((a) => a.rentalYield)\n .reduce((sum, a) => sum + (a.currentValue * (a.rentalYield || 0)) / 100 / 12, 0);\n const avgAppreciation =\n assets.length > 0 ? assets.reduce((sum, a) => sum + a.appreciationPercent, 0) / assets.length : 0;\n const totalPurchasePrice = assets.reduce((sum, a) => sum + a.purchasePrice, 0);\n const totalROI = totalPurchasePrice > 0 ? ((totalValue - totalPurchasePrice) / totalPurchasePrice) * 100 : 0;\n return { totalValue, totalMonthlyIncome, avgAppreciation, totalROI };\n }, [assets]);\n\n // Portfolio value split by property type — real current values, never fabricated.\n const valueByType = useMemo(() => {\n const byType = new Map<string, number>();\n for (const asset of assets) {\n byType.set(asset.type, (byType.get(asset.type) ?? 0) + asset.currentValue);\n }\n const rows = Array.from(byType.entries())\n .filter(([, value]) => value > 0)\n .map(([type, value], idx) => ({\n type,\n label: type.charAt(0).toUpperCase() + type.slice(1).replace('_', ' '),\n value,\n color: CHART_COLORS[idx % CHART_COLORS.length],\n }))\n .sort((a, b) => b.value - a.value);\n const total = rows.reduce((s, r) => s + r.value, 0);\n return { rows, total };\n }, [assets]);\n\n const assetColumns: Column<Asset>[] = [\n {\n key: 'title',\n header: 'Property',\n render: (item) => (\n <div className=\"flex items-center gap-3\">\n <AssetImage src={item.imageUrl} alt={item.title} className=\"h-10 w-10 shrink-0 rounded-md\" />\n <div>\n <div className=\"font-medium text-text-primary\">{item.title}</div>\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <MapPin className=\"h-3 w-3\" />\n {item.location}\n </div>\n </div>\n </div>\n ),\n },\n {\n key: 'type',\n header: 'Type',\n render: (item) => <span className=\"text-sm capitalize text-text-primary\">{item.type}</span>,\n },\n {\n key: 'purchasePrice',\n header: 'Purchase Price',\n render: (item) => <span className=\"text-sm text-text-primary\">{formatCurrency(item.purchasePrice)}</span>,\n },\n {\n key: 'currentValue',\n header: 'Current Value',\n render: (item) => (\n <span className=\"text-sm font-medium text-text-primary\">{formatCurrency(item.currentValue)}</span>\n ),\n },\n {\n key: 'appreciationPercent',\n header: 'Change',\n render: (item) => {\n const change = item.currentValue - item.purchasePrice;\n const isPositive = change >= 0;\n return (\n <div\n className={cn(\n 'flex items-center gap-1 text-sm font-medium',\n isPositive ? 'text-status-success-text' : 'text-status-error-text'\n )}\n >\n {isPositive ? <ArrowUpRight className=\"h-3 w-3\" /> : <ArrowDownRight className=\"h-3 w-3\" />}\n {formatCurrency(Math.abs(change))}\n <span className=\"text-xs\">({item.appreciationPercent}%)</span>\n </div>\n );\n },\n },\n {\n key: 'status',\n header: 'Status',\n render: (item) => <StatusBadge label={item.status.replace('_', ' ')} variant={getStatusVariant(item.status)} />,\n },\n {\n key: 'actions',\n header: '',\n sortable: false,\n render: (item) => (\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(`../assets/${item.id}`);\n }}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-2 py-1 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Eye className=\"h-3 w-3\" />\n View\n </button>\n ),\n },\n ];\n\n return (\n <PageLayout\n title=\"Asset Management\"\n description=\"Monitor and manage your property portfolio performance\"\n purpose=\"See how every property you own is performing as an investment — current value, appreciation, rental yield, and overall ROI in one portfolio view. Use it to decide what to hold, renovate, or sell.\"\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n onClick={handleExport}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Download className=\"h-4 w-4\" />\n Export\n </button>\n <button\n onClick={openCreate}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg-hover\"\n >\n <Plus className=\"h-4 w-4\" />\n Add Asset\n </button>\n </div>\n }\n >\n {/* Portfolio Value Stats — primary insight zone (one EmphasisPanel per page) */}\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n label=\"Total Value\"\n value={portfolioStats.totalValue}\n prefix=\"$\"\n icon={<DollarSign className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Monthly Income\"\n value={Math.round(portfolioStats.totalMonthlyIncome)}\n prefix=\"$\"\n icon={<TrendingUp className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Avg. Appreciation\"\n value={`${portfolioStats.avgAppreciation.toFixed(1)}%`}\n icon={<BarChart3 className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Overall ROI\"\n value={`${portfolioStats.totalROI.toFixed(1)}%`}\n icon={<Building className=\"h-4 w-4\" />}\n />\n </div>\n </EmphasisPanel>\n\n <NextSteps\n storageKey=\"housingvista-assets\"\n steps={[\n {\n id: 'open-asset',\n label: 'Open an asset to see full performance',\n description: 'Drill into value history, yield, and ROI for any property.',\n onClick: () => {\n const first = assets[0];\n if (first) navigate(`../assets/${first.id}`);\n },\n },\n {\n id: 'compare-market',\n label: 'Compare against market trends',\n description: 'Check market analytics to benchmark your portfolio.',\n onClick: () => navigate('../market-analytics'),\n },\n {\n id: 'add-asset',\n label: 'Add a property you already own',\n description: 'Bring an existing holding into your portfolio view.',\n onClick: openCreate,\n },\n ]}\n />\n\n {/* Portfolio overview: rich asset cards (left) + value-by-type donut (aside) */}\n {assets.length > 0 && (\n <div className=\"grid grid-cols-1 gap-6 xl:grid-cols-3\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:col-span-2\">\n {assets.map((asset) => {\n const change = asset.currentValue - asset.purchasePrice;\n const isPositive = change >= 0;\n const roi = asset.purchasePrice > 0 ? (change / asset.purchasePrice) * 100 : 0;\n return (\n <button\n key={asset.id}\n type=\"button\"\n onClick={() => navigate(`../assets/${asset.id}`)}\n className=\"group overflow-hidden rounded-card border border-border-seam bg-bg-surface text-left shadow-elevation-1 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-elevation-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <div className=\"relative aspect-[16/9] overflow-hidden\">\n <AssetImage src={asset.imageUrl} alt={asset.title} className=\"h-full w-full\" />\n <div className=\"absolute left-3 top-3\">\n <StatusBadge\n label={asset.status.replace('_', ' ')}\n variant={getStatusVariant(asset.status)}\n dot\n />\n </div>\n <div\n className={cn(\n 'absolute right-3 top-3 inline-flex items-center gap-0.5 rounded-full px-2 py-0.5 text-xs font-semibold backdrop-blur-sm',\n isPositive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-status-error-bg-subtle text-status-error-text'\n )}\n >\n {isPositive ? <ArrowUpRight className=\"h-3 w-3\" /> : <ArrowDownRight className=\"h-3 w-3\" />}\n {asset.appreciationPercent}%\n </div>\n </div>\n <div className=\"p-4\">\n <h3 className=\"truncate font-medium text-text-primary\">{asset.title}</h3>\n <p className=\"mt-0.5 flex items-center gap-1 text-xs text-text-secondary\">\n <MapPin className=\"h-3 w-3 shrink-0\" />\n <span className=\"truncate\">{asset.location}</span>\n </p>\n <div className=\"mt-3 flex items-end justify-between\">\n <div>\n <p className=\"text-xs text-text-secondary\">Current Value</p>\n <p className=\"text-lg font-bold text-text-primary\">{formatCurrency(asset.currentValue)}</p>\n </div>\n <span className=\"rounded-md bg-bg-sunken px-2 py-1 text-xs font-medium capitalize text-text-secondary\">\n {asset.category}\n </span>\n </div>\n <div className=\"mt-3 grid grid-cols-2 gap-3 border-t border-border-subtle pt-3 text-xs\">\n <div className=\"flex items-center gap-1.5 text-text-secondary\">\n <Percent className=\"h-3.5 w-3.5\" />\n <span>Yield</span>\n <span className=\"ml-auto font-semibold text-text-primary\">\n {asset.rentalYield != null ? `${asset.rentalYield}%` : '—'}\n </span>\n </div>\n <div className=\"flex items-center gap-1.5 text-text-secondary\">\n <Target className=\"h-3.5 w-3.5\" />\n <span>ROI</span>\n <span\n className={cn(\n 'ml-auto font-semibold',\n roi >= 0 ? 'text-status-success-text' : 'text-status-error-text'\n )}\n >\n {roi.toFixed(1)}%\n </span>\n </div>\n </div>\n </div>\n </button>\n );\n })}\n </div>\n\n {valueByType.rows.length > 0 && (\n <aside className=\"xl:col-span-1\">\n <ChartCard title=\"Portfolio by Type\" description=\"Current value split across property types\">\n <div className=\"relative h-52 w-full\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <PieChart>\n <Pie\n data={valueByType.rows}\n dataKey=\"value\"\n nameKey=\"label\"\n innerRadius={56}\n outerRadius={82}\n paddingAngle={2}\n stroke=\"var(--color-bg-surface)\"\n strokeWidth={2}\n >\n {valueByType.rows.map((entry) => (\n <Cell key={entry.type} fill={entry.color} />\n ))}\n </Pie>\n <Tooltip\n contentStyle={assetTooltipStyle}\n formatter={(value: number, _n, item) => [formatCurrency(value), item?.payload?.label]}\n />\n </PieChart>\n </ResponsiveContainer>\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center\">\n <span className=\"text-xs text-text-secondary\">Total value</span>\n <span className=\"text-lg font-bold text-text-primary\">{formatCurrency(valueByType.total)}</span>\n </div>\n </div>\n <ul className=\"mt-4 space-y-2.5\">\n {valueByType.rows.map((entry) => (\n <li key={entry.type} className=\"flex items-center justify-between text-sm\">\n <span className=\"flex items-center gap-2\">\n <span className=\"h-2.5 w-2.5 rounded-full\" style={{ background: entry.color }} />\n <span className=\"text-text-primary\">{entry.label}</span>\n </span>\n <span className=\"font-medium text-text-secondary\">\n {valueByType.total > 0 ? Math.round((entry.value / valueByType.total) * 100) : 0}%\n </span>\n </li>\n ))}\n </ul>\n </ChartCard>\n </aside>\n )}\n </div>\n )}\n\n {/* Asset Table */}\n <div>\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">All Assets</h2>\n <FilterBar\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder=\"Search assets by name, location, or type...\"\n />\n <div className=\"mt-4\">\n {assets.length > 0 ? (\n <DataTable\n columns={assetColumns}\n data={assets}\n keyField=\"id\"\n onRowClick={(item) => navigate(`../assets/${item.id}`)}\n pageSize={10}\n serverPagination={{\n page: pagination.page,\n pageSize: pagination.pageSize,\n hasNextPage: assets.length >= pagination.pageSize,\n loading,\n onPageChange: pagination.setPage,\n onPageSizeChange: pagination.setPageSize,\n }}\n />\n ) : (\n <EmptyState\n illustration=\"empty-search\"\n icon={<Building className=\"h-12 w-12\" />}\n title=\"No assets found\"\n description=\"Try adjusting your search criteria or add a new asset.\"\n action={\n <button\n onClick={openCreate}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg-hover\"\n >\n <Plus className=\"h-4 w-4\" />\n Add Asset\n </button>\n }\n />\n )}\n </div>\n </div>\n\n {/* Add Asset dialog */}\n <ResponsiveDialog open={createOpen} onOpenChange={setCreateOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <form onSubmit={handleCreate}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add asset</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Bring a property you own into your portfolio to track its value and performance.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Title *</label>\n <input\n type=\"text\"\n required\n placeholder=\"e.g. Riverside Villa\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Location *</label>\n <input\n type=\"text\"\n required\n placeholder=\"City, State\"\n value={location}\n onChange={(e) => setLocation(e.target.value)}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Type</label>\n <select value={type} onChange={(e) => setType(e.target.value as PropertyType)} className={inputClass}>\n {assetTypeOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Category</label>\n <select\n value={category}\n onChange={(e) => setCategory(e.target.value as PropertyCategory)}\n className={inputClass}\n >\n {assetCategoryOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Purchase price (USD) *</label>\n <input\n type=\"number\"\n required\n min={1}\n step=\"any\"\n placeholder=\"0\"\n value={purchasePrice}\n onChange={(e) => setPurchasePrice(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Current value (USD) *</label>\n <input\n type=\"number\"\n required\n min={1}\n step=\"any\"\n placeholder=\"0\"\n value={currentValue}\n onChange={(e) => setCurrentValue(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Purchase date</label>\n <input\n type=\"date\"\n value={purchaseDate}\n onChange={(e) => setPurchaseDate(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Rental yield (%)</label>\n <input\n type=\"number\"\n min={0}\n step=\"any\"\n placeholder=\"0\"\n value={rentalYield}\n onChange={(e) => setRentalYield(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Status</label>\n <select\n value={status}\n onChange={(e) => setStatus(e.target.value as Asset['status'])}\n className={inputClass}\n >\n {assetStatusOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Image URL</label>\n <input\n type=\"url\"\n placeholder=\"https://…\"\n value={imageUrl}\n onChange={(e) => setImageUrl(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setCreateOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={creating || !title.trim() || !location.trim()}>\n {creating ? 'Adding…' : 'Add asset'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ChatPage.d.ts","sourceRoot":"","sources":["../../../../src/housingvista/pages/chat/ChatPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAuDH,wBAAgB,QAAQ,gCA4PvB"}
1
+ {"version":3,"file":"ChatPage.d.ts","sourceRoot":"","sources":["../../../../src/housingvista/pages/chat/ChatPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAwDH,wBAAgB,QAAQ,gCA6PvB"}
@@ -4,154 +4,156 @@ import { PageLayout as r } from "../../components/PageLayout.js";
4
4
  import { StatCard as i } from "../../components/StatCard.js";
5
5
  import { EmptyState as a } from "../../components/EmptyState.js";
6
6
  import { useConversationMessages as o, useSendMessage as s } from "../../hooks/platform.js";
7
+ import { useTrackPageView as c } from "../../hooks/useHousingVistaAnalytics.js";
7
8
  import "../../hooks/index.js";
8
- import { notify as c } from "../../lib/toast.js";
9
+ import { notify as l } from "../../lib/toast.js";
9
10
  import "../../components/index.js";
10
- import { useHousingVistaConversations as l } from "../../hooks/messages.js";
11
- import { useEffect as u, useMemo as d, useRef as f, useState as p } from "react";
12
- import { ArrowLeft as m, MapPin as h, MessageSquare as g, Search as _, Send as v } from "lucide-react";
13
- import { useNavigate as y } from "react-router-dom";
14
- import { Fragment as b, jsx as x, jsxs as S } from "react/jsx-runtime";
15
- import { Button as C, EmphasisPanel as w, GlassCard as T } from "@burdenoff/fe-libs/ui";
11
+ import { useHousingVistaConversations as u } from "../../hooks/messages.js";
12
+ import { useEffect as d, useMemo as f, useRef as p, useState as m } from "react";
13
+ import { ArrowLeft as h, MapPin as g, MessageSquare as _, Search as v, Send as y } from "lucide-react";
14
+ import { useNavigate as b } from "react-router-dom";
15
+ import { Fragment as x, jsx as S, jsxs as C } from "react/jsx-runtime";
16
+ import { Button as w, EmphasisPanel as T, GlassCard as E } from "@burdenoff/fe-libs/ui";
16
17
  //#region src/housingvista/pages/chat/ChatPage.tsx
17
- var E = [
18
+ var D = [
18
19
  "bg-status-info-bg-subtle text-status-info-text",
19
20
  "bg-status-success-bg-subtle text-status-success-text",
20
21
  "bg-status-warning-bg-subtle text-status-warning-text",
21
22
  "bg-action-primary-bg/15 text-primary"
22
23
  ];
23
- function D(e) {
24
+ function O(e) {
24
25
  let t = e.trim().split(/\s+/).filter(Boolean);
25
26
  return t.length === 0 ? "?" : t.length === 1 ? t[0].slice(0, 2).toUpperCase() : (t[0][0] + t[t.length - 1][0]).toUpperCase();
26
27
  }
27
- function O(e) {
28
+ function k(e) {
28
29
  let t = 0;
29
30
  for (let n = 0; n < e.length; n += 1) t += e.charCodeAt(n);
30
- return E[t % E.length];
31
+ return D[t % D.length];
31
32
  }
32
- function k(e) {
33
+ function A(e) {
33
34
  let t = new Date(e);
34
35
  return Number.isNaN(t.getTime()) ? "" : t.toLocaleTimeString("en-US", {
35
36
  hour: "numeric",
36
37
  minute: "2-digit"
37
38
  });
38
39
  }
39
- function A({ title: e, id: n, size: r = "md" }) {
40
- return /* @__PURE__ */ x("span", {
41
- className: t("inline-flex shrink-0 items-center justify-center rounded-full font-semibold", r === "md" ? "h-10 w-10 text-sm" : "h-9 w-9 text-xs", O(n)),
40
+ function j({ title: e, id: n, size: r = "md" }) {
41
+ return /* @__PURE__ */ S("span", {
42
+ className: t("inline-flex shrink-0 items-center justify-center rounded-full font-semibold", r === "md" ? "h-10 w-10 text-sm" : "h-9 w-9 text-xs", k(n)),
42
43
  "aria-hidden": !0,
43
- children: D(e)
44
+ children: O(e)
44
45
  });
45
46
  }
46
- function j() {
47
- let E = y(), { currentUser: D } = e(), O = D?.id ?? "", { data: j, loading: M, refetch: N } = l(), { sendMessage: P, loading: F } = s(), [I, L] = p(), [R, z] = p(""), [B, V] = p(""), H = f(null);
48
- u(() => {
49
- !I && j.length > 0 && typeof window < "u" && window.innerWidth >= 1024 && L(j[0].id);
50
- }, [j, I]);
51
- let U = d(() => j.find((e) => e.id === I), [j, I]), { data: W, loading: G } = o(I);
52
- u(() => {
53
- H.current?.scrollTo({ top: H.current.scrollHeight });
54
- }, [W, I]);
55
- let K = d(() => {
56
- let e = R.trim().toLowerCase();
57
- return e ? j.filter((t) => t.title.toLowerCase().includes(e) || t.lastMessageText.toLowerCase().includes(e)) : j;
58
- }, [j, R]), q = j.filter((e) => e.unreadCount > 0).length, J = async () => {
59
- let e = B.trim();
60
- if (!(!e || !I)) try {
61
- await P(I, e), V(""), N();
47
+ function M() {
48
+ c("housingvista.chat.viewed");
49
+ let D = b(), { currentUser: O } = e(), k = O?.id ?? "", { data: M, loading: N, refetch: P } = u(), { sendMessage: F, loading: I } = s(), [L, R] = m(), [z, B] = m(""), [V, H] = m(""), U = p(null);
50
+ d(() => {
51
+ !L && M.length > 0 && typeof window < "u" && window.innerWidth >= 1024 && R(M[0].id);
52
+ }, [M, L]);
53
+ let W = f(() => M.find((e) => e.id === L), [M, L]), { data: G, loading: K } = o(L);
54
+ d(() => {
55
+ U.current?.scrollTo({ top: U.current.scrollHeight });
56
+ }, [G, L]);
57
+ let q = f(() => {
58
+ let e = z.trim().toLowerCase();
59
+ return e ? M.filter((t) => t.title.toLowerCase().includes(e) || t.lastMessageText.toLowerCase().includes(e)) : M;
60
+ }, [M, z]), J = M.filter((e) => e.unreadCount > 0).length, Y = async () => {
61
+ let e = V.trim();
62
+ if (!(!e || !L)) try {
63
+ await F(L, e), H(""), P();
62
64
  } catch (e) {
63
- c.error("Message not sent", e.message);
65
+ l.error("Message not sent", e.message);
64
66
  }
65
- }, Y = !M && j.length === 0;
66
- return /* @__PURE__ */ S(r, {
67
+ }, X = !N && M.length === 0;
68
+ return /* @__PURE__ */ C(r, {
67
69
  title: "Messages",
68
70
  description: "Your conversations with property owners, agents, and service providers",
69
71
  purpose: "Every thread you start from a property, listing, or service lands here — read the full history, pick up where you left off, and reply. Messages are delivered to the other party's inbox in real time.",
70
- children: [/* @__PURE__ */ x(w, {
72
+ children: [/* @__PURE__ */ S(T, {
71
73
  className: "p-4",
72
- children: /* @__PURE__ */ S("div", {
74
+ children: /* @__PURE__ */ C("div", {
73
75
  className: "grid grid-cols-2 gap-4 sm:grid-cols-3",
74
76
  children: [
75
- /* @__PURE__ */ x(i, {
77
+ /* @__PURE__ */ S(i, {
76
78
  label: "Conversations",
77
- value: j.length,
78
- icon: /* @__PURE__ */ x(g, { className: "h-4 w-4" })
79
+ value: M.length,
80
+ icon: /* @__PURE__ */ S(_, { className: "h-4 w-4" })
79
81
  }),
80
- /* @__PURE__ */ x(i, {
82
+ /* @__PURE__ */ S(i, {
81
83
  label: "Unread threads",
82
- value: q,
83
- icon: /* @__PURE__ */ x(g, { className: "h-4 w-4" })
84
+ value: J,
85
+ icon: /* @__PURE__ */ S(_, { className: "h-4 w-4" })
84
86
  }),
85
- /* @__PURE__ */ x(i, {
87
+ /* @__PURE__ */ S(i, {
86
88
  label: "With updates today",
87
- value: j.filter((e) => e.lastMessageAt && n(e.lastMessageAt) === "Today").length,
88
- icon: /* @__PURE__ */ x(v, { className: "h-4 w-4" })
89
+ value: M.filter((e) => e.lastMessageAt && n(e.lastMessageAt) === "Today").length,
90
+ icon: /* @__PURE__ */ S(y, { className: "h-4 w-4" })
89
91
  })
90
92
  ]
91
93
  })
92
- }), Y ? /* @__PURE__ */ x(a, {
94
+ }), X ? /* @__PURE__ */ S(a, {
93
95
  illustration: "empty-generic",
94
- icon: /* @__PURE__ */ x(g, { className: "h-12 w-12" }),
96
+ icon: /* @__PURE__ */ S(_, { className: "h-12 w-12" }),
95
97
  title: "No messages yet",
96
98
  description: "Start a conversation from any property or service — reach out to an owner or provider and your thread will show up here.",
97
- action: /* @__PURE__ */ x(C, {
98
- onClick: () => E("../properties"),
99
+ action: /* @__PURE__ */ S(w, {
100
+ onClick: () => D("../properties"),
99
101
  children: "Browse properties"
100
102
  })
101
- }) : /* @__PURE__ */ x(T, {
103
+ }) : /* @__PURE__ */ S(E, {
102
104
  treatment: "plain",
103
105
  className: "overflow-hidden p-0",
104
- children: /* @__PURE__ */ S("div", {
106
+ children: /* @__PURE__ */ C("div", {
105
107
  className: "grid h-[68vh] min-h-[26rem] grid-cols-1 lg:grid-cols-[20rem_1fr]",
106
- children: [/* @__PURE__ */ S("aside", {
107
- className: t("flex flex-col border-border-subtle lg:border-r", I ? "hidden lg:flex" : "flex"),
108
- children: [/* @__PURE__ */ x("div", {
108
+ children: [/* @__PURE__ */ C("aside", {
109
+ className: t("flex flex-col border-border-subtle lg:border-r", L ? "hidden lg:flex" : "flex"),
110
+ children: [/* @__PURE__ */ S("div", {
109
111
  className: "border-b border-border-subtle p-3",
110
- children: /* @__PURE__ */ S("div", {
112
+ children: /* @__PURE__ */ C("div", {
111
113
  className: "relative",
112
- children: [/* @__PURE__ */ x(_, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ x("input", {
114
+ children: [/* @__PURE__ */ S(v, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ S("input", {
113
115
  type: "search",
114
- value: R,
115
- onChange: (e) => z(e.target.value),
116
+ value: z,
117
+ onChange: (e) => B(e.target.value),
116
118
  placeholder: "Search conversations",
117
119
  "aria-label": "Search conversations",
118
120
  className: "h-10 w-full rounded-md border border-border-subtle bg-bg-surface pl-9 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
119
121
  })]
120
122
  })
121
- }), /* @__PURE__ */ x("div", {
123
+ }), /* @__PURE__ */ S("div", {
122
124
  className: "min-h-0 flex-1 overflow-y-auto",
123
- children: M && j.length === 0 ? /* @__PURE__ */ x("p", {
125
+ children: N && M.length === 0 ? /* @__PURE__ */ S("p", {
124
126
  className: "p-6 text-center text-sm text-text-secondary",
125
127
  children: "Loading conversations…"
126
- }) : K.length === 0 ? /* @__PURE__ */ x("p", {
128
+ }) : q.length === 0 ? /* @__PURE__ */ S("p", {
127
129
  className: "p-6 text-center text-sm text-text-secondary",
128
130
  children: "No conversations match your search."
129
- }) : /* @__PURE__ */ x("ul", { children: K.map((e) => /* @__PURE__ */ x("li", { children: /* @__PURE__ */ S("button", {
131
+ }) : /* @__PURE__ */ S("ul", { children: q.map((e) => /* @__PURE__ */ S("li", { children: /* @__PURE__ */ C("button", {
130
132
  type: "button",
131
133
  onClick: () => {
132
- L(e.id), V("");
134
+ R(e.id), H("");
133
135
  },
134
- className: t("flex w-full items-start gap-3 border-b border-border-subtle px-3 py-3 text-left transition-colors", e.id === I ? "bg-action-primary-bg/10" : "hover:bg-bg-sunken"),
135
- children: [/* @__PURE__ */ x(A, {
136
+ className: t("flex w-full items-start gap-3 border-b border-border-subtle px-3 py-3 text-left transition-colors", e.id === L ? "bg-action-primary-bg/10" : "hover:bg-bg-sunken"),
137
+ children: [/* @__PURE__ */ S(j, {
136
138
  title: e.title,
137
139
  id: e.id
138
- }), /* @__PURE__ */ S("div", {
140
+ }), /* @__PURE__ */ C("div", {
139
141
  className: "min-w-0 flex-1",
140
- children: [/* @__PURE__ */ S("div", {
142
+ children: [/* @__PURE__ */ C("div", {
141
143
  className: "flex items-center justify-between gap-2",
142
- children: [/* @__PURE__ */ x("span", {
144
+ children: [/* @__PURE__ */ S("span", {
143
145
  className: "truncate text-sm font-semibold text-text-primary",
144
146
  children: e.title
145
- }), e.lastMessageAt && /* @__PURE__ */ x("span", {
147
+ }), e.lastMessageAt && /* @__PURE__ */ S("span", {
146
148
  className: "shrink-0 text-xs text-text-secondary",
147
149
  children: n(e.lastMessageAt)
148
150
  })]
149
- }), /* @__PURE__ */ S("div", {
151
+ }), /* @__PURE__ */ C("div", {
150
152
  className: "mt-0.5 flex items-center gap-2",
151
- children: [/* @__PURE__ */ x("span", {
153
+ children: [/* @__PURE__ */ S("span", {
152
154
  className: "min-w-0 flex-1 truncate text-xs text-text-secondary",
153
155
  children: e.lastMessageText || "No messages yet"
154
- }), e.unreadCount > 0 && /* @__PURE__ */ x("span", {
156
+ }), e.unreadCount > 0 && /* @__PURE__ */ S("span", {
155
157
  className: "inline-flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full bg-action-primary-bg px-1.5 text-[0.65rem] font-semibold text-action-primary-text",
156
158
  children: e.unreadCount
157
159
  })]
@@ -159,91 +161,91 @@ function j() {
159
161
  })]
160
162
  }) }, e.id)) })
161
163
  })]
162
- }), /* @__PURE__ */ x("section", {
163
- className: t("flex min-h-0 flex-col", I ? "flex" : "hidden lg:flex"),
164
- children: U ? /* @__PURE__ */ S(b, { children: [
165
- /* @__PURE__ */ S("header", {
164
+ }), /* @__PURE__ */ S("section", {
165
+ className: t("flex min-h-0 flex-col", L ? "flex" : "hidden lg:flex"),
166
+ children: W ? /* @__PURE__ */ C(x, { children: [
167
+ /* @__PURE__ */ C("header", {
166
168
  className: "flex items-center gap-3 border-b border-border-subtle p-3",
167
169
  children: [
168
- /* @__PURE__ */ x("button", {
170
+ /* @__PURE__ */ S("button", {
169
171
  type: "button",
170
- onClick: () => L(void 0),
172
+ onClick: () => R(void 0),
171
173
  className: "rounded-md p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken lg:hidden",
172
174
  "aria-label": "Back to conversations",
173
- children: /* @__PURE__ */ x(m, { className: "h-5 w-5" })
175
+ children: /* @__PURE__ */ S(h, { className: "h-5 w-5" })
174
176
  }),
175
- /* @__PURE__ */ x(A, {
176
- title: U.title,
177
- id: U.id,
177
+ /* @__PURE__ */ S(j, {
178
+ title: W.title,
179
+ id: W.id,
178
180
  size: "sm"
179
181
  }),
180
- /* @__PURE__ */ S("div", {
182
+ /* @__PURE__ */ C("div", {
181
183
  className: "min-w-0 flex-1",
182
- children: [/* @__PURE__ */ x("h2", {
184
+ children: [/* @__PURE__ */ S("h2", {
183
185
  className: "truncate text-sm font-semibold text-text-primary",
184
- children: U.title
185
- }), U.contextHref && /* @__PURE__ */ S("button", {
186
+ children: W.title
187
+ }), W.contextHref && /* @__PURE__ */ C("button", {
186
188
  type: "button",
187
- onClick: () => E(`..${U.contextHref}`),
189
+ onClick: () => D(`..${W.contextHref}`),
188
190
  className: "flex items-center gap-1 text-xs text-text-secondary transition-colors hover:text-primary",
189
- children: [/* @__PURE__ */ x(h, { className: "h-3 w-3" }), "View listing"]
191
+ children: [/* @__PURE__ */ S(g, { className: "h-3 w-3" }), "View listing"]
190
192
  })]
191
193
  })
192
194
  ]
193
195
  }),
194
- /* @__PURE__ */ x("div", {
195
- ref: H,
196
+ /* @__PURE__ */ S("div", {
197
+ ref: U,
196
198
  className: "min-h-0 flex-1 space-y-2 overflow-y-auto bg-bg-sunken/40 p-4",
197
- children: G && W.length === 0 ? /* @__PURE__ */ x("p", {
199
+ children: K && G.length === 0 ? /* @__PURE__ */ S("p", {
198
200
  className: "py-10 text-center text-sm text-text-secondary",
199
201
  children: "Loading messages…"
200
- }) : W.length === 0 ? /* @__PURE__ */ x("p", {
202
+ }) : G.length === 0 ? /* @__PURE__ */ S("p", {
201
203
  className: "py-10 text-center text-sm text-text-secondary",
202
204
  children: "No messages yet. Say hello to get the conversation started."
203
- }) : W.map((e) => {
204
- let n = e.authorId === O;
205
- return /* @__PURE__ */ x("div", {
205
+ }) : G.map((e) => {
206
+ let n = e.authorId === k;
207
+ return /* @__PURE__ */ S("div", {
206
208
  className: t("flex", n ? "justify-end" : "justify-start"),
207
- children: /* @__PURE__ */ S("div", {
209
+ children: /* @__PURE__ */ C("div", {
208
210
  className: t("max-w-[78%]", n ? "items-end" : "items-start"),
209
- children: [/* @__PURE__ */ x("div", {
211
+ children: [/* @__PURE__ */ S("div", {
210
212
  className: t("rounded-2xl px-3.5 py-2 text-sm", n ? "rounded-br-sm bg-action-primary-bg text-action-primary-text" : "rounded-bl-sm border border-border-subtle bg-bg-surface text-text-primary"),
211
213
  children: e.text
212
- }), /* @__PURE__ */ x("div", {
214
+ }), /* @__PURE__ */ S("div", {
213
215
  className: t("mt-1 px-1 text-[0.65rem] text-text-secondary", n && "text-right"),
214
- children: k(e.createdAt)
216
+ children: A(e.createdAt)
215
217
  })]
216
218
  })
217
219
  }, e.id);
218
220
  })
219
221
  }),
220
- /* @__PURE__ */ S("div", {
222
+ /* @__PURE__ */ C("div", {
221
223
  className: "flex items-end gap-2 border-t border-border-subtle p-3",
222
- children: [/* @__PURE__ */ x("textarea", {
223
- value: B,
224
- onChange: (e) => V(e.target.value),
224
+ children: [/* @__PURE__ */ S("textarea", {
225
+ value: V,
226
+ onChange: (e) => H(e.target.value),
225
227
  onKeyDown: (e) => {
226
- e.key === "Enter" && !e.shiftKey && (e.preventDefault(), J());
228
+ e.key === "Enter" && !e.shiftKey && (e.preventDefault(), Y());
227
229
  },
228
230
  rows: 1,
229
231
  placeholder: "Type a message…",
230
232
  "aria-label": "Message",
231
233
  className: "max-h-32 min-h-10 flex-1 resize-none rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
232
- }), /* @__PURE__ */ S(C, {
234
+ }), /* @__PURE__ */ C(w, {
233
235
  type: "button",
234
- onClick: J,
235
- disabled: !B.trim() || F,
236
- children: [/* @__PURE__ */ x(v, { className: "h-4 w-4" }), /* @__PURE__ */ x("span", {
236
+ onClick: Y,
237
+ disabled: !V.trim() || I,
238
+ children: [/* @__PURE__ */ S(y, { className: "h-4 w-4" }), /* @__PURE__ */ S("span", {
237
239
  className: "ml-1 hidden sm:inline",
238
240
  children: "Send"
239
241
  })]
240
242
  })]
241
243
  })
242
- ] }) : /* @__PURE__ */ x("div", {
244
+ ] }) : /* @__PURE__ */ S("div", {
243
245
  className: "hidden flex-1 items-center justify-center p-8 lg:flex",
244
- children: /* @__PURE__ */ S("div", {
246
+ children: /* @__PURE__ */ C("div", {
245
247
  className: "text-center",
246
- children: [/* @__PURE__ */ x(g, { className: "mx-auto h-10 w-10 text-text-secondary" }), /* @__PURE__ */ x("p", {
248
+ children: [/* @__PURE__ */ S(_, { className: "mx-auto h-10 w-10 text-text-secondary" }), /* @__PURE__ */ S("p", {
247
249
  className: "mt-3 text-sm text-text-secondary",
248
250
  children: "Select a conversation to read and reply."
249
251
  })]
@@ -255,6 +257,6 @@ function j() {
255
257
  });
256
258
  }
257
259
  //#endregion
258
- export { j as ChatPage };
260
+ export { M as ChatPage };
259
261
 
260
262
  //# sourceMappingURL=ChatPage.js.map