@burdenoff/microfe-housingvista 2026.703.1 → 2026.703.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/housingvista/components/DataTable.js.map +1 -1
- package/dist/housingvista/components/EmptyState.js.map +1 -1
- package/dist/housingvista/components/PageLayout.js.map +1 -1
- package/dist/housingvista/components/PropertyCard.js.map +1 -1
- package/dist/housingvista/hooks/index.js.map +1 -1
- package/dist/housingvista/pages/HomePage.js.map +1 -1
- package/dist/housingvista/pages/amc/AMCManagementPage.js.map +1 -1
- package/dist/housingvista/pages/finance/HomeLoansPage.js.map +1 -1
- package/dist/housingvista/pages/investments/InvestmentDetailPage.js.map +1 -1
- package/dist/housingvista/pages/market/AreaInsightsPage.js.map +1 -1
- package/dist/housingvista/pages/market/MarketAnalyticsPage.js.map +1 -1
- package/dist/housingvista/pages/properties/AddPropertyPage.js.map +1 -1
- package/dist/housingvista/pages/properties/PropertiesPage.js.map +1 -1
- package/dist/housingvista/pages/properties/PropertyDetailPage.js.map +1 -1
- package/dist/housingvista/pages/rental/RentalManagementPage.js.map +1 -1
- package/dist/housingvista/pages/settings/ProfilePage.js.map +1 -1
- package/dist/housingvista/pages/settings/SettingsPage.js.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionDetailPage.js.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionsPage.js.map +1 -1
- package/dist/housingvista/pages/verification/VerificationPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.js","names":[],"sources":["../../../src/housingvista/components/DataTable.tsx"],"sourcesContent":["import { useState, useMemo, useCallback, type ReactNode } from 'react';\nimport { ChevronUp, ChevronDown, ChevronsUpDown, ChevronLeft, ChevronRight } from 'lucide-react';\nimport { IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/helpers';\n\nexport interface Column<T> {\n key: string;\n header: string;\n render?: (item: T) => ReactNode;\n className?: string;\n sortable?: boolean;\n}\n\ntype SortDirection = 'asc' | 'desc' | null;\n\ninterface DataTableProps<T> {\n columns: Column<T>[];\n data: T[];\n keyField: string;\n onRowClick?: (item: T) => void;\n emptyMessage?: string;\n /** Optional secondary line under the empty title. */\n emptyDescription?: string;\n /**\n * Boff UI 2.0 — on-brand illustration name (served from `/illustrations/<name>.webp`)\n * shown in the zero state. Standard names: empty-data, empty-search, empty-files,\n * empty-people, empty-notifications, empty-generic. Defaults to `empty-data`.\n */\n emptyIllustration?: string;\n /** Optional action rendered in the empty state (e.g. a create button). */\n emptyAction?: ReactNode;\n className?: string;\n pageSize?: number;\n selectable?: boolean;\n selectedKeys?: Set<string>;\n onSelectionChange?: (keys: Set<string>) => void;\n serverPagination?: {\n page: number;\n pageSize: number;\n hasNextPage?: boolean;\n loading?: boolean;\n onPageChange: (page: number) => void;\n onPageSizeChange?: (pageSize: number) => void;\n };\n}\n\nexport function DataTable<T extends object>({\n columns,\n data,\n keyField,\n onRowClick,\n emptyMessage = 'No data available',\n emptyDescription,\n emptyIllustration = 'empty-data',\n emptyAction,\n className,\n pageSize = 10,\n selectable = false,\n selectedKeys,\n onSelectionChange,\n serverPagination,\n}: DataTableProps<T>) {\n const [sortKey, setSortKey] = useState<string | null>(null);\n const [sortDir, setSortDir] = useState<SortDirection>(null);\n const [page, setPage] = useState(0);\n\n const handleSort = useCallback(\n (key: string) => {\n if (sortKey === key) {\n if (sortDir === 'asc') setSortDir('desc');\n else if (sortDir === 'desc') {\n setSortKey(null);\n setSortDir(null);\n } else setSortDir('asc');\n } else {\n setSortKey(key);\n setSortDir('asc');\n }\n setPage(0);\n },\n [sortKey, sortDir]\n );\n\n const sorted = useMemo(() => {\n if (!sortKey || !sortDir) return data;\n return [...data].sort((a, b) => {\n const av = (a as Record<string, unknown>)[sortKey];\n const bv = (b as Record<string, unknown>)[sortKey];\n if (av == null && bv == null) return 0;\n if (av == null) return 1;\n if (bv == null) return -1;\n const cmp = typeof av === 'number' && typeof bv === 'number' ? av - bv : String(av).localeCompare(String(bv));\n return sortDir === 'asc' ? cmp : -cmp;\n });\n }, [data, sortKey, sortDir]);\n\n const effectivePageSize = serverPagination?.pageSize ?? pageSize;\n const totalPages = Math.max(1, Math.ceil(sorted.length / effectivePageSize));\n const paged = serverPagination ? sorted : sorted.slice(page * effectivePageSize, (page + 1) * effectivePageSize);\n\n const allPageKeysSelected =\n selectable && selectedKeys\n ? paged.every((item) => selectedKeys.has(String((item as Record<string, unknown>)[keyField])))\n : false;\n\n const handleSelectAll = () => {\n if (!onSelectionChange || !selectedKeys) return;\n const keys = new Set(selectedKeys);\n if (allPageKeysSelected) {\n paged.forEach((item) => keys.delete(String((item as Record<string, unknown>)[keyField])));\n } else {\n paged.forEach((item) => keys.add(String((item as Record<string, unknown>)[keyField])));\n }\n onSelectionChange(keys);\n };\n\n const handleSelectRow = (item: T) => {\n if (!onSelectionChange || !selectedKeys) return;\n const key = String((item as Record<string, unknown>)[keyField]);\n const keys = new Set(selectedKeys);\n if (keys.has(key)) keys.delete(key);\n else keys.add(key);\n onSelectionChange(keys);\n };\n\n if (data.length === 0) {\n return (\n <div className=\"rounded-card border border-dashed border-border-seam bg-bg-surface/50\">\n <IllustratedEmptyState\n illustration={emptyIllustration}\n title={emptyMessage}\n description={emptyDescription}\n action={emptyAction}\n />\n </div>\n );\n }\n\n return (\n <div className={cn('space-y-3', className)}>\n <div className=\"overflow-x-auto rounded-card border border-border-seam bg-bg-surface shadow-elevation-1\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border-subtle bg-bg-sunken/50\">\n {selectable && (\n <th className=\"w-10 px-3 py-3\">\n <input\n type=\"checkbox\"\n checked={allPageKeysSelected && paged.length > 0}\n onChange={handleSelectAll}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </th>\n )}\n {columns.map((col) => {\n const isSortable = col.sortable !== false;\n const isActive = sortKey === col.key;\n return (\n <th\n key={col.key}\n className={cn(\n 'px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-text-secondary',\n isSortable && 'cursor-pointer select-none hover:text-text-primary',\n col.className\n )}\n onClick={isSortable ? () => handleSort(col.key) : undefined}\n >\n <span className=\"inline-flex items-center gap-1\">\n {col.header}\n {isSortable &&\n (isActive ? (\n sortDir === 'asc' ? (\n <ChevronUp className=\"h-3 w-3\" />\n ) : (\n <ChevronDown className=\"h-3 w-3\" />\n )\n ) : (\n <ChevronsUpDown className=\"h-3 w-3 opacity-30\" />\n ))}\n </span>\n </th>\n );\n })}\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border bg-bg-surface\">\n {paged.map((item) => {\n const rowKey = String((item as Record<string, unknown>)[keyField]);\n const isSelected = selectable && selectedKeys?.has(rowKey);\n return (\n <tr\n key={rowKey}\n className={cn(\n 'transition-colors',\n onRowClick && 'cursor-pointer hover:bg-bg-sunken/50',\n isSelected && 'bg-action-primary-bg/5'\n )}\n onClick={() => onRowClick?.(item)}\n >\n {selectable && (\n <td className=\"w-10 px-3 py-3\" onClick={(e) => e.stopPropagation()}>\n <input\n type=\"checkbox\"\n checked={isSelected ?? false}\n onChange={() => handleSelectRow(item)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </td>\n )}\n {columns.map((col) => (\n <td key={col.key} className={cn('px-4 py-3 text-sm text-text-primary', col.className)}>\n {col.render ? col.render(item) : String((item as Record<string, unknown>)[col.key] ?? '')}\n </td>\n ))}\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n\n {serverPagination ? (\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-subtle bg-bg-surface p-3 text-sm sm:flex-row sm:items-center sm:justify-between\">\n <span className=\"text-text-secondary\">Page {serverPagination.page}</span>\n <div className=\"flex flex-wrap items-center gap-2\">\n {serverPagination.onPageSizeChange ? (\n <select\n value={serverPagination.pageSize}\n onChange={(event) => serverPagination.onPageSizeChange?.(Number(event.target.value))}\n disabled={serverPagination.loading}\n className=\"h-8 rounded-md border border-border-subtle bg-bg-surface px-2 text-sm text-text-primary disabled:opacity-50\"\n aria-label=\"Rows per page\"\n >\n {[10, 25, 50, 100].map((option) => (\n <option key={option} value={option}>\n {option} / page\n </option>\n ))}\n </select>\n ) : null}\n <button\n type=\"button\"\n onClick={() => serverPagination.onPageChange(serverPagination.page - 1)}\n disabled={serverPagination.loading || serverPagination.page <= 1}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n Previous\n </button>\n <button\n type=\"button\"\n onClick={() => serverPagination.onPageChange(serverPagination.page + 1)}\n disabled={\n serverPagination.loading ||\n !(serverPagination.hasNextPage ?? sorted.length >= serverPagination.pageSize)\n }\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n Next\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ) : sorted.length > pageSize ? (\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">\n Showing {page * pageSize + 1}-{Math.min((page + 1) * pageSize, sorted.length)} of {sorted.length}\n </span>\n <div className=\"flex items-center gap-1\">\n <button\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n disabled={page === 0}\n className=\"rounded-md border border-border-subtle p-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n </button>\n {Array.from({ length: totalPages }).map((_, i) => (\n <button\n key={i}\n onClick={() => setPage(i)}\n className={cn(\n 'h-8 w-8 rounded-md text-xs font-medium transition-colors',\n i === page ? 'bg-action-primary-bg text-action-primary-text' : 'text-text-secondary hover:bg-bg-sunken'\n )}\n >\n {i + 1}\n </button>\n ))}\n <button\n onClick={() => setPage((p) => Math.min(totalPages - 1, p + 1))}\n disabled={page >= totalPages - 1}\n className=\"rounded-md border border-border-subtle p-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DataTable.js","names":[],"sources":["../../../src/housingvista/components/DataTable.tsx"],"sourcesContent":["import { useState, useMemo, useCallback, type ReactNode } from 'react';\nimport { ChevronUp, ChevronDown, ChevronsUpDown, ChevronLeft, ChevronRight } from 'lucide-react';\nimport { IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/helpers';\n\nexport interface Column<T> {\n key: string;\n header: string;\n render?: (item: T) => ReactNode;\n className?: string;\n sortable?: boolean;\n}\n\ntype SortDirection = 'asc' | 'desc' | null;\n\ninterface DataTableProps<T> {\n columns: Column<T>[];\n data: T[];\n keyField: string;\n onRowClick?: (item: T) => void;\n emptyMessage?: string;\n /** Optional secondary line under the empty title. */\n emptyDescription?: string;\n /**\n * Boff UI 2.0 — on-brand illustration name (served from `/illustrations/<name>.webp`)\n * shown in the zero state. Standard names: empty-data, empty-search, empty-files,\n * empty-people, empty-notifications, empty-generic. Defaults to `empty-data`.\n */\n emptyIllustration?: string;\n /** Optional action rendered in the empty state (e.g. a create button). */\n emptyAction?: ReactNode;\n className?: string;\n pageSize?: number;\n selectable?: boolean;\n selectedKeys?: Set<string>;\n onSelectionChange?: (keys: Set<string>) => void;\n serverPagination?: {\n page: number;\n pageSize: number;\n hasNextPage?: boolean;\n loading?: boolean;\n onPageChange: (page: number) => void;\n onPageSizeChange?: (pageSize: number) => void;\n };\n}\n\nexport function DataTable<T extends object>({\n columns,\n data,\n keyField,\n onRowClick,\n emptyMessage = 'No data available',\n emptyDescription,\n emptyIllustration = 'empty-data',\n emptyAction,\n className,\n pageSize = 10,\n selectable = false,\n selectedKeys,\n onSelectionChange,\n serverPagination,\n}: DataTableProps<T>) {\n const [sortKey, setSortKey] = useState<string | null>(null);\n const [sortDir, setSortDir] = useState<SortDirection>(null);\n const [page, setPage] = useState(0);\n\n const handleSort = useCallback(\n (key: string) => {\n if (sortKey === key) {\n if (sortDir === 'asc') setSortDir('desc');\n else if (sortDir === 'desc') {\n setSortKey(null);\n setSortDir(null);\n } else setSortDir('asc');\n } else {\n setSortKey(key);\n setSortDir('asc');\n }\n setPage(0);\n },\n [sortKey, sortDir]\n );\n\n const sorted = useMemo(() => {\n if (!sortKey || !sortDir) return data;\n return [...data].sort((a, b) => {\n const av = (a as Record<string, unknown>)[sortKey];\n const bv = (b as Record<string, unknown>)[sortKey];\n if (av == null && bv == null) return 0;\n if (av == null) return 1;\n if (bv == null) return -1;\n const cmp = typeof av === 'number' && typeof bv === 'number' ? av - bv : String(av).localeCompare(String(bv));\n return sortDir === 'asc' ? cmp : -cmp;\n });\n }, [data, sortKey, sortDir]);\n\n const effectivePageSize = serverPagination?.pageSize ?? pageSize;\n const totalPages = Math.max(1, Math.ceil(sorted.length / effectivePageSize));\n const paged = serverPagination ? sorted : sorted.slice(page * effectivePageSize, (page + 1) * effectivePageSize);\n\n const allPageKeysSelected =\n selectable && selectedKeys\n ? paged.every((item) => selectedKeys.has(String((item as Record<string, unknown>)[keyField])))\n : false;\n\n const handleSelectAll = () => {\n if (!onSelectionChange || !selectedKeys) return;\n const keys = new Set(selectedKeys);\n if (allPageKeysSelected) {\n paged.forEach((item) => keys.delete(String((item as Record<string, unknown>)[keyField])));\n } else {\n paged.forEach((item) => keys.add(String((item as Record<string, unknown>)[keyField])));\n }\n onSelectionChange(keys);\n };\n\n const handleSelectRow = (item: T) => {\n if (!onSelectionChange || !selectedKeys) return;\n const key = String((item as Record<string, unknown>)[keyField]);\n const keys = new Set(selectedKeys);\n if (keys.has(key)) keys.delete(key);\n else keys.add(key);\n onSelectionChange(keys);\n };\n\n if (data.length === 0) {\n return (\n <div className=\"rounded-card border border-dashed border-border-seam bg-bg-surface/50\">\n <IllustratedEmptyState\n illustration={emptyIllustration}\n title={emptyMessage}\n description={emptyDescription}\n action={emptyAction}\n />\n </div>\n );\n }\n\n return (\n <div className={cn('space-y-3', className)}>\n <div className=\"overflow-x-auto rounded-card border border-border-seam bg-bg-surface shadow-elevation-1\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border-subtle bg-bg-sunken/50\">\n {selectable && (\n <th className=\"w-10 px-3 py-3\">\n <input\n type=\"checkbox\"\n checked={allPageKeysSelected && paged.length > 0}\n onChange={handleSelectAll}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </th>\n )}\n {columns.map((col) => {\n const isSortable = col.sortable !== false;\n const isActive = sortKey === col.key;\n return (\n <th\n key={col.key}\n className={cn(\n 'px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-text-secondary',\n isSortable && 'cursor-pointer select-none hover:text-text-primary',\n col.className\n )}\n onClick={isSortable ? () => handleSort(col.key) : undefined}\n >\n <span className=\"inline-flex items-center gap-1\">\n {col.header}\n {isSortable &&\n (isActive ? (\n sortDir === 'asc' ? (\n <ChevronUp className=\"h-3 w-3\" />\n ) : (\n <ChevronDown className=\"h-3 w-3\" />\n )\n ) : (\n <ChevronsUpDown className=\"h-3 w-3 opacity-30\" />\n ))}\n </span>\n </th>\n );\n })}\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border bg-bg-surface\">\n {paged.map((item) => {\n const rowKey = String((item as Record<string, unknown>)[keyField]);\n const isSelected = selectable && selectedKeys?.has(rowKey);\n return (\n <tr\n key={rowKey}\n className={cn(\n 'transition-colors',\n onRowClick && 'cursor-pointer hover:bg-bg-sunken/50',\n isSelected && 'bg-action-primary-bg/5'\n )}\n onClick={() => onRowClick?.(item)}\n >\n {selectable && (\n <td className=\"w-10 px-3 py-3\" onClick={(e) => e.stopPropagation()}>\n <input\n type=\"checkbox\"\n checked={isSelected ?? false}\n onChange={() => handleSelectRow(item)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </td>\n )}\n {columns.map((col) => (\n <td key={col.key} className={cn('px-4 py-3 text-sm text-text-primary', col.className)}>\n {col.render ? col.render(item) : String((item as Record<string, unknown>)[col.key] ?? '')}\n </td>\n ))}\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n\n {serverPagination ? (\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-subtle bg-bg-surface p-3 text-sm sm:flex-row sm:items-center sm:justify-between\">\n <span className=\"text-text-secondary\">Page {serverPagination.page}</span>\n <div className=\"flex flex-wrap items-center gap-2\">\n {serverPagination.onPageSizeChange ? (\n <select\n value={serverPagination.pageSize}\n onChange={(event) => serverPagination.onPageSizeChange?.(Number(event.target.value))}\n disabled={serverPagination.loading}\n className=\"h-8 rounded-md border border-border-subtle bg-bg-surface px-2 text-sm text-text-primary disabled:opacity-50\"\n aria-label=\"Rows per page\"\n >\n {[10, 25, 50, 100].map((option) => (\n <option key={option} value={option}>\n {option} / page\n </option>\n ))}\n </select>\n ) : null}\n <button\n type=\"button\"\n onClick={() => serverPagination.onPageChange(serverPagination.page - 1)}\n disabled={serverPagination.loading || serverPagination.page <= 1}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n Previous\n </button>\n <button\n type=\"button\"\n onClick={() => serverPagination.onPageChange(serverPagination.page + 1)}\n disabled={\n serverPagination.loading ||\n !(serverPagination.hasNextPage ?? sorted.length >= serverPagination.pageSize)\n }\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n Next\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ) : sorted.length > pageSize ? (\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">\n Showing {page * pageSize + 1}-{Math.min((page + 1) * pageSize, sorted.length)} of {sorted.length}\n </span>\n <div className=\"flex items-center gap-1\">\n <button\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n disabled={page === 0}\n className=\"rounded-md border border-border-subtle p-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n </button>\n {Array.from({ length: totalPages }).map((_, i) => (\n <button\n key={i}\n onClick={() => setPage(i)}\n className={cn(\n 'h-8 w-8 rounded-md text-xs font-medium transition-colors',\n i === page\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:bg-bg-sunken'\n )}\n >\n {i + 1}\n </button>\n ))}\n <button\n onClick={() => setPage((p) => Math.min(totalPages - 1, p + 1))}\n disabled={page >= totalPages - 1}\n className=\"rounded-md border border-border-subtle p-1.5 text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;AA8CA,SAAgB,EAA4B,EAC1C,YACA,SACA,aACA,eACA,kBAAe,qBACf,qBACA,uBAAoB,cACpB,gBACA,cACA,cAAW,IACX,gBAAa,IACb,iBACA,sBACA,uBACoB;CACpB,IAAM,CAAC,GAAS,KAAc,EAAwB,KAAK,EACrD,CAAC,GAAS,KAAc,EAAwB,KAAK,EACrD,CAAC,GAAM,KAAW,EAAS,EAAE,EAE7B,IAAa,GAChB,MAAgB;AAWf,EAVI,MAAY,IACV,MAAY,QAAO,EAAW,OAAO,GAChC,MAAY,UACnB,EAAW,KAAK,EAChB,EAAW,KAAK,IACX,EAAW,MAAM,IAExB,EAAW,EAAI,EACf,EAAW,MAAM,GAEnB,EAAQ,EAAE;IAEZ,CAAC,GAAS,EAAQ,CACnB,EAEK,IAAS,QACT,CAAC,KAAW,CAAC,IAAgB,IAC1B,CAAC,GAAG,EAAK,CAAC,MAAM,GAAG,MAAM;EAC9B,IAAM,IAAM,EAA8B,IACpC,IAAM,EAA8B;AAC1C,MAAI,KAAM,QAAQ,KAAM,KAAM,QAAO;AACrC,MAAI,KAAM,KAAM,QAAO;AACvB,MAAI,KAAM,KAAM,QAAO;EACvB,IAAM,IAAM,OAAO,KAAO,YAAY,OAAO,KAAO,WAAW,IAAK,IAAK,OAAO,EAAG,CAAC,cAAc,OAAO,EAAG,CAAC;AAC7G,SAAO,MAAY,QAAQ,IAAM,CAAC;GAClC,EACD;EAAC;EAAM;EAAS;EAAQ,CAAC,EAEtB,IAAoB,GAAkB,YAAY,GAClD,IAAa,KAAK,IAAI,GAAG,KAAK,KAAK,EAAO,SAAS,EAAkB,CAAC,EACtE,IAAQ,IAAmB,IAAS,EAAO,MAAM,IAAO,IAAoB,IAAO,KAAK,EAAkB,EAE1G,IACJ,KAAc,IACV,EAAM,OAAO,MAAS,EAAa,IAAI,OAAQ,EAAiC,GAAU,CAAC,CAAC,GAC5F,IAEA,UAAwB;AAC5B,MAAI,CAAC,KAAqB,CAAC,EAAc;EACzC,IAAM,IAAO,IAAI,IAAI,EAAa;AAMlC,EALI,IACF,EAAM,SAAS,MAAS,EAAK,OAAO,OAAQ,EAAiC,GAAU,CAAC,CAAC,GAEzF,EAAM,SAAS,MAAS,EAAK,IAAI,OAAQ,EAAiC,GAAU,CAAC,CAAC,EAExF,EAAkB,EAAK;IAGnB,KAAmB,MAAY;AACnC,MAAI,CAAC,KAAqB,CAAC,EAAc;EACzC,IAAM,IAAM,OAAQ,EAAiC,GAAU,EACzD,IAAO,IAAI,IAAI,EAAa;AAGlC,EAFI,EAAK,IAAI,EAAI,GAAE,EAAK,OAAO,EAAI,GAC9B,EAAK,IAAI,EAAI,EAClB,EAAkB,EAAK;;AAgBzB,QAbI,EAAK,WAAW,IAEhB,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,cAAc;GACd,OAAO;GACP,aAAa;GACb,QAAQ;GACR,CAAA;EACE,CAAA,GAKR,kBAAC,OAAD;EAAK,WAAW,EAAG,aAAa,EAAU;YAA1C,CACE,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,SAAD;IAAO,WAAU;cAAjB,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD;KAAI,WAAU;eAAd,CACG,KACC,kBAAC,MAAD;MAAI,WAAU;gBACZ,kBAAC,SAAD;OACE,MAAK;OACL,SAAS,KAAuB,EAAM,SAAS;OAC/C,UAAU;OACV,WAAU;OACV,CAAA;MACC,CAAA,EAEN,EAAQ,KAAK,MAAQ;MACpB,IAAM,IAAa,EAAI,aAAa,IAC9B,IAAW,MAAY,EAAI;AACjC,aACE,kBAAC,MAAD;OAEE,WAAW,EACT,wFACA,KAAc,sDACd,EAAI,UACL;OACD,SAAS,UAAmB,EAAW,EAAI,IAAI,GAAG,KAAA;iBAElD,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CACG,EAAI,QACJ,MACE,IAEG,EADF,MAAY,QACT,IAEA,GAFD,EAAW,WAAU,WAAY,CAEE,GAGrC,kBAAC,GAAD,EAAgB,WAAU,sBAAuB,CAAA,EAEhD;;OACJ,EArBE,EAAI,IAqBN;OAEP,CACC;QACC,CAAA,EACR,kBAAC,SAAD;KAAO,WAAU;eACd,EAAM,KAAK,MAAS;MACnB,IAAM,IAAS,OAAQ,EAAiC,GAAU,EAC5D,IAAa,KAAc,GAAc,IAAI,EAAO;AAC1D,aACE,kBAAC,MAAD;OAEE,WAAW,EACT,qBACA,KAAc,wCACd,KAAc,yBACf;OACD,eAAe,IAAa,EAAK;iBAPnC,CASG,KACC,kBAAC,MAAD;QAAI,WAAU;QAAiB,UAAU,MAAM,EAAE,iBAAiB;kBAChE,kBAAC,SAAD;SACE,MAAK;SACL,SAAS,KAAc;SACvB,gBAAgB,EAAgB,EAAK;SACrC,WAAU;SACV,CAAA;QACC,CAAA,EAEN,EAAQ,KAAK,MACZ,kBAAC,MAAD;QAAkB,WAAW,EAAG,uCAAuC,EAAI,UAAU;kBAClF,EAAI,SAAS,EAAI,OAAO,EAAK,GAAG,OAAQ,EAAiC,EAAI,QAAQ,GAAG;QACtF,EAFI,EAAI,IAER,CACL,CACC;SAvBE,EAuBF;OAEP;KACI,CAAA,CACF;;GACJ,CAAA,EAEL,IACC,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,QAAD;IAAM,WAAU;cAAhB,CAAsC,SAAM,EAAiB,KAAY;OACzE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,EAAiB,mBAChB,kBAAC,UAAD;MACE,OAAO,EAAiB;MACxB,WAAW,MAAU,EAAiB,mBAAmB,OAAO,EAAM,OAAO,MAAM,CAAC;MACpF,UAAU,EAAiB;MAC3B,WAAU;MACV,cAAW;gBAEV;OAAC;OAAI;OAAI;OAAI;OAAI,CAAC,KAAK,MACtB,kBAAC,UAAD;OAAqB,OAAO;iBAA5B,CACG,GAAO,UACD;SAFI,EAEJ,CACT;MACK,CAAA,GACP;KACJ,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAiB,aAAa,EAAiB,OAAO,EAAE;MACvE,UAAU,EAAiB,WAAW,EAAiB,QAAQ;MAC/D,WAAU;gBAJZ,CAME,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA,EAAA,WAE5B;;KACT,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAiB,aAAa,EAAiB,OAAO,EAAE;MACvE,UACE,EAAiB,WACjB,EAAE,EAAiB,eAAe,EAAO,UAAU,EAAiB;MAEtE,WAAU;gBAPZ,CAQC,QAEC,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA,CAC7B;;KACL;MACF;OACJ,EAAO,SAAS,IAClB,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,QAAD;IAAM,WAAU;cAAhB;KAAsC;KAC3B,IAAO,IAAW;KAAE;KAAE,KAAK,KAAK,IAAO,KAAK,GAAU,EAAO,OAAO;KAAC;KAAK,EAAO;KACrF;OACP,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,UAAD;MACE,eAAe,GAAS,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC;MACjD,UAAU,MAAS;MACnB,WAAU;gBAEV,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;MAC5B,CAAA;KACR,MAAM,KAAK,EAAE,QAAQ,GAAY,CAAC,CAAC,KAAK,GAAG,MAC1C,kBAAC,UAAD;MAEE,eAAe,EAAQ,EAAE;MACzB,WAAW,EACT,4DACA,MAAM,IACF,kDACA,yCACL;gBAEA,IAAI;MACE,EAVF,EAUE,CACT;KACF,kBAAC,UAAD;MACE,eAAe,GAAS,MAAM,KAAK,IAAI,IAAa,GAAG,IAAI,EAAE,CAAC;MAC9D,UAAU,KAAQ,IAAa;MAC/B,WAAU;gBAEV,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA;MAC7B,CAAA;KACL;MACF;OACJ,KACA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmptyState.js","names":[],"sources":["../../../src/housingvista/components/EmptyState.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { Inbox } from 'lucide-react';\nimport { IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/helpers';\n\ninterface EmptyStateProps {\n icon?: ReactNode;\n title: string;\n description?: string;\n action?: ReactNode;\n className?: string;\n /**\n * Boff UI 2.0 — on-brand illustration name served by the host shell from\n * `/illustrations/<name>.webp`. When provided, renders the IllustratedEmptyState\n * primitive (preferred). Standard names: empty-data, empty-search, empty-files,\n * empty-people, empty-notifications, onboarding, empty-generic.\n */\n illustration?: string;\n}\n\nexport const EmptyState: FC<EmptyStateProps> = ({
|
|
1
|
+
{"version":3,"file":"EmptyState.js","names":[],"sources":["../../../src/housingvista/components/EmptyState.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { Inbox } from 'lucide-react';\nimport { IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/helpers';\n\ninterface EmptyStateProps {\n icon?: ReactNode;\n title: string;\n description?: string;\n action?: ReactNode;\n className?: string;\n /**\n * Boff UI 2.0 — on-brand illustration name served by the host shell from\n * `/illustrations/<name>.webp`. When provided, renders the IllustratedEmptyState\n * primitive (preferred). Standard names: empty-data, empty-search, empty-files,\n * empty-people, empty-notifications, onboarding, empty-generic.\n */\n illustration?: string;\n}\n\nexport const EmptyState: FC<EmptyStateProps> = ({ icon, title, description, action, className, illustration }) => {\n if (illustration) {\n return (\n <IllustratedEmptyState\n illustration={illustration}\n title={title}\n description={description}\n action={action}\n className={cn('rounded-card border border-dashed border-border-seam bg-bg-surface/50 p-12', className)}\n />\n );\n }\n\n return (\n <div\n className={cn(\n 'flex flex-col items-center justify-center rounded-card border border-dashed border-border-seam bg-bg-surface/50 p-12 text-center',\n className\n )}\n >\n <div className=\"mb-4 text-text-secondary\">{icon || <Inbox className=\"h-12 w-12\" />}</div>\n <h3 className=\"text-lg font-medium text-text-primary\">{title}</h3>\n {description && <p className=\"mt-1 text-sm text-text-secondary max-w-md\">{description}</p>}\n {action && <div className=\"mt-4\">{action}</div>}\n </div>\n );\n};\n"],"mappings":";;;;;AAoBA,IAAa,KAAmC,EAAE,SAAM,UAAO,gBAAa,WAAQ,cAAW,sBACzF,IAEA,kBAAC,GAAD;CACgB;CACP;CACM;CACL;CACR,WAAW,EAAG,8EAA8E,EAAU;CACtG,CAAA,GAKJ,kBAAC,OAAD;CACE,WAAW,EACT,oIACA,EACD;WAJH;EAME,kBAAC,OAAD;GAAK,WAAU;aAA4B,KAAQ,kBAAC,GAAD,EAAO,WAAU,aAAc,CAAA;GAAO,CAAA;EACzF,kBAAC,MAAD;GAAI,WAAU;aAAyC;GAAW,CAAA;EACjE,KAAe,kBAAC,KAAD;GAAG,WAAU;aAA6C;GAAgB,CAAA;EACzF,KAAU,kBAAC,OAAD;GAAK,WAAU;aAAQ;GAAa,CAAA;EAC3C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/housingvista/components/PageLayout.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { GradientText, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/helpers';\n\ninterface PageLayoutProps {\n title: string;\n description?: string;\n actions?: ReactNode;\n children: ReactNode;\n className?: string;\n /**\n * Boff UI 2.0 — a calm one/two-line \"story\" explaining WHAT this page is for\n * and the use-case, so a new user understands it at a glance. Rendered with\n * the {@link PagePurpose} primitive directly under the title.\n */\n purpose?: ReactNode;\n}\n\nexport const PageLayout: FC<PageLayoutProps> = ({
|
|
1
|
+
{"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/housingvista/components/PageLayout.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { GradientText, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/helpers';\n\ninterface PageLayoutProps {\n title: string;\n description?: string;\n actions?: ReactNode;\n children: ReactNode;\n className?: string;\n /**\n * Boff UI 2.0 — a calm one/two-line \"story\" explaining WHAT this page is for\n * and the use-case, so a new user understands it at a glance. Rendered with\n * the {@link PagePurpose} primitive directly under the title.\n */\n purpose?: ReactNode;\n}\n\nexport const PageLayout: FC<PageLayoutProps> = ({ title, description, actions, children, className, purpose }) => {\n return (\n <div className={cn('flex flex-col gap-6 p-6', className)}>\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl font-semibold text-text-primary\">\n <GradientText>{title}</GradientText>\n </h1>\n {description && <p className=\"mt-1 text-sm text-text-secondary\">{description}</p>}\n </div>\n {actions && <div className=\"flex items-center gap-2 shrink-0\">{actions}</div>}\n </div>\n {purpose && <PagePurpose className=\"max-w-3xl\">{purpose}</PagePurpose>}\n </div>\n {children}\n </div>\n );\n};\n"],"mappings":";;;;AAkBA,IAAa,KAAmC,EAAE,UAAO,gBAAa,YAAS,aAAU,cAAW,iBAEhG,kBAAC,OAAD;CAAK,WAAW,EAAG,2BAA2B,EAAU;WAAxD,CACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,GAAD,EAAA,UAAe,GAAqB,CAAA;IACjC,CAAA,EACJ,KAAe,kBAAC,KAAD;IAAG,WAAU;cAAoC;IAAgB,CAAA,CAC7E,EAAA,CAAA,EACL,KAAW,kBAAC,OAAD;IAAK,WAAU;cAAoC;IAAc,CAAA,CACzE;MACL,KAAW,kBAAC,GAAD;GAAa,WAAU;aAAa;GAAsB,CAAA,CAClE;KACL,EACG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PropertyCard.js","names":[],"sources":["../../../src/housingvista/components/PropertyCard.tsx"],"sourcesContent":["import { type FC } from 'react';\nimport { MapPin, Bed, Bath, Square, Heart } from 'lucide-react';\nimport { cn, formatCurrency, formatNumber } from '../utils/helpers';\nimport { StatusBadge } from './StatusBadge';\n\ninterface PropertyCardProps {\n id: string;\n title: string;\n price: number;\n priceType: 'sale' | 'rent';\n pricePeriod?: string;\n location: string;\n bedrooms?: number;\n bathrooms?: number;\n area: number;\n imageUrl?: string;\n status: string;\n type: string;\n onClick?: () => void;\n className?: string;\n /** When provided the heart becomes a controlled shortlist toggle. */\n isShortlisted?: boolean;\n onToggleShortlist?: () => void;\n}\n\nconst statusVariantMap: Record<string, 'success' | 'warning' | 'error' | 'info' | 'default' | 'muted'> = {\n available: 'success',\n active: 'success',\n listed: 'success',\n pending: 'warning',\n 'under contract': 'warning',\n 'under offer': 'warning',\n sold: 'error',\n rented: 'error',\n off_market: 'muted',\n 'off market': 'muted',\n draft: 'muted',\n};\n\nexport const PropertyCard: FC<PropertyCardProps> = ({\n title,\n price,\n priceType,\n pricePeriod,\n location,\n bedrooms,\n bathrooms,\n area,\n imageUrl,\n status,\n type,\n onClick,\n className,\n isShortlisted,\n onToggleShortlist,\n}) => {\n const priceDisplay =\n priceType === 'rent' ? `${formatCurrency(price)}${pricePeriod ? `/${pricePeriod}` : '/mo'}` : formatCurrency(price);\n\n const badgeVariant = statusVariantMap[status.toLowerCase()] ?? 'default';\n\n return (\n <div\n className={cn(\n 'group overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-elevation-1 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-elevation-2',\n onClick && 'cursor-pointer',\n className\n )}\n onClick={onClick}\n >\n {/* Image Section */}\n <div className=\"relative aspect-[16/10] overflow-hidden\">\n {imageUrl ? (\n <img
|
|
1
|
+
{"version":3,"file":"PropertyCard.js","names":[],"sources":["../../../src/housingvista/components/PropertyCard.tsx"],"sourcesContent":["import { type FC } from 'react';\nimport { MapPin, Bed, Bath, Square, Heart } from 'lucide-react';\nimport { cn, formatCurrency, formatNumber } from '../utils/helpers';\nimport { StatusBadge } from './StatusBadge';\n\ninterface PropertyCardProps {\n id: string;\n title: string;\n price: number;\n priceType: 'sale' | 'rent';\n pricePeriod?: string;\n location: string;\n bedrooms?: number;\n bathrooms?: number;\n area: number;\n imageUrl?: string;\n status: string;\n type: string;\n onClick?: () => void;\n className?: string;\n /** When provided the heart becomes a controlled shortlist toggle. */\n isShortlisted?: boolean;\n onToggleShortlist?: () => void;\n}\n\nconst statusVariantMap: Record<string, 'success' | 'warning' | 'error' | 'info' | 'default' | 'muted'> = {\n available: 'success',\n active: 'success',\n listed: 'success',\n pending: 'warning',\n 'under contract': 'warning',\n 'under offer': 'warning',\n sold: 'error',\n rented: 'error',\n off_market: 'muted',\n 'off market': 'muted',\n draft: 'muted',\n};\n\nexport const PropertyCard: FC<PropertyCardProps> = ({\n title,\n price,\n priceType,\n pricePeriod,\n location,\n bedrooms,\n bathrooms,\n area,\n imageUrl,\n status,\n type,\n onClick,\n className,\n isShortlisted,\n onToggleShortlist,\n}) => {\n const priceDisplay =\n priceType === 'rent' ? `${formatCurrency(price)}${pricePeriod ? `/${pricePeriod}` : '/mo'}` : formatCurrency(price);\n\n const badgeVariant = statusVariantMap[status.toLowerCase()] ?? 'default';\n\n return (\n <div\n className={cn(\n 'group overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-elevation-1 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-elevation-2',\n onClick && 'cursor-pointer',\n className\n )}\n onClick={onClick}\n >\n {/* Image Section */}\n <div className=\"relative aspect-[16/10] overflow-hidden\">\n {imageUrl ? (\n <img src={imageUrl} alt={title} className=\"h-full w-full object-cover\" />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center bg-gradient-to-br from-bg-sunken to-bg-sunken/60\">\n <Square className=\"h-10 w-10 text-text-secondary/40\" />\n </div>\n )}\n\n {/* Overlays */}\n <div className=\"absolute left-3 top-3\">\n <StatusBadge label={status} variant={badgeVariant} dot />\n </div>\n {onToggleShortlist && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n onToggleShortlist();\n }}\n className={cn(\n 'absolute right-3 top-3 rounded-full bg-bg-surface/80 p-1.5 backdrop-blur-sm transition-colors hover:bg-bg-surface',\n isShortlisted ? 'text-status-error-text' : 'text-text-secondary'\n )}\n aria-label={isShortlisted ? 'Remove from shortlist' : 'Save to shortlist'}\n >\n <Heart className={cn('h-4 w-4', isShortlisted && 'fill-current')} />\n </button>\n )}\n <div className=\"absolute bottom-3 left-3\">\n <span className=\"rounded-md bg-bg-surface/80 px-2 py-1 text-xs font-medium text-text-primary backdrop-blur-sm\">\n {type}\n </span>\n </div>\n </div>\n\n {/* Content Section */}\n <div className=\"p-4\">\n {/* Price */}\n <div className=\"flex items-baseline justify-between gap-2\">\n <span className=\"text-lg font-bold text-text-primary\">{priceDisplay}</span>\n {priceType === 'sale' && <span className=\"text-xs font-medium text-text-secondary\">For Sale</span>}\n </div>\n\n {/* Title */}\n <h3 className=\"mt-1 truncate text-sm font-medium text-text-primary\">{title}</h3>\n\n {/* Location */}\n <div className=\"mt-1.5 flex items-center gap-1 text-text-secondary\">\n <MapPin className=\"h-3.5 w-3.5 shrink-0\" />\n <span className=\"truncate text-xs\">{location}</span>\n </div>\n\n {/* Specs Row */}\n <div className=\"mt-3 flex items-center gap-3 border-t border-border-subtle pt-3\">\n {bedrooms != null && (\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Bed className=\"h-3.5 w-3.5\" />\n <span>\n {bedrooms} {bedrooms === 1 ? 'Bed' : 'Beds'}\n </span>\n </div>\n )}\n {bathrooms != null && (\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Bath className=\"h-3.5 w-3.5\" />\n <span>\n {bathrooms} {bathrooms === 1 ? 'Bath' : 'Baths'}\n </span>\n </div>\n )}\n <div className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Square className=\"h-3.5 w-3.5\" />\n <span>{formatNumber(area)} sq ft</span>\n </div>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;AAyBA,IAAM,IAAmG;CACvG,WAAW;CACX,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,kBAAkB;CAClB,eAAe;CACf,MAAM;CACN,QAAQ;CACR,YAAY;CACZ,cAAc;CACd,OAAO;CACR,EAEY,KAAuC,EAClD,UACA,UACA,cACA,gBACA,aACA,aACA,cACA,SACA,aACA,WACA,SACA,YACA,cACA,kBACA,2BACI;CACJ,IAAM,IACJ,MAAc,SAAS,GAAG,EAAe,EAAM,GAAG,IAAc,IAAI,MAAgB,UAAU,EAAe,EAAM,EAE/G,IAAe,EAAiB,EAAO,aAAa,KAAK;AAE/D,QACE,kBAAC,OAAD;EACE,WAAW,EACT,0LACA,KAAW,kBACX,EACD;EACQ;YANX,CASE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,IACC,kBAAC,OAAD;KAAK,KAAK;KAAU,KAAK;KAAO,WAAU;KAA+B,CAAA,GAEzE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAQ,WAAU,oCAAqC,CAAA;KACnD,CAAA;IAIR,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAa,OAAO;MAAQ,SAAS;MAAc,KAAA;MAAM,CAAA;KACrD,CAAA;IACL,KACC,kBAAC,UAAD;KACE,UAAU,MAAM;AAEd,MADA,EAAE,iBAAiB,EACnB,GAAmB;;KAErB,WAAW,EACT,qHACA,IAAgB,2BAA2B,sBAC5C;KACD,cAAY,IAAgB,0BAA0B;eAEtD,kBAAC,GAAD,EAAO,WAAW,EAAG,WAAW,KAAiB,eAAe,EAAI,CAAA;KAC7D,CAAA;IAEX,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,QAAD;MAAM,WAAU;gBACb;MACI,CAAA;KACH,CAAA;IACF;MAGN,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MAAM,WAAU;gBAAuC;MAAoB,CAAA,EAC1E,MAAc,UAAU,kBAAC,QAAD;MAAM,WAAU;gBAA0C;MAAe,CAAA,CAC9F;;IAGN,kBAAC,MAAD;KAAI,WAAU;eAAuD;KAAW,CAAA;IAGhF,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,wBAAyB,CAAA,EAC3C,kBAAC,QAAD;MAAM,WAAU;gBAAoB;MAAgB,CAAA,CAChD;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,KAAY,QACX,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAK,WAAU,eAAgB,CAAA,EAC/B,kBAAC,QAAD,EAAA,UAAA;QACG;QAAS;QAAE,MAAa,IAAI,QAAQ;QAChC,EAAA,CAAA,CACH;;MAEP,KAAa,QACZ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA,EAChC,kBAAC,QAAD,EAAA,UAAA;QACG;QAAU;QAAE,MAAc,IAAI,SAAS;QACnC,EAAA,CAAA,CACH;;MAER,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,eAAgB,CAAA,EAClC,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAa,EAAK,EAAC,SAAa,EAAA,CAAA,CACnC;;MACF;;IACF;KACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/housingvista/hooks/index.ts"],"sourcesContent":["/**\n * HousingVista data hooks — real GraphQL against the wspace gateway via Apollo.\n *\n * Every read maps the `housingvista*` query contract to the MFE domain types in\n * `../types`. Every write wraps a `housingvista*` mutation and (where it changes\n * a list the UI shows) refetches the affected query so the cache stays honest.\n * No `any`, no mock data — hooks that have no backing query return an explicit\n * `unsupported` empty-state instead of fabricating rows.\n * @module housingvista\n */\n\nimport { gql } from '@apollo/client';\nimport { useQuery, useMutation } from '@apollo/client/react';\nimport type {\n Property,\n Transaction,\n Investment,\n DashboardStats,\n RecentActivity,\n RentalUnit,\n Asset,\n AMCContract,\n MarketplaceService,\n ConversationListItem,\n Notification,\n VerificationItem,\n LegalService,\n HomeLoan,\n MarketTrend,\n AreaInsight,\n AnalyticsChartData,\n PropertyType,\n PropertyStatus,\n} from '../types';\n\n// ---------------------------------------------------------------------------\n// Shared input shapes (mirror HousingVista*Input from the subgraph schema)\n// ---------------------------------------------------------------------------\n\nexport type PaginationInput = {\n limit?: number;\n offset?: number;\n};\n\nexport type PropertyFilterInput = {\n search?: string;\n status?: PropertyStatus;\n type?: PropertyType;\n city?: string;\n};\n\nconst DEFAULT_LIST_PAGINATION: Required<PaginationInput> = { limit: 50, offset: 0 };\n\nconst getPaginationVariables = (pagination: PaginationInput = DEFAULT_LIST_PAGINATION) => ({\n pagination: {\n limit: pagination.limit ?? DEFAULT_LIST_PAGINATION.limit,\n offset: pagination.offset ?? DEFAULT_LIST_PAGINATION.offset,\n },\n});\n\n// ---------------------------------------------------------------------------\n// Query documents\n// ---------------------------------------------------------------------------\n\nconst GET_DASHBOARD_STATS = gql`\n query GetDashboardStats {\n housingvistaDashboardStats {\n totalProperties\n totalValue\n occupancyRate\n monthlyRevenue\n activeTransactions\n pendingVerifications\n totalInvestments\n totalPortfolioValue\n investmentReturns\n totalTenants\n maintenanceRequests\n unreadMessages\n upcomingViewings\n }\n }\n`;\n\nconst PROPERTY_FIELDS = gql`\n fragment PropertyFields on HousingVistaProperty {\n id\n title\n description\n type\n status\n category\n verificationStatus\n imageUrl\n createdAt\n updatedAt\n price {\n amount\n currency\n type\n period\n negotiable\n pricePerSqft\n }\n location {\n address\n city\n state\n country\n pincode\n coordinates {\n latitude\n longitude\n }\n neighborhood\n landmarks\n }\n specifications {\n totalArea\n carpetArea\n builtUpArea\n bedrooms\n bathrooms\n floors\n totalFloors\n parking\n balconies\n furnishingStatus\n ageOfProperty\n facing\n }\n amenities {\n id\n name\n category\n available\n }\n images {\n id\n url\n title\n type\n order\n isPrimary\n }\n }\n`;\n\nconst GET_PROPERTIES = gql`\n ${PROPERTY_FIELDS}\n query GetProperties($filter: HousingVistaPropertyFilterInput, $pagination: HousingVistaPaginationInput) {\n housingvistaProperties(filter: $filter, pagination: $pagination) {\n ...PropertyFields\n }\n }\n`;\n\nconst GET_PROPERTY = gql`\n ${PROPERTY_FIELDS}\n query GetProperty($id: ID!) {\n housingvistaProperty(id: $id) {\n ...PropertyFields\n }\n }\n`;\n\nconst TRANSACTION_FIELDS = gql`\n fragment TransactionFields on HousingVistaTransaction {\n id\n title\n type\n status\n amount\n currency\n documents\n milestones\n parties\n escrow\n createdAt\n updatedAt\n property {\n id\n title\n location {\n city\n state\n }\n images {\n id\n url\n isPrimary\n }\n }\n buyer {\n id\n firstName\n lastName\n avatar\n }\n seller {\n id\n firstName\n lastName\n avatar\n }\n tenant {\n id\n firstName\n lastName\n avatar\n }\n landlord {\n id\n firstName\n lastName\n avatar\n }\n }\n`;\n\nconst GET_TRANSACTIONS = gql`\n ${TRANSACTION_FIELDS}\n query GetTransactions($pagination: HousingVistaPaginationInput) {\n housingvistaTransactions(pagination: $pagination) {\n ...TransactionFields\n }\n }\n`;\n\nconst GET_TRANSACTION = gql`\n ${TRANSACTION_FIELDS}\n query GetTransaction($id: ID!) {\n housingvistaTransaction(id: $id) {\n ...TransactionFields\n }\n }\n`;\n\nconst INVESTMENT_FIELDS = gql`\n fragment InvestmentFields on HousingVistaInvestment {\n id\n title\n investmentType\n totalValue\n status\n startDate\n maturityDate\n riskLevel\n investors\n returns\n property {\n id\n title\n location {\n city\n state\n }\n }\n }\n`;\n\nconst GET_INVESTMENTS = gql`\n ${INVESTMENT_FIELDS}\n query GetInvestments($pagination: HousingVistaPaginationInput) {\n housingvistaInvestments(pagination: $pagination) {\n ...InvestmentFields\n }\n }\n`;\n\nconst GET_INVESTMENT = gql`\n ${INVESTMENT_FIELDS}\n query GetInvestment($id: ID!) {\n housingvistaInvestment(id: $id) {\n ...InvestmentFields\n }\n }\n`;\n\nconst GET_RENTAL_UNITS = gql`\n query GetRentalUnits($pagination: HousingVistaPaginationInput) {\n housingvistaRentalUnits(pagination: $pagination) {\n id\n propertyId\n propertyTitle\n unitNumber\n tenantName\n tenantEmail\n tenantPhone\n monthlyRent\n leaseStart\n leaseEnd\n status\n lastPaymentDate\n paymentStatus\n securityDeposit\n }\n }\n`;\n\nconst GET_ASSETS = gql`\n query GetAssets($pagination: HousingVistaPaginationInput) {\n housingvistaAssets(pagination: $pagination) {\n id\n title\n type\n category\n location\n currentValue\n purchasePrice\n purchaseDate\n appreciationPercent\n rentalYield\n status\n imageUrl\n lastValuationDate\n }\n }\n`;\n\nconst GET_AMC_CONTRACTS = gql`\n query GetAmcContracts($pagination: HousingVistaPaginationInput) {\n housingvistaAmcContracts(pagination: $pagination) {\n id\n propertyId\n propertyTitle\n packageName\n providerName\n status\n startDate\n endDate\n monthlyCost\n nextServiceDate\n servicesIncluded\n }\n }\n`;\n\nconst GET_MARKETPLACE_SERVICES = gql`\n query GetMarketplaceServices($pagination: HousingVistaPaginationInput) {\n housingvistaMarketplaceServices(pagination: $pagination) {\n id\n name\n providerName\n category\n description\n rating\n reviewCount\n priceRange\n availableAreas\n responseTime\n verified\n imageUrl\n featured\n }\n }\n`;\n\nconst GET_VERIFICATIONS = gql`\n query GetVerifications($pagination: HousingVistaPaginationInput) {\n housingvistaVerificationItems(pagination: $pagination) {\n id\n entityType\n entityId\n entityLabel\n status\n submittedDate\n reviewerName\n priority\n notes\n }\n }\n`;\n\nconst GET_LEGAL_SERVICES = gql`\n query GetLegalServices($pagination: HousingVistaPaginationInput) {\n housingvistaLegalServices(pagination: $pagination) {\n id\n title\n category\n description\n providerName\n providerRating\n estimatedDuration\n priceRange\n status\n consultationFee\n documentsRequired\n }\n }\n`;\n\nconst GET_HOME_LOANS = gql`\n query GetHomeLoans($pagination: HousingVistaPaginationInput) {\n housingvistaHomeLoanOffers(pagination: $pagination) {\n id\n bankName\n bankLogo\n interestRate\n maxTenure\n maxAmount\n processingFee\n emiPerLakh\n features\n rating\n specialOffer\n prepaymentCharges\n }\n }\n`;\n\nconst GET_MARKET_TRENDS = gql`\n query GetMarketTrends($pagination: HousingVistaPaginationInput) {\n housingvistaMarketTrends(pagination: $pagination) {\n month\n avgPrice\n avgRent\n demandIndex\n supplyIndex\n priceChangePercent\n }\n }\n`;\n\nconst GET_AREA_INSIGHTS = gql`\n query GetAreaInsights($pagination: HousingVistaPaginationInput) {\n housingvistaAreaInsights(pagination: $pagination) {\n id\n areaName\n city\n avgPropertyPrice\n avgRentalYield\n appreciationRate\n safetyScore\n connectivityScore\n amenitiesScore\n schoolsNearby\n hospitalsNearby\n population\n greenCoverPercent\n upcomingProjects\n }\n }\n`;\n\nconst GET_ANALYTICS = gql`\n query GetHousingvistaAnalytics {\n housingvistaAnalytics {\n kpis {\n id\n label\n value\n previousValue\n unit\n trend\n changePercent\n }\n revenueByMonth {\n month\n revenue\n expenses\n }\n propertyTypeDistribution {\n type\n count\n value\n }\n occupancyRate {\n month\n rate\n }\n }\n }\n`;\n\nconst SHORTLIST_FIELDS = gql`\n ${PROPERTY_FIELDS}\n fragment ShortlistFields on HousingVistaShortlist {\n id\n propertyId\n notes\n createdAt\n property {\n ...PropertyFields\n }\n }\n`;\n\nconst GET_SHORTLISTS = gql`\n ${SHORTLIST_FIELDS}\n query GetShortlists($pagination: HousingVistaPaginationInput) {\n housingvistaShortlists(pagination: $pagination) {\n ...ShortlistFields\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Mutation documents\n// ---------------------------------------------------------------------------\n\nconst CREATE_PROPERTY = gql`\n ${PROPERTY_FIELDS}\n mutation CreateHousingvistaProperty($input: HousingVistaCreatePropertyInput!) {\n createHousingvistaProperty(input: $input) {\n ...PropertyFields\n }\n }\n`;\n\nconst UPDATE_PROPERTY = gql`\n ${PROPERTY_FIELDS}\n mutation UpdateHousingvistaProperty($id: ID!, $input: HousingVistaUpdatePropertyInput!) {\n updateHousingvistaProperty(id: $id, input: $input) {\n ...PropertyFields\n }\n }\n`;\n\nconst DELETE_PROPERTY = gql`\n mutation DeleteHousingvistaProperty($id: ID!) {\n deleteHousingvistaProperty(id: $id)\n }\n`;\n\nconst SAVE_SHORTLIST = gql`\n ${SHORTLIST_FIELDS}\n mutation SaveHousingvistaShortlist($input: HousingVistaSaveShortlistInput!) {\n saveHousingvistaShortlist(input: $input) {\n ...ShortlistFields\n }\n }\n`;\n\nconst REMOVE_SHORTLIST = gql`\n mutation RemoveHousingvistaShortlist($propertyId: ID!) {\n removeHousingvistaShortlist(propertyId: $propertyId)\n }\n`;\n\nconst BOOK_VIEWING = gql`\n mutation BookHousingvistaViewing($input: HousingVistaBookViewingInput!) {\n bookHousingvistaViewing(input: $input) {\n id\n propertyId\n scheduledAt\n type\n status\n attendeeName\n attendeeEmail\n notes\n }\n }\n`;\n\nconst CANCEL_VIEWING = gql`\n mutation CancelHousingvistaViewing($id: ID!) {\n cancelHousingvistaViewing(id: $id) {\n id\n status\n }\n }\n`;\n\nconst SUBMIT_OFFER = gql`\n mutation SubmitHousingvistaOffer($input: HousingVistaSubmitOfferInput!) {\n submitHousingvistaOffer(input: $input) {\n id\n propertyId\n amount\n currency\n status\n notes\n createdAt\n }\n }\n`;\n\nconst RESPOND_OFFER = gql`\n mutation RespondHousingvistaOffer($id: ID!, $input: HousingVistaRespondOfferInput!) {\n respondHousingvistaOffer(id: $id, input: $input) {\n id\n status\n counterAmount\n respondedAt\n }\n }\n`;\n\nconst CREATE_TRANSACTION = gql`\n ${TRANSACTION_FIELDS}\n mutation CreateHousingvistaTransaction($input: HousingVistaCreateTransactionInput!) {\n createHousingvistaTransaction(input: $input) {\n ...TransactionFields\n }\n }\n`;\n\nconst CREATE_INVESTMENT = gql`\n ${INVESTMENT_FIELDS}\n mutation CreateHousingvistaInvestment($input: HousingVistaCreateInvestmentInput!) {\n createHousingvistaInvestment(input: $input) {\n ...InvestmentFields\n }\n }\n`;\n\nconst CREATE_RENTAL_UNIT = gql`\n mutation CreateHousingvistaRentalUnit($input: HousingVistaCreateRentalUnitInput!) {\n createHousingvistaRentalUnit(input: $input) {\n id\n propertyTitle\n unitNumber\n tenantName\n monthlyRent\n status\n }\n }\n`;\n\nconst SUBMIT_RENTAL_APPLICATION = gql`\n mutation SubmitHousingvistaRentalApplication($input: HousingVistaSubmitRentalApplicationInput!) {\n submitHousingvistaRentalApplication(input: $input) {\n id\n rentalUnitId\n applicantName\n status\n }\n }\n`;\n\nconst CREATE_AMC_CONTRACT = gql`\n mutation CreateHousingvistaAmcContract($input: HousingVistaCreateAmcContractInput!) {\n createHousingvistaAmcContract(input: $input) {\n id\n propertyTitle\n packageName\n providerName\n monthlyCost\n status\n }\n }\n`;\n\nconst CREATE_MAINTENANCE_REQUEST = gql`\n mutation CreateHousingvistaMaintenanceRequest($input: HousingVistaCreateMaintenanceRequestInput!) {\n createHousingvistaMaintenanceRequest(input: $input) {\n id\n title\n category\n priority\n status\n }\n }\n`;\n\nconst APPROVE_VERIFICATION = gql`\n mutation ApproveHousingvistaVerificationItem($id: ID!, $notes: String) {\n approveHousingvistaVerificationItem(id: $id, notes: $notes) {\n id\n status\n reviewerName\n notes\n }\n }\n`;\n\nconst REJECT_VERIFICATION = gql`\n mutation RejectHousingvistaVerificationItem($id: ID!, $notes: String) {\n rejectHousingvistaVerificationItem(id: $id, notes: $notes) {\n id\n status\n reviewerName\n notes\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Query-result row shapes (as selected above)\n// ---------------------------------------------------------------------------\n\ninterface PropertyRow {\n id: string;\n title: string;\n description: string | null;\n type: Property['type'];\n status: Property['status'];\n category: string;\n verificationStatus: string;\n imageUrl: string | null;\n createdAt: string;\n updatedAt: string;\n price: Property['price'];\n location: Omit<Property['location'], 'coordinates'> & {\n coordinates: { latitude: number; longitude: number } | null;\n };\n specifications: Property['specifications'];\n amenities: Property['amenities'];\n images: Property['images'];\n}\n\ninterface TransactionRow {\n id: string;\n title: string;\n type: string;\n status: string;\n amount: number;\n currency: string;\n documents: unknown;\n milestones: unknown;\n parties: unknown;\n escrow: unknown;\n createdAt: string;\n updatedAt: string;\n property: PropertyRow | null;\n buyer: TxParty | null;\n seller: TxParty | null;\n tenant: TxParty | null;\n landlord: TxParty | null;\n}\n\ninterface TxParty {\n id: string | null;\n firstName: string | null;\n lastName: string | null;\n avatar: string | null;\n}\n\ninterface InvestmentRow {\n id: string;\n title: string;\n investmentType: string;\n totalValue: number;\n status: string;\n startDate: string;\n maturityDate: string | null;\n riskLevel: string;\n investors: unknown;\n returns: unknown;\n property: PropertyRow | null;\n}\n\ninterface ShortlistRow {\n id: string;\n propertyId: string;\n notes: string | null;\n createdAt: string;\n property: PropertyRow;\n}\n\n// ---------------------------------------------------------------------------\n// Mappers: GraphQL row → MFE domain type\n// ---------------------------------------------------------------------------\n\n/**\n * The domain `Property` carries display-only fields the subgraph does not\n * resolve (owner/documents/features/virtualTours/availability). We fill the\n * present fields from the query and default the rest to empty so consumers can\n * render honest empty-states rather than crashing on `undefined`.\n */\nconst mapProperty = (row: PropertyRow): Property => ({\n id: row.id,\n title: row.title,\n description: row.description ?? '',\n type: row.type,\n status: row.status,\n category: row.category as Property['category'],\n price: row.price,\n location: {\n ...row.location,\n coordinates: row.location.coordinates ?? { latitude: 0, longitude: 0 },\n },\n specifications: row.specifications,\n amenities: row.amenities ?? [],\n images: row.images ?? [],\n documents: [],\n virtualTours: [],\n owner: { id: '', name: '', email: '', phone: '', verified: false },\n features: [],\n verificationStatus: (row.verificationStatus as Property['verificationStatus']) ?? 'pending',\n availability: { available: true },\n createdAt: new Date(row.createdAt),\n updatedAt: new Date(row.updatedAt),\n});\n\n// ---------------------------------------------------------------------------\n// Query hooks\n// ---------------------------------------------------------------------------\n\nexport const useDashboardStats = () => {\n const { data, loading, error } = useQuery<{ housingvistaDashboardStats: DashboardStats }>(GET_DASHBOARD_STATS);\n const empty: DashboardStats = {\n totalProperties: 0,\n totalValue: 0,\n occupancyRate: 0,\n monthlyRevenue: 0,\n activeTransactions: 0,\n pendingVerifications: 0,\n totalInvestments: 0,\n investmentReturns: 0,\n totalTenants: 0,\n maintenanceRequests: 0,\n unreadMessages: 0,\n upcomingViewings: 0,\n };\n return { data: data?.housingvistaDashboardStats ?? empty, loading, error };\n};\n\nexport const useRecentActivities = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaTransactions: TransactionRow[] }>(GET_TRANSACTIONS, {\n variables: getPaginationVariables(pagination),\n });\n const activities: RecentActivity[] = (data?.housingvistaTransactions ?? []).map((txn) => ({\n id: `act-${txn.id}`,\n type: 'transaction_update',\n title: `Transaction: ${txn.type}`,\n description: `${txn.property?.title ?? 'Property'} - ${txn.status}`,\n timestamp: txn.updatedAt || txn.createdAt,\n entityId: txn.id,\n entityType: 'transaction',\n actorName: txn.buyer\n ? `${txn.buyer.firstName ?? ''} ${txn.buyer.lastName ?? ''}`.trim()\n : txn.tenant\n ? `${txn.tenant.firstName ?? ''} ${txn.tenant.lastName ?? ''}`.trim()\n : 'System',\n actorAvatar: txn.buyer?.avatar ?? txn.tenant?.avatar ?? undefined,\n }));\n return { data: activities, loading, error };\n};\n\nexport const useProperties = (options?: { filter?: PropertyFilterInput; pagination?: PaginationInput }) => {\n const { data, loading, error, refetch } = useQuery<{ housingvistaProperties: PropertyRow[] }>(GET_PROPERTIES, {\n variables: {\n filter: options?.filter,\n ...getPaginationVariables(options?.pagination),\n },\n });\n return {\n data: (data?.housingvistaProperties ?? []).map(mapProperty),\n loading,\n error,\n refetch,\n };\n};\n\nexport const useProperty = (id: string | undefined) => {\n const { data, loading, error } = useQuery<{ housingvistaProperty: PropertyRow | null }>(GET_PROPERTY, {\n variables: { id: id ?? '' },\n skip: !id,\n });\n return {\n data: data?.housingvistaProperty ? mapProperty(data.housingvistaProperty) : null,\n loading,\n error,\n };\n};\n\nexport const useTransactions = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaTransactions: TransactionRow[] }>(GET_TRANSACTIONS, {\n variables: getPaginationVariables(pagination),\n });\n // Transaction JSON columns (documents/milestones/parties/escrow) are opaque to\n // the schema; the page guards their runtime shape. Cast is localized here.\n return { data: (data?.housingvistaTransactions ?? []) as unknown as Transaction[], loading, error };\n};\n\nexport const useTransaction = (id: string | undefined) => {\n const { data, loading, error } = useQuery<{ housingvistaTransaction: TransactionRow | null }>(GET_TRANSACTION, {\n variables: { id: id ?? '' },\n skip: !id,\n });\n return {\n data: (data?.housingvistaTransaction ?? null) as unknown as Transaction | null,\n loading,\n error,\n };\n};\n\nexport const useInvestments = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaInvestments: InvestmentRow[] }>(GET_INVESTMENTS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: (data?.housingvistaInvestments ?? []) as unknown as Investment[], loading, error };\n};\n\nexport const useInvestment = (id: string | undefined) => {\n const { data, loading, error } = useQuery<{ housingvistaInvestment: InvestmentRow | null }>(GET_INVESTMENT, {\n variables: { id: id ?? '' },\n skip: !id,\n });\n return { data: (data?.housingvistaInvestment ?? null) as unknown as Investment | null, loading, error };\n};\n\nexport const useRentalUnits = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaRentalUnits: RentalUnit[] }>(GET_RENTAL_UNITS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaRentalUnits ?? [], loading, error };\n};\n\nexport const useAssets = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaAssets: Asset[] }>(GET_ASSETS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaAssets ?? [], loading, error };\n};\n\nexport const useAMCContracts = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaAmcContracts: AMCContract[] }>(GET_AMC_CONTRACTS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaAmcContracts ?? [], loading, error };\n};\n\nexport const useMarketplaceServices = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaMarketplaceServices: MarketplaceService[] }>(\n GET_MARKETPLACE_SERVICES,\n { variables: getPaginationVariables(pagination) }\n );\n return { data: data?.housingvistaMarketplaceServices ?? [], loading, error };\n};\n\nexport const useVerifications = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaVerificationItems: VerificationItem[] }>(GET_VERIFICATIONS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaVerificationItems ?? [], loading, error };\n};\n\nexport const useLegalServices = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaLegalServices: LegalService[] }>(GET_LEGAL_SERVICES, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaLegalServices ?? [], loading, error };\n};\n\nexport const useHomeLoans = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaHomeLoanOffers: HomeLoan[] }>(GET_HOME_LOANS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaHomeLoanOffers ?? [], loading, error };\n};\n\nexport const useMarketTrends = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaMarketTrends: MarketTrend[] }>(GET_MARKET_TRENDS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaMarketTrends ?? [], loading, error };\n};\n\nexport const useAreaInsights = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaAreaInsights: AreaInsight[] }>(GET_AREA_INSIGHTS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaAreaInsights ?? [], loading, error };\n};\n\nexport const useShortlists = (pagination?: PaginationInput) => {\n const { data, loading, error, refetch } = useQuery<{ housingvistaShortlists: ShortlistRow[] }>(GET_SHORTLISTS, {\n variables: getPaginationVariables(pagination),\n });\n const shortlists = (data?.housingvistaShortlists ?? []).map((row) => ({\n id: row.id,\n propertyId: row.propertyId,\n notes: row.notes ?? undefined,\n createdAt: row.createdAt,\n property: mapProperty(row.property),\n }));\n return { data: shortlists, loading, error, refetch };\n};\n\nexport const useAnalyticsData = () => {\n const { data, loading, error } = useQuery<{ housingvistaAnalytics: AnalyticsChartData }>(GET_ANALYTICS);\n return { data: data?.housingvistaAnalytics ?? null, loading, error };\n};\n\n/**\n * Conversations are owned by the platform conversations module, which the MFE\n * shell does not currently expose to housingvista. Return an explicit empty\n * state (never mock rows) so the page renders an honest \"coming soon\".\n */\nexport const useConversations = () => {\n return { data: [] as ConversationListItem[], loading: false, error: null, unsupported: true };\n};\n\n/**\n * Notifications are surfaced by the shell's global notification centre; the MFE\n * has no dedicated feed query, so return an honest empty state.\n */\nexport const useNotifications = () => {\n return { data: [] as Notification[], loading: false, error: null, unsupported: true };\n};\n\n// ---------------------------------------------------------------------------\n// Mutation hooks\n// ---------------------------------------------------------------------------\n\nexport interface CreatePropertyInput {\n title: string;\n description?: string;\n status: PropertyStatus;\n type: PropertyType;\n category: string;\n address?: string;\n city: string;\n state: string;\n country?: string;\n pincode?: string;\n neighborhood?: string;\n landmarks?: string[];\n price: number;\n currency?: string;\n priceType?: string;\n pricePeriod?: string;\n negotiable?: boolean;\n pricePerSqft?: number;\n bedrooms: number;\n bathrooms: number;\n totalArea: number;\n carpetArea?: number;\n builtUpArea?: number;\n floors?: number;\n totalFloors?: number;\n parking?: number;\n balconies?: number;\n furnishingStatus?: string;\n ageOfProperty?: number;\n facing?: string;\n amenities?: { name: string; category: string; available?: boolean }[];\n images?: { url: string; title?: string; type?: string; order?: number; isPrimary?: boolean }[];\n imageUrl?: string;\n}\n\nexport type UpdatePropertyInput = Partial<CreatePropertyInput>;\n\nexport const useCreateProperty = () => {\n const [mutate, { loading }] = useMutation<{ createHousingvistaProperty: PropertyRow }, { input: CreatePropertyInput }>(\n CREATE_PROPERTY,\n { refetchQueries: [{ query: GET_PROPERTIES, variables: getPaginationVariables() }] }\n );\n const createProperty = async (input: CreatePropertyInput): Promise<Property> => {\n const res = await mutate({ variables: { input } });\n if (!res.data) throw new Error('Property was not created');\n return mapProperty(res.data.createHousingvistaProperty);\n };\n return { createProperty, loading };\n};\n\nexport const useUpdateProperty = () => {\n const [mutate, { loading }] = useMutation<\n { updateHousingvistaProperty: PropertyRow },\n { id: string; input: UpdatePropertyInput }\n >(UPDATE_PROPERTY);\n const updateProperty = async (id: string, input: UpdatePropertyInput): Promise<Property> => {\n const res = await mutate({ variables: { id, input } });\n if (!res.data) throw new Error('Property was not updated');\n return mapProperty(res.data.updateHousingvistaProperty);\n };\n return { updateProperty, loading };\n};\n\nexport const useDeleteProperty = () => {\n const [mutate, { loading }] = useMutation<{ deleteHousingvistaProperty: boolean }, { id: string }>(DELETE_PROPERTY, {\n refetchQueries: [{ query: GET_PROPERTIES, variables: getPaginationVariables() }],\n });\n const deleteProperty = async (id: string): Promise<boolean> => {\n const res = await mutate({ variables: { id } });\n return res.data?.deleteHousingvistaProperty ?? false;\n };\n return { deleteProperty, loading };\n};\n\nexport const useSaveShortlist = () => {\n const [mutate, { loading }] = useMutation<\n { saveHousingvistaShortlist: ShortlistRow },\n { input: { propertyId: string; notes?: string } }\n >(SAVE_SHORTLIST, { refetchQueries: [{ query: GET_SHORTLISTS, variables: getPaginationVariables() }] });\n const saveShortlist = async (propertyId: string, notes?: string) => {\n await mutate({ variables: { input: { propertyId, notes } } });\n };\n return { saveShortlist, loading };\n};\n\nexport const useRemoveShortlist = () => {\n const [mutate, { loading }] = useMutation<{ removeHousingvistaShortlist: boolean }, { propertyId: string }>(\n REMOVE_SHORTLIST,\n { refetchQueries: [{ query: GET_SHORTLISTS, variables: getPaginationVariables() }] }\n );\n const removeShortlist = async (propertyId: string) => {\n await mutate({ variables: { propertyId } });\n };\n return { removeShortlist, loading };\n};\n\nexport interface BookViewingInput {\n propertyId: string;\n scheduledAt: string;\n type?: 'in_person' | 'virtual';\n attendeeName?: string;\n attendeeEmail?: string;\n notes?: string;\n}\n\nexport const useBookViewing = () => {\n const [mutate, { loading }] = useMutation<{ bookHousingvistaViewing: { id: string } }, { input: BookViewingInput }>(\n BOOK_VIEWING\n );\n const bookViewing = async (input: BookViewingInput) => {\n await mutate({ variables: { input } });\n };\n return { bookViewing, loading };\n};\n\nexport const useCancelViewing = () => {\n const [mutate, { loading }] = useMutation<{ cancelHousingvistaViewing: { id: string } }, { id: string }>(\n CANCEL_VIEWING\n );\n const cancelViewing = async (id: string) => {\n await mutate({ variables: { id } });\n };\n return { cancelViewing, loading };\n};\n\nexport interface SubmitOfferInput {\n propertyId: string;\n amount: number;\n currency?: string;\n notes?: string;\n}\n\nexport const useSubmitOffer = () => {\n const [mutate, { loading }] = useMutation<{ submitHousingvistaOffer: { id: string } }, { input: SubmitOfferInput }>(\n SUBMIT_OFFER\n );\n const submitOffer = async (input: SubmitOfferInput) => {\n await mutate({ variables: { input } });\n };\n return { submitOffer, loading };\n};\n\nexport const useRespondOffer = () => {\n const [mutate, { loading }] = useMutation<\n { respondHousingvistaOffer: { id: string } },\n { id: string; input: { action: 'accept' | 'reject' | 'counter'; counterAmount?: number; notes?: string } }\n >(RESPOND_OFFER);\n const respondOffer = async (\n id: string,\n input: { action: 'accept' | 'reject' | 'counter'; counterAmount?: number; notes?: string }\n ) => {\n await mutate({ variables: { id, input } });\n };\n return { respondOffer, loading };\n};\n\nexport interface CreateTransactionInput {\n propertyId: string;\n title: string;\n type: string;\n status: string;\n amount: number;\n currency?: string;\n}\n\nexport const useCreateTransaction = () => {\n const [mutate, { loading }] = useMutation<\n { createHousingvistaTransaction: TransactionRow },\n { input: CreateTransactionInput }\n >(CREATE_TRANSACTION, { refetchQueries: [{ query: GET_TRANSACTIONS, variables: getPaginationVariables() }] });\n const createTransaction = async (input: CreateTransactionInput) => {\n await mutate({ variables: { input } });\n };\n return { createTransaction, loading };\n};\n\nexport interface CreateInvestmentInput {\n propertyId: string;\n title: string;\n status: string;\n investmentType: string;\n riskLevel: string;\n totalValue: number;\n startDate?: string;\n maturityDate?: string;\n}\n\nexport const useCreateInvestment = () => {\n const [mutate, { loading }] = useMutation<\n { createHousingvistaInvestment: InvestmentRow },\n { input: CreateInvestmentInput }\n >(CREATE_INVESTMENT, { refetchQueries: [{ query: GET_INVESTMENTS, variables: getPaginationVariables() }] });\n const createInvestment = async (input: CreateInvestmentInput) => {\n await mutate({ variables: { input } });\n };\n return { createInvestment, loading };\n};\n\nexport interface CreateRentalUnitInput {\n propertyId?: string;\n propertyTitle: string;\n unitNumber: string;\n tenantName: string;\n tenantEmail: string;\n tenantPhone?: string;\n monthlyRent: number;\n leaseStart: string;\n leaseEnd: string;\n status: string;\n paymentStatus: string;\n securityDeposit?: number;\n}\n\nexport const useCreateRentalUnit = () => {\n const [mutate, { loading }] = useMutation<\n { createHousingvistaRentalUnit: { id: string } },\n { input: CreateRentalUnitInput }\n >(CREATE_RENTAL_UNIT, { refetchQueries: [{ query: GET_RENTAL_UNITS, variables: getPaginationVariables() }] });\n const createRentalUnit = async (input: CreateRentalUnitInput) => {\n await mutate({ variables: { input } });\n };\n return { createRentalUnit, loading };\n};\n\nexport interface SubmitRentalApplicationInput {\n rentalUnitId: string;\n applicantName: string;\n applicantEmail: string;\n applicantPhone?: string;\n monthlyIncome?: number;\n moveInDate?: string;\n}\n\nexport const useSubmitRentalApplication = () => {\n const [mutate, { loading }] = useMutation<\n { submitHousingvistaRentalApplication: { id: string } },\n { input: SubmitRentalApplicationInput }\n >(SUBMIT_RENTAL_APPLICATION);\n const submitRentalApplication = async (input: SubmitRentalApplicationInput) => {\n await mutate({ variables: { input } });\n };\n return { submitRentalApplication, loading };\n};\n\nexport interface CreateAmcContractInput {\n propertyId?: string;\n propertyTitle: string;\n packageName: string;\n providerName: string;\n monthlyCost: number;\n startDate: string;\n endDate: string;\n nextServiceDate?: string;\n status: string;\n servicesIncluded?: string[];\n}\n\nexport const useCreateAmcContract = () => {\n const [mutate, { loading }] = useMutation<\n { createHousingvistaAmcContract: { id: string } },\n { input: CreateAmcContractInput }\n >(CREATE_AMC_CONTRACT, { refetchQueries: [{ query: GET_AMC_CONTRACTS, variables: getPaginationVariables() }] });\n const createAmcContract = async (input: CreateAmcContractInput) => {\n await mutate({ variables: { input } });\n };\n return { createAmcContract, loading };\n};\n\nexport interface CreateMaintenanceRequestInput {\n propertyId?: string;\n rentalUnitId?: string;\n title: string;\n description?: string;\n category: string;\n priority?: 'low' | 'medium' | 'high';\n}\n\nexport const useCreateMaintenanceRequest = () => {\n const [mutate, { loading }] = useMutation<\n { createHousingvistaMaintenanceRequest: { id: string } },\n { input: CreateMaintenanceRequestInput }\n >(CREATE_MAINTENANCE_REQUEST);\n const createMaintenanceRequest = async (input: CreateMaintenanceRequestInput) => {\n await mutate({ variables: { input } });\n };\n return { createMaintenanceRequest, loading };\n};\n\nexport const useApproveVerification = () => {\n const [mutate, { loading }] = useMutation<\n { approveHousingvistaVerificationItem: { id: string } },\n { id: string; notes?: string }\n >(APPROVE_VERIFICATION, { refetchQueries: [{ query: GET_VERIFICATIONS, variables: getPaginationVariables() }] });\n const approveVerification = async (id: string, notes?: string) => {\n await mutate({ variables: { id, notes } });\n };\n return { approveVerification, loading };\n};\n\nexport const useRejectVerification = () => {\n const [mutate, { loading }] = useMutation<\n { rejectHousingvistaVerificationItem: { id: string } },\n { id: string; notes?: string }\n >(REJECT_VERIFICATION, { refetchQueries: [{ query: GET_VERIFICATIONS, variables: getPaginationVariables() }] });\n const rejectVerification = async (id: string, notes?: string) => {\n await mutate({ variables: { id, notes } });\n };\n return { rejectVerification, loading };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/housingvista/hooks/index.ts"],"sourcesContent":["/**\n * HousingVista data hooks — real GraphQL against the wspace gateway via Apollo.\n *\n * Every read maps the `housingvista*` query contract to the MFE domain types in\n * `../types`. Every write wraps a `housingvista*` mutation and (where it changes\n * a list the UI shows) refetches the affected query so the cache stays honest.\n * No `any`, no mock data — hooks that have no backing query return an explicit\n * `unsupported` empty-state instead of fabricating rows.\n * @module housingvista\n */\n\nimport { gql } from '@apollo/client';\nimport { useQuery, useMutation } from '@apollo/client/react';\nimport type {\n Property,\n Transaction,\n Investment,\n DashboardStats,\n RecentActivity,\n RentalUnit,\n Asset,\n AMCContract,\n MarketplaceService,\n ConversationListItem,\n Notification,\n VerificationItem,\n LegalService,\n HomeLoan,\n MarketTrend,\n AreaInsight,\n AnalyticsChartData,\n PropertyType,\n PropertyStatus,\n} from '../types';\n\n// ---------------------------------------------------------------------------\n// Shared input shapes (mirror HousingVista*Input from the subgraph schema)\n// ---------------------------------------------------------------------------\n\nexport type PaginationInput = {\n limit?: number;\n offset?: number;\n};\n\nexport type PropertyFilterInput = {\n search?: string;\n status?: PropertyStatus;\n type?: PropertyType;\n city?: string;\n};\n\nconst DEFAULT_LIST_PAGINATION: Required<PaginationInput> = { limit: 50, offset: 0 };\n\nconst getPaginationVariables = (pagination: PaginationInput = DEFAULT_LIST_PAGINATION) => ({\n pagination: {\n limit: pagination.limit ?? DEFAULT_LIST_PAGINATION.limit,\n offset: pagination.offset ?? DEFAULT_LIST_PAGINATION.offset,\n },\n});\n\n// ---------------------------------------------------------------------------\n// Query documents\n// ---------------------------------------------------------------------------\n\nconst GET_DASHBOARD_STATS = gql`\n query GetDashboardStats {\n housingvistaDashboardStats {\n totalProperties\n totalValue\n occupancyRate\n monthlyRevenue\n activeTransactions\n pendingVerifications\n totalInvestments\n totalPortfolioValue\n investmentReturns\n totalTenants\n maintenanceRequests\n unreadMessages\n upcomingViewings\n }\n }\n`;\n\nconst PROPERTY_FIELDS = gql`\n fragment PropertyFields on HousingVistaProperty {\n id\n title\n description\n type\n status\n category\n verificationStatus\n imageUrl\n createdAt\n updatedAt\n price {\n amount\n currency\n type\n period\n negotiable\n pricePerSqft\n }\n location {\n address\n city\n state\n country\n pincode\n coordinates {\n latitude\n longitude\n }\n neighborhood\n landmarks\n }\n specifications {\n totalArea\n carpetArea\n builtUpArea\n bedrooms\n bathrooms\n floors\n totalFloors\n parking\n balconies\n furnishingStatus\n ageOfProperty\n facing\n }\n amenities {\n id\n name\n category\n available\n }\n images {\n id\n url\n title\n type\n order\n isPrimary\n }\n }\n`;\n\nconst GET_PROPERTIES = gql`\n ${PROPERTY_FIELDS}\n query GetProperties($filter: HousingVistaPropertyFilterInput, $pagination: HousingVistaPaginationInput) {\n housingvistaProperties(filter: $filter, pagination: $pagination) {\n ...PropertyFields\n }\n }\n`;\n\nconst GET_PROPERTY = gql`\n ${PROPERTY_FIELDS}\n query GetProperty($id: ID!) {\n housingvistaProperty(id: $id) {\n ...PropertyFields\n }\n }\n`;\n\nconst TRANSACTION_FIELDS = gql`\n fragment TransactionFields on HousingVistaTransaction {\n id\n title\n type\n status\n amount\n currency\n documents\n milestones\n parties\n escrow\n createdAt\n updatedAt\n property {\n id\n title\n location {\n city\n state\n }\n images {\n id\n url\n isPrimary\n }\n }\n buyer {\n id\n firstName\n lastName\n avatar\n }\n seller {\n id\n firstName\n lastName\n avatar\n }\n tenant {\n id\n firstName\n lastName\n avatar\n }\n landlord {\n id\n firstName\n lastName\n avatar\n }\n }\n`;\n\nconst GET_TRANSACTIONS = gql`\n ${TRANSACTION_FIELDS}\n query GetTransactions($pagination: HousingVistaPaginationInput) {\n housingvistaTransactions(pagination: $pagination) {\n ...TransactionFields\n }\n }\n`;\n\nconst GET_TRANSACTION = gql`\n ${TRANSACTION_FIELDS}\n query GetTransaction($id: ID!) {\n housingvistaTransaction(id: $id) {\n ...TransactionFields\n }\n }\n`;\n\nconst INVESTMENT_FIELDS = gql`\n fragment InvestmentFields on HousingVistaInvestment {\n id\n title\n investmentType\n totalValue\n status\n startDate\n maturityDate\n riskLevel\n investors\n returns\n property {\n id\n title\n location {\n city\n state\n }\n }\n }\n`;\n\nconst GET_INVESTMENTS = gql`\n ${INVESTMENT_FIELDS}\n query GetInvestments($pagination: HousingVistaPaginationInput) {\n housingvistaInvestments(pagination: $pagination) {\n ...InvestmentFields\n }\n }\n`;\n\nconst GET_INVESTMENT = gql`\n ${INVESTMENT_FIELDS}\n query GetInvestment($id: ID!) {\n housingvistaInvestment(id: $id) {\n ...InvestmentFields\n }\n }\n`;\n\nconst GET_RENTAL_UNITS = gql`\n query GetRentalUnits($pagination: HousingVistaPaginationInput) {\n housingvistaRentalUnits(pagination: $pagination) {\n id\n propertyId\n propertyTitle\n unitNumber\n tenantName\n tenantEmail\n tenantPhone\n monthlyRent\n leaseStart\n leaseEnd\n status\n lastPaymentDate\n paymentStatus\n securityDeposit\n }\n }\n`;\n\nconst GET_ASSETS = gql`\n query GetAssets($pagination: HousingVistaPaginationInput) {\n housingvistaAssets(pagination: $pagination) {\n id\n title\n type\n category\n location\n currentValue\n purchasePrice\n purchaseDate\n appreciationPercent\n rentalYield\n status\n imageUrl\n lastValuationDate\n }\n }\n`;\n\nconst GET_AMC_CONTRACTS = gql`\n query GetAmcContracts($pagination: HousingVistaPaginationInput) {\n housingvistaAmcContracts(pagination: $pagination) {\n id\n propertyId\n propertyTitle\n packageName\n providerName\n status\n startDate\n endDate\n monthlyCost\n nextServiceDate\n servicesIncluded\n }\n }\n`;\n\nconst GET_MARKETPLACE_SERVICES = gql`\n query GetMarketplaceServices($pagination: HousingVistaPaginationInput) {\n housingvistaMarketplaceServices(pagination: $pagination) {\n id\n name\n providerName\n category\n description\n rating\n reviewCount\n priceRange\n availableAreas\n responseTime\n verified\n imageUrl\n featured\n }\n }\n`;\n\nconst GET_VERIFICATIONS = gql`\n query GetVerifications($pagination: HousingVistaPaginationInput) {\n housingvistaVerificationItems(pagination: $pagination) {\n id\n entityType\n entityId\n entityLabel\n status\n submittedDate\n reviewerName\n priority\n notes\n }\n }\n`;\n\nconst GET_LEGAL_SERVICES = gql`\n query GetLegalServices($pagination: HousingVistaPaginationInput) {\n housingvistaLegalServices(pagination: $pagination) {\n id\n title\n category\n description\n providerName\n providerRating\n estimatedDuration\n priceRange\n status\n consultationFee\n documentsRequired\n }\n }\n`;\n\nconst GET_HOME_LOANS = gql`\n query GetHomeLoans($pagination: HousingVistaPaginationInput) {\n housingvistaHomeLoanOffers(pagination: $pagination) {\n id\n bankName\n bankLogo\n interestRate\n maxTenure\n maxAmount\n processingFee\n emiPerLakh\n features\n rating\n specialOffer\n prepaymentCharges\n }\n }\n`;\n\nconst GET_MARKET_TRENDS = gql`\n query GetMarketTrends($pagination: HousingVistaPaginationInput) {\n housingvistaMarketTrends(pagination: $pagination) {\n month\n avgPrice\n avgRent\n demandIndex\n supplyIndex\n priceChangePercent\n }\n }\n`;\n\nconst GET_AREA_INSIGHTS = gql`\n query GetAreaInsights($pagination: HousingVistaPaginationInput) {\n housingvistaAreaInsights(pagination: $pagination) {\n id\n areaName\n city\n avgPropertyPrice\n avgRentalYield\n appreciationRate\n safetyScore\n connectivityScore\n amenitiesScore\n schoolsNearby\n hospitalsNearby\n population\n greenCoverPercent\n upcomingProjects\n }\n }\n`;\n\nconst GET_ANALYTICS = gql`\n query GetHousingvistaAnalytics {\n housingvistaAnalytics {\n kpis {\n id\n label\n value\n previousValue\n unit\n trend\n changePercent\n }\n revenueByMonth {\n month\n revenue\n expenses\n }\n propertyTypeDistribution {\n type\n count\n value\n }\n occupancyRate {\n month\n rate\n }\n }\n }\n`;\n\nconst SHORTLIST_FIELDS = gql`\n ${PROPERTY_FIELDS}\n fragment ShortlistFields on HousingVistaShortlist {\n id\n propertyId\n notes\n createdAt\n property {\n ...PropertyFields\n }\n }\n`;\n\nconst GET_SHORTLISTS = gql`\n ${SHORTLIST_FIELDS}\n query GetShortlists($pagination: HousingVistaPaginationInput) {\n housingvistaShortlists(pagination: $pagination) {\n ...ShortlistFields\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Mutation documents\n// ---------------------------------------------------------------------------\n\nconst CREATE_PROPERTY = gql`\n ${PROPERTY_FIELDS}\n mutation CreateHousingvistaProperty($input: HousingVistaCreatePropertyInput!) {\n createHousingvistaProperty(input: $input) {\n ...PropertyFields\n }\n }\n`;\n\nconst UPDATE_PROPERTY = gql`\n ${PROPERTY_FIELDS}\n mutation UpdateHousingvistaProperty($id: ID!, $input: HousingVistaUpdatePropertyInput!) {\n updateHousingvistaProperty(id: $id, input: $input) {\n ...PropertyFields\n }\n }\n`;\n\nconst DELETE_PROPERTY = gql`\n mutation DeleteHousingvistaProperty($id: ID!) {\n deleteHousingvistaProperty(id: $id)\n }\n`;\n\nconst SAVE_SHORTLIST = gql`\n ${SHORTLIST_FIELDS}\n mutation SaveHousingvistaShortlist($input: HousingVistaSaveShortlistInput!) {\n saveHousingvistaShortlist(input: $input) {\n ...ShortlistFields\n }\n }\n`;\n\nconst REMOVE_SHORTLIST = gql`\n mutation RemoveHousingvistaShortlist($propertyId: ID!) {\n removeHousingvistaShortlist(propertyId: $propertyId)\n }\n`;\n\nconst BOOK_VIEWING = gql`\n mutation BookHousingvistaViewing($input: HousingVistaBookViewingInput!) {\n bookHousingvistaViewing(input: $input) {\n id\n propertyId\n scheduledAt\n type\n status\n attendeeName\n attendeeEmail\n notes\n }\n }\n`;\n\nconst CANCEL_VIEWING = gql`\n mutation CancelHousingvistaViewing($id: ID!) {\n cancelHousingvistaViewing(id: $id) {\n id\n status\n }\n }\n`;\n\nconst SUBMIT_OFFER = gql`\n mutation SubmitHousingvistaOffer($input: HousingVistaSubmitOfferInput!) {\n submitHousingvistaOffer(input: $input) {\n id\n propertyId\n amount\n currency\n status\n notes\n createdAt\n }\n }\n`;\n\nconst RESPOND_OFFER = gql`\n mutation RespondHousingvistaOffer($id: ID!, $input: HousingVistaRespondOfferInput!) {\n respondHousingvistaOffer(id: $id, input: $input) {\n id\n status\n counterAmount\n respondedAt\n }\n }\n`;\n\nconst CREATE_TRANSACTION = gql`\n ${TRANSACTION_FIELDS}\n mutation CreateHousingvistaTransaction($input: HousingVistaCreateTransactionInput!) {\n createHousingvistaTransaction(input: $input) {\n ...TransactionFields\n }\n }\n`;\n\nconst CREATE_INVESTMENT = gql`\n ${INVESTMENT_FIELDS}\n mutation CreateHousingvistaInvestment($input: HousingVistaCreateInvestmentInput!) {\n createHousingvistaInvestment(input: $input) {\n ...InvestmentFields\n }\n }\n`;\n\nconst CREATE_RENTAL_UNIT = gql`\n mutation CreateHousingvistaRentalUnit($input: HousingVistaCreateRentalUnitInput!) {\n createHousingvistaRentalUnit(input: $input) {\n id\n propertyTitle\n unitNumber\n tenantName\n monthlyRent\n status\n }\n }\n`;\n\nconst SUBMIT_RENTAL_APPLICATION = gql`\n mutation SubmitHousingvistaRentalApplication($input: HousingVistaSubmitRentalApplicationInput!) {\n submitHousingvistaRentalApplication(input: $input) {\n id\n rentalUnitId\n applicantName\n status\n }\n }\n`;\n\nconst CREATE_AMC_CONTRACT = gql`\n mutation CreateHousingvistaAmcContract($input: HousingVistaCreateAmcContractInput!) {\n createHousingvistaAmcContract(input: $input) {\n id\n propertyTitle\n packageName\n providerName\n monthlyCost\n status\n }\n }\n`;\n\nconst CREATE_MAINTENANCE_REQUEST = gql`\n mutation CreateHousingvistaMaintenanceRequest($input: HousingVistaCreateMaintenanceRequestInput!) {\n createHousingvistaMaintenanceRequest(input: $input) {\n id\n title\n category\n priority\n status\n }\n }\n`;\n\nconst APPROVE_VERIFICATION = gql`\n mutation ApproveHousingvistaVerificationItem($id: ID!, $notes: String) {\n approveHousingvistaVerificationItem(id: $id, notes: $notes) {\n id\n status\n reviewerName\n notes\n }\n }\n`;\n\nconst REJECT_VERIFICATION = gql`\n mutation RejectHousingvistaVerificationItem($id: ID!, $notes: String) {\n rejectHousingvistaVerificationItem(id: $id, notes: $notes) {\n id\n status\n reviewerName\n notes\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Query-result row shapes (as selected above)\n// ---------------------------------------------------------------------------\n\ninterface PropertyRow {\n id: string;\n title: string;\n description: string | null;\n type: Property['type'];\n status: Property['status'];\n category: string;\n verificationStatus: string;\n imageUrl: string | null;\n createdAt: string;\n updatedAt: string;\n price: Property['price'];\n location: Omit<Property['location'], 'coordinates'> & {\n coordinates: { latitude: number; longitude: number } | null;\n };\n specifications: Property['specifications'];\n amenities: Property['amenities'];\n images: Property['images'];\n}\n\ninterface TransactionRow {\n id: string;\n title: string;\n type: string;\n status: string;\n amount: number;\n currency: string;\n documents: unknown;\n milestones: unknown;\n parties: unknown;\n escrow: unknown;\n createdAt: string;\n updatedAt: string;\n property: PropertyRow | null;\n buyer: TxParty | null;\n seller: TxParty | null;\n tenant: TxParty | null;\n landlord: TxParty | null;\n}\n\ninterface TxParty {\n id: string | null;\n firstName: string | null;\n lastName: string | null;\n avatar: string | null;\n}\n\ninterface InvestmentRow {\n id: string;\n title: string;\n investmentType: string;\n totalValue: number;\n status: string;\n startDate: string;\n maturityDate: string | null;\n riskLevel: string;\n investors: unknown;\n returns: unknown;\n property: PropertyRow | null;\n}\n\ninterface ShortlistRow {\n id: string;\n propertyId: string;\n notes: string | null;\n createdAt: string;\n property: PropertyRow;\n}\n\n// ---------------------------------------------------------------------------\n// Mappers: GraphQL row → MFE domain type\n// ---------------------------------------------------------------------------\n\n/**\n * The domain `Property` carries display-only fields the subgraph does not\n * resolve (owner/documents/features/virtualTours/availability). We fill the\n * present fields from the query and default the rest to empty so consumers can\n * render honest empty-states rather than crashing on `undefined`.\n */\nconst mapProperty = (row: PropertyRow): Property => ({\n id: row.id,\n title: row.title,\n description: row.description ?? '',\n type: row.type,\n status: row.status,\n category: row.category as Property['category'],\n price: row.price,\n location: {\n ...row.location,\n coordinates: row.location.coordinates ?? { latitude: 0, longitude: 0 },\n },\n specifications: row.specifications,\n amenities: row.amenities ?? [],\n images: row.images ?? [],\n documents: [],\n virtualTours: [],\n owner: { id: '', name: '', email: '', phone: '', verified: false },\n features: [],\n verificationStatus: (row.verificationStatus as Property['verificationStatus']) ?? 'pending',\n availability: { available: true },\n createdAt: new Date(row.createdAt),\n updatedAt: new Date(row.updatedAt),\n});\n\n// ---------------------------------------------------------------------------\n// Query hooks\n// ---------------------------------------------------------------------------\n\nexport const useDashboardStats = () => {\n const { data, loading, error } = useQuery<{ housingvistaDashboardStats: DashboardStats }>(GET_DASHBOARD_STATS);\n const empty: DashboardStats = {\n totalProperties: 0,\n totalValue: 0,\n occupancyRate: 0,\n monthlyRevenue: 0,\n activeTransactions: 0,\n pendingVerifications: 0,\n totalInvestments: 0,\n investmentReturns: 0,\n totalTenants: 0,\n maintenanceRequests: 0,\n unreadMessages: 0,\n upcomingViewings: 0,\n };\n return { data: data?.housingvistaDashboardStats ?? empty, loading, error };\n};\n\nexport const useRecentActivities = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaTransactions: TransactionRow[] }>(GET_TRANSACTIONS, {\n variables: getPaginationVariables(pagination),\n });\n const activities: RecentActivity[] = (data?.housingvistaTransactions ?? []).map((txn) => ({\n id: `act-${txn.id}`,\n type: 'transaction_update',\n title: `Transaction: ${txn.type}`,\n description: `${txn.property?.title ?? 'Property'} - ${txn.status}`,\n timestamp: txn.updatedAt || txn.createdAt,\n entityId: txn.id,\n entityType: 'transaction',\n actorName: txn.buyer\n ? `${txn.buyer.firstName ?? ''} ${txn.buyer.lastName ?? ''}`.trim()\n : txn.tenant\n ? `${txn.tenant.firstName ?? ''} ${txn.tenant.lastName ?? ''}`.trim()\n : 'System',\n actorAvatar: txn.buyer?.avatar ?? txn.tenant?.avatar ?? undefined,\n }));\n return { data: activities, loading, error };\n};\n\nexport const useProperties = (options?: { filter?: PropertyFilterInput; pagination?: PaginationInput }) => {\n const { data, loading, error, refetch } = useQuery<{ housingvistaProperties: PropertyRow[] }>(GET_PROPERTIES, {\n variables: {\n filter: options?.filter,\n ...getPaginationVariables(options?.pagination),\n },\n });\n return {\n data: (data?.housingvistaProperties ?? []).map(mapProperty),\n loading,\n error,\n refetch,\n };\n};\n\nexport const useProperty = (id: string | undefined) => {\n const { data, loading, error } = useQuery<{ housingvistaProperty: PropertyRow | null }>(GET_PROPERTY, {\n variables: { id: id ?? '' },\n skip: !id,\n });\n return {\n data: data?.housingvistaProperty ? mapProperty(data.housingvistaProperty) : null,\n loading,\n error,\n };\n};\n\nexport const useTransactions = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaTransactions: TransactionRow[] }>(GET_TRANSACTIONS, {\n variables: getPaginationVariables(pagination),\n });\n // Transaction JSON columns (documents/milestones/parties/escrow) are opaque to\n // the schema; the page guards their runtime shape. Cast is localized here.\n return { data: (data?.housingvistaTransactions ?? []) as unknown as Transaction[], loading, error };\n};\n\nexport const useTransaction = (id: string | undefined) => {\n const { data, loading, error } = useQuery<{ housingvistaTransaction: TransactionRow | null }>(GET_TRANSACTION, {\n variables: { id: id ?? '' },\n skip: !id,\n });\n return {\n data: (data?.housingvistaTransaction ?? null) as unknown as Transaction | null,\n loading,\n error,\n };\n};\n\nexport const useInvestments = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaInvestments: InvestmentRow[] }>(GET_INVESTMENTS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: (data?.housingvistaInvestments ?? []) as unknown as Investment[], loading, error };\n};\n\nexport const useInvestment = (id: string | undefined) => {\n const { data, loading, error } = useQuery<{ housingvistaInvestment: InvestmentRow | null }>(GET_INVESTMENT, {\n variables: { id: id ?? '' },\n skip: !id,\n });\n return { data: (data?.housingvistaInvestment ?? null) as unknown as Investment | null, loading, error };\n};\n\nexport const useRentalUnits = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaRentalUnits: RentalUnit[] }>(GET_RENTAL_UNITS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaRentalUnits ?? [], loading, error };\n};\n\nexport const useAssets = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaAssets: Asset[] }>(GET_ASSETS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaAssets ?? [], loading, error };\n};\n\nexport const useAMCContracts = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaAmcContracts: AMCContract[] }>(GET_AMC_CONTRACTS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaAmcContracts ?? [], loading, error };\n};\n\nexport const useMarketplaceServices = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaMarketplaceServices: MarketplaceService[] }>(\n GET_MARKETPLACE_SERVICES,\n { variables: getPaginationVariables(pagination) }\n );\n return { data: data?.housingvistaMarketplaceServices ?? [], loading, error };\n};\n\nexport const useVerifications = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaVerificationItems: VerificationItem[] }>(GET_VERIFICATIONS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaVerificationItems ?? [], loading, error };\n};\n\nexport const useLegalServices = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaLegalServices: LegalService[] }>(GET_LEGAL_SERVICES, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaLegalServices ?? [], loading, error };\n};\n\nexport const useHomeLoans = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaHomeLoanOffers: HomeLoan[] }>(GET_HOME_LOANS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaHomeLoanOffers ?? [], loading, error };\n};\n\nexport const useMarketTrends = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaMarketTrends: MarketTrend[] }>(GET_MARKET_TRENDS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaMarketTrends ?? [], loading, error };\n};\n\nexport const useAreaInsights = (pagination?: PaginationInput) => {\n const { data, loading, error } = useQuery<{ housingvistaAreaInsights: AreaInsight[] }>(GET_AREA_INSIGHTS, {\n variables: getPaginationVariables(pagination),\n });\n return { data: data?.housingvistaAreaInsights ?? [], loading, error };\n};\n\nexport const useShortlists = (pagination?: PaginationInput) => {\n const { data, loading, error, refetch } = useQuery<{ housingvistaShortlists: ShortlistRow[] }>(GET_SHORTLISTS, {\n variables: getPaginationVariables(pagination),\n });\n const shortlists = (data?.housingvistaShortlists ?? []).map((row) => ({\n id: row.id,\n propertyId: row.propertyId,\n notes: row.notes ?? undefined,\n createdAt: row.createdAt,\n property: mapProperty(row.property),\n }));\n return { data: shortlists, loading, error, refetch };\n};\n\nexport const useAnalyticsData = () => {\n const { data, loading, error } = useQuery<{ housingvistaAnalytics: AnalyticsChartData }>(GET_ANALYTICS);\n return { data: data?.housingvistaAnalytics ?? null, loading, error };\n};\n\n/**\n * Conversations are owned by the platform conversations module, which the MFE\n * shell does not currently expose to housingvista. Return an explicit empty\n * state (never mock rows) so the page renders an honest \"coming soon\".\n */\nexport const useConversations = () => {\n return { data: [] as ConversationListItem[], loading: false, error: null, unsupported: true };\n};\n\n/**\n * Notifications are surfaced by the shell's global notification centre; the MFE\n * has no dedicated feed query, so return an honest empty state.\n */\nexport const useNotifications = () => {\n return { data: [] as Notification[], loading: false, error: null, unsupported: true };\n};\n\n// ---------------------------------------------------------------------------\n// Mutation hooks\n// ---------------------------------------------------------------------------\n\nexport interface CreatePropertyInput {\n title: string;\n description?: string;\n status: PropertyStatus;\n type: PropertyType;\n category: string;\n address?: string;\n city: string;\n state: string;\n country?: string;\n pincode?: string;\n neighborhood?: string;\n landmarks?: string[];\n price: number;\n currency?: string;\n priceType?: string;\n pricePeriod?: string;\n negotiable?: boolean;\n pricePerSqft?: number;\n bedrooms: number;\n bathrooms: number;\n totalArea: number;\n carpetArea?: number;\n builtUpArea?: number;\n floors?: number;\n totalFloors?: number;\n parking?: number;\n balconies?: number;\n furnishingStatus?: string;\n ageOfProperty?: number;\n facing?: string;\n amenities?: { name: string; category: string; available?: boolean }[];\n images?: { url: string; title?: string; type?: string; order?: number; isPrimary?: boolean }[];\n imageUrl?: string;\n}\n\nexport type UpdatePropertyInput = Partial<CreatePropertyInput>;\n\nexport const useCreateProperty = () => {\n const [mutate, { loading }] = useMutation<\n { createHousingvistaProperty: PropertyRow },\n { input: CreatePropertyInput }\n >(CREATE_PROPERTY, { refetchQueries: [{ query: GET_PROPERTIES, variables: getPaginationVariables() }] });\n const createProperty = async (input: CreatePropertyInput): Promise<Property> => {\n const res = await mutate({ variables: { input } });\n if (!res.data) throw new Error('Property was not created');\n return mapProperty(res.data.createHousingvistaProperty);\n };\n return { createProperty, loading };\n};\n\nexport const useUpdateProperty = () => {\n const [mutate, { loading }] = useMutation<\n { updateHousingvistaProperty: PropertyRow },\n { id: string; input: UpdatePropertyInput }\n >(UPDATE_PROPERTY);\n const updateProperty = async (id: string, input: UpdatePropertyInput): Promise<Property> => {\n const res = await mutate({ variables: { id, input } });\n if (!res.data) throw new Error('Property was not updated');\n return mapProperty(res.data.updateHousingvistaProperty);\n };\n return { updateProperty, loading };\n};\n\nexport const useDeleteProperty = () => {\n const [mutate, { loading }] = useMutation<{ deleteHousingvistaProperty: boolean }, { id: string }>(DELETE_PROPERTY, {\n refetchQueries: [{ query: GET_PROPERTIES, variables: getPaginationVariables() }],\n });\n const deleteProperty = async (id: string): Promise<boolean> => {\n const res = await mutate({ variables: { id } });\n return res.data?.deleteHousingvistaProperty ?? false;\n };\n return { deleteProperty, loading };\n};\n\nexport const useSaveShortlist = () => {\n const [mutate, { loading }] = useMutation<\n { saveHousingvistaShortlist: ShortlistRow },\n { input: { propertyId: string; notes?: string } }\n >(SAVE_SHORTLIST, { refetchQueries: [{ query: GET_SHORTLISTS, variables: getPaginationVariables() }] });\n const saveShortlist = async (propertyId: string, notes?: string) => {\n await mutate({ variables: { input: { propertyId, notes } } });\n };\n return { saveShortlist, loading };\n};\n\nexport const useRemoveShortlist = () => {\n const [mutate, { loading }] = useMutation<{ removeHousingvistaShortlist: boolean }, { propertyId: string }>(\n REMOVE_SHORTLIST,\n { refetchQueries: [{ query: GET_SHORTLISTS, variables: getPaginationVariables() }] }\n );\n const removeShortlist = async (propertyId: string) => {\n await mutate({ variables: { propertyId } });\n };\n return { removeShortlist, loading };\n};\n\nexport interface BookViewingInput {\n propertyId: string;\n scheduledAt: string;\n type?: 'in_person' | 'virtual';\n attendeeName?: string;\n attendeeEmail?: string;\n notes?: string;\n}\n\nexport const useBookViewing = () => {\n const [mutate, { loading }] = useMutation<{ bookHousingvistaViewing: { id: string } }, { input: BookViewingInput }>(\n BOOK_VIEWING\n );\n const bookViewing = async (input: BookViewingInput) => {\n await mutate({ variables: { input } });\n };\n return { bookViewing, loading };\n};\n\nexport const useCancelViewing = () => {\n const [mutate, { loading }] = useMutation<{ cancelHousingvistaViewing: { id: string } }, { id: string }>(\n CANCEL_VIEWING\n );\n const cancelViewing = async (id: string) => {\n await mutate({ variables: { id } });\n };\n return { cancelViewing, loading };\n};\n\nexport interface SubmitOfferInput {\n propertyId: string;\n amount: number;\n currency?: string;\n notes?: string;\n}\n\nexport const useSubmitOffer = () => {\n const [mutate, { loading }] = useMutation<{ submitHousingvistaOffer: { id: string } }, { input: SubmitOfferInput }>(\n SUBMIT_OFFER\n );\n const submitOffer = async (input: SubmitOfferInput) => {\n await mutate({ variables: { input } });\n };\n return { submitOffer, loading };\n};\n\nexport const useRespondOffer = () => {\n const [mutate, { loading }] = useMutation<\n { respondHousingvistaOffer: { id: string } },\n { id: string; input: { action: 'accept' | 'reject' | 'counter'; counterAmount?: number; notes?: string } }\n >(RESPOND_OFFER);\n const respondOffer = async (\n id: string,\n input: { action: 'accept' | 'reject' | 'counter'; counterAmount?: number; notes?: string }\n ) => {\n await mutate({ variables: { id, input } });\n };\n return { respondOffer, loading };\n};\n\nexport interface CreateTransactionInput {\n propertyId: string;\n title: string;\n type: string;\n status: string;\n amount: number;\n currency?: string;\n}\n\nexport const useCreateTransaction = () => {\n const [mutate, { loading }] = useMutation<\n { createHousingvistaTransaction: TransactionRow },\n { input: CreateTransactionInput }\n >(CREATE_TRANSACTION, { refetchQueries: [{ query: GET_TRANSACTIONS, variables: getPaginationVariables() }] });\n const createTransaction = async (input: CreateTransactionInput) => {\n await mutate({ variables: { input } });\n };\n return { createTransaction, loading };\n};\n\nexport interface CreateInvestmentInput {\n propertyId: string;\n title: string;\n status: string;\n investmentType: string;\n riskLevel: string;\n totalValue: number;\n startDate?: string;\n maturityDate?: string;\n}\n\nexport const useCreateInvestment = () => {\n const [mutate, { loading }] = useMutation<\n { createHousingvistaInvestment: InvestmentRow },\n { input: CreateInvestmentInput }\n >(CREATE_INVESTMENT, { refetchQueries: [{ query: GET_INVESTMENTS, variables: getPaginationVariables() }] });\n const createInvestment = async (input: CreateInvestmentInput) => {\n await mutate({ variables: { input } });\n };\n return { createInvestment, loading };\n};\n\nexport interface CreateRentalUnitInput {\n propertyId?: string;\n propertyTitle: string;\n unitNumber: string;\n tenantName: string;\n tenantEmail: string;\n tenantPhone?: string;\n monthlyRent: number;\n leaseStart: string;\n leaseEnd: string;\n status: string;\n paymentStatus: string;\n securityDeposit?: number;\n}\n\nexport const useCreateRentalUnit = () => {\n const [mutate, { loading }] = useMutation<\n { createHousingvistaRentalUnit: { id: string } },\n { input: CreateRentalUnitInput }\n >(CREATE_RENTAL_UNIT, { refetchQueries: [{ query: GET_RENTAL_UNITS, variables: getPaginationVariables() }] });\n const createRentalUnit = async (input: CreateRentalUnitInput) => {\n await mutate({ variables: { input } });\n };\n return { createRentalUnit, loading };\n};\n\nexport interface SubmitRentalApplicationInput {\n rentalUnitId: string;\n applicantName: string;\n applicantEmail: string;\n applicantPhone?: string;\n monthlyIncome?: number;\n moveInDate?: string;\n}\n\nexport const useSubmitRentalApplication = () => {\n const [mutate, { loading }] = useMutation<\n { submitHousingvistaRentalApplication: { id: string } },\n { input: SubmitRentalApplicationInput }\n >(SUBMIT_RENTAL_APPLICATION);\n const submitRentalApplication = async (input: SubmitRentalApplicationInput) => {\n await mutate({ variables: { input } });\n };\n return { submitRentalApplication, loading };\n};\n\nexport interface CreateAmcContractInput {\n propertyId?: string;\n propertyTitle: string;\n packageName: string;\n providerName: string;\n monthlyCost: number;\n startDate: string;\n endDate: string;\n nextServiceDate?: string;\n status: string;\n servicesIncluded?: string[];\n}\n\nexport const useCreateAmcContract = () => {\n const [mutate, { loading }] = useMutation<\n { createHousingvistaAmcContract: { id: string } },\n { input: CreateAmcContractInput }\n >(CREATE_AMC_CONTRACT, { refetchQueries: [{ query: GET_AMC_CONTRACTS, variables: getPaginationVariables() }] });\n const createAmcContract = async (input: CreateAmcContractInput) => {\n await mutate({ variables: { input } });\n };\n return { createAmcContract, loading };\n};\n\nexport interface CreateMaintenanceRequestInput {\n propertyId?: string;\n rentalUnitId?: string;\n title: string;\n description?: string;\n category: string;\n priority?: 'low' | 'medium' | 'high';\n}\n\nexport const useCreateMaintenanceRequest = () => {\n const [mutate, { loading }] = useMutation<\n { createHousingvistaMaintenanceRequest: { id: string } },\n { input: CreateMaintenanceRequestInput }\n >(CREATE_MAINTENANCE_REQUEST);\n const createMaintenanceRequest = async (input: CreateMaintenanceRequestInput) => {\n await mutate({ variables: { input } });\n };\n return { createMaintenanceRequest, loading };\n};\n\nexport const useApproveVerification = () => {\n const [mutate, { loading }] = useMutation<\n { approveHousingvistaVerificationItem: { id: string } },\n { id: string; notes?: string }\n >(APPROVE_VERIFICATION, { refetchQueries: [{ query: GET_VERIFICATIONS, variables: getPaginationVariables() }] });\n const approveVerification = async (id: string, notes?: string) => {\n await mutate({ variables: { id, notes } });\n };\n return { approveVerification, loading };\n};\n\nexport const useRejectVerification = () => {\n const [mutate, { loading }] = useMutation<\n { rejectHousingvistaVerificationItem: { id: string } },\n { id: string; notes?: string }\n >(REJECT_VERIFICATION, { refetchQueries: [{ query: GET_VERIFICATIONS, variables: getPaginationVariables() }] });\n const rejectVerification = async (id: string, notes?: string) => {\n await mutate({ variables: { id, notes } });\n };\n return { rejectVerification, loading };\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HomePage.js","names":[],"sources":["../../../src/housingvista/pages/HomePage.tsx"],"sourcesContent":["/**\n * HousingVista Dashboard / Home Page\n * Migrated from legacy housingvista-frontend dashboard.tsx\n * @module housingvista\n */\n\nimport { useMemo } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n AuroraBackground,\n GradientText,\n Button,\n GlassCard,\n EmphasisPanel,\n IllustratedEmptyState,\n PagePurpose,\n NextSteps,\n} from '@burdenoff/fe-libs/ui';\nimport { Building, TrendingUp, Eye, DollarSign, Plus, BarChart3, FileText, Home, ArrowRight } from 'lucide-react';\nimport { StatCard, PropertyCard } from '../components';\nimport { useDashboardStats, useRecentActivities, useProperties } from '../hooks';\nimport { cn, formatNumber, getRelativeTime } from '../utils/helpers';\nimport type { RecentActivity } from '../types';\n\n// ---- Activity icon mapping ----\nconst activityIconMap: Record<RecentActivity['type'], typeof Eye> = {\n property_listed: Home,\n transaction_update: FileText,\n payment_received: DollarSign,\n maintenance_request: Building,\n viewing_scheduled: Eye,\n document_uploaded: FileText,\n offer_received: TrendingUp,\n lease_signed: FileText,\n};\n\nconst activityColorMap: Record<RecentActivity['type'], string> = {\n property_listed: 'text-accent-purple',\n transaction_update: 'text-status-info-text',\n payment_received: 'text-status-success-text',\n maintenance_request: 'text-status-warning-text',\n viewing_scheduled: 'text-status-info-text',\n document_uploaded: 'text-status-info-text',\n offer_received: 'text-status-success-text',\n lease_signed: 'text-accent-teal',\n};\n\nexport const HomePage = () => {\n const navigate = useNavigate();\n const { t } = useI18n();\n const { data: stats } = useDashboardStats();\n const { data: activities } = useRecentActivities();\n const { data: properties } = useProperties();\n\n const tr = (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n\n const quickActions = [\n {\n title: tr('housingvista.home.quickActions.addProperty.title', 'Add Property'),\n description: tr('housingvista.home.quickActions.addProperty.description', 'List a new property'),\n icon: Plus,\n path: 'properties/new',\n },\n {\n title: tr('housingvista.home.quickActions.viewAnalytics.title', 'View Analytics'),\n description: tr('housingvista.home.quickActions.viewAnalytics.description', 'Check performance metrics'),\n icon: BarChart3,\n path: 'analytics',\n },\n {\n title: tr('housingvista.home.quickActions.manageTransactions.title', 'Manage Transactions'),\n description: tr('housingvista.home.quickActions.manageTransactions.description', 'Handle ongoing deals'),\n icon: FileText,\n path: 'transactions',\n },\n {\n title: tr('housingvista.home.quickActions.browseProperties.title', 'Browse Properties'),\n description: tr('housingvista.home.quickActions.browseProperties.description', 'Explore listings'),\n icon: Building,\n path: 'properties',\n },\n ];\n\n const recentProperties = useMemo(() => properties.slice(0, 6), [properties]);\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Welcome Header — Boff UI 2.0 aurora hero (one per MFE) */}\n <div className=\"relative overflow-hidden rounded-card border border-border-seam bg-gloss-card p-6 shadow-pop\">\n <AuroraBackground intensity=\"subtle\" />\n <div className=\"relative flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n <div>\n <h1 className=\"text-3xl font-bold text-text-primary\">\n <GradientText>{tr('housingvista.home.header.title', 'Welcome back!')}</GradientText>\n </h1>\n <p className=\"mt-1 text-text-secondary\">\n {tr(\n 'housingvista.home.header.description',\n \"Here's what's happening with your real estate portfolio today.\"\n )}\n </p>\n </div>\n <Button onClick={() => navigate('properties/new')} className=\"gap-2 self-start lg:self-auto\">\n <Plus className=\"h-4 w-4\" />\n {tr('housingvista.home.header.quickAdd', 'Quick Add')}\n </Button>\n </div>\n </div>\n\n <PagePurpose className=\"max-w-3xl\">\n {tr(\n 'housingvista.home.purpose',\n 'Your real-estate command center. See your portfolio at a glance, jump to the things you do most, and pick up where you left off — from listing a property to tracking deals and analytics.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"housingvista-home\"\n steps={[\n {\n id: 'add-property',\n label: tr('housingvista.home.nextSteps.addProperty', 'List your first property'),\n description: tr(\n 'housingvista.home.nextSteps.addPropertyDesc',\n 'Create a market-ready listing in a few guided steps.'\n ),\n onClick: () => navigate('properties/new'),\n },\n {\n id: 'browse',\n label: tr('housingvista.home.nextSteps.browse', 'Browse properties'),\n description: tr('housingvista.home.nextSteps.browseDesc', 'Explore listings and open one for full details.'),\n onClick: () => navigate('properties'),\n },\n {\n id: 'analytics',\n label: tr('housingvista.home.nextSteps.analytics', 'Review your analytics'),\n description: tr(\n 'housingvista.home.nextSteps.analyticsDesc',\n 'Check portfolio performance and market insights.'\n ),\n onClick: () => navigate('analytics'),\n },\n ]}\n />\n\n {/* Stats Grid */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n label={tr('housingvista.home.stats.totalProperties', 'Total Properties')}\n value={stats.totalProperties}\n icon={<Building className=\"h-5 w-5\" />}\n trend=\"up\"\n change={12}\n featured\n />\n <StatCard\n label={tr('housingvista.home.stats.activeListings', 'Active Listings')}\n value={stats.activeTransactions}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n trend=\"up\"\n change={8}\n />\n <StatCard\n label={tr('housingvista.home.stats.totalViews', 'Total Views')}\n value={formatNumber(12540)}\n icon={<Eye className=\"h-5 w-5\" />}\n trend=\"down\"\n change={-2}\n />\n <StatCard\n label={tr('housingvista.home.stats.monthlyRevenue', 'Monthly Revenue')}\n value={stats.monthlyRevenue}\n prefix=\"$\"\n icon={<DollarSign className=\"h-5 w-5\" />}\n trend=\"up\"\n change={15}\n />\n </div>\n\n {/* Quick Actions + Recent Activity */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Quick Actions */}\n <div className=\"lg:col-span-1\">\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <div className=\"border-b border-border-seam px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n {tr('housingvista.home.quickActions.title', 'Quick Actions')}\n </h2>\n </div>\n <div className=\"space-y-2 p-4\">\n {quickActions.map((action) => {\n const Icon = action.icon;\n return (\n <button\n key={action.title}\n onClick={() => navigate(action.path)}\n className=\"flex min-h-[44px] w-full items-center gap-3 rounded-lg border border-border-seam p-3 text-left transition-colors hover:bg-bg-sunken/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <div className=\"rounded-lg bg-action-primary-bg/10 p-2\">\n <Icon className=\"h-5 w-5 text-text-primary\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">{action.title}</div>\n <div className=\"text-xs text-text-secondary\">{action.description}</div>\n </div>\n </button>\n );\n })}\n </div>\n </GlassCard>\n </div>\n\n {/* Recent Activity — primary insight zone (one EmphasisPanel per page) */}\n <div className=\"lg:col-span-2\">\n <EmphasisPanel className=\"p-0\">\n <div className=\"border-b border-border-seam px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n {tr('housingvista.home.recentActivity.title', 'Recent Activity')}\n </h2>\n </div>\n <div className=\"space-y-2 p-4\">\n {activities.slice(0, 6).map((activity) => {\n const Icon = activityIconMap[activity.type] ?? Eye;\n const colorClass = activityColorMap[activity.type] ?? 'text-text-secondary';\n return (\n <div\n key={activity.id}\n className=\"flex items-start gap-4 rounded-lg border border-border-seam bg-bg-surface/60 p-4 transition-colors hover:bg-bg-sunken/50\"\n >\n <div className=\"rounded-lg bg-bg-sunken p-2\">\n <Icon className={cn('h-5 w-5', colorClass)} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">{activity.title}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">{activity.description}</p>\n <p className=\"text-xs text-text-secondary mt-1\">\n {getRelativeTime(activity.timestamp)}\n {activity.actorName && ` · ${activity.actorName}`}\n </p>\n </div>\n </div>\n );\n })}\n </div>\n </EmphasisPanel>\n </div>\n </div>\n\n {/* Recent Properties */}\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <div className=\"flex items-center justify-between border-b border-border-seam px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n {tr('housingvista.home.recentProperties.title', 'Recent Properties')}\n </h2>\n <button\n onClick={() => navigate('properties')}\n className=\"inline-flex min-h-[44px] items-center gap-1 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] rounded-md px-1\"\n >\n {tr('housingvista.home.recentProperties.viewAll', 'View All')}\n <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {recentProperties.length === 0 ? (\n <div className=\"p-4\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tr('housingvista.home.recentProperties.empty.title', 'No properties yet')}\n description={tr(\n 'housingvista.home.recentProperties.empty.description',\n 'List your first property to see it appear here.'\n )}\n action={\n <Button onClick={() => navigate('properties/new')} className=\"gap-2\">\n <Plus className=\"h-4 w-4\" />\n {tr('housingvista.home.header.quickAdd', 'Quick Add')}\n </Button>\n }\n />\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 p-4 sm:grid-cols-2 lg:grid-cols-3\">\n {recentProperties.map((property) => (\n <PropertyCard\n key={property.id}\n id={property.id}\n title={property.title}\n price={property.price.amount}\n priceType={property.price.type === 'rent' ? 'rent' : 'sale'}\n pricePeriod={property.price.period}\n location={`${property.location.neighborhood}, ${property.location.city}`}\n bedrooms={property.specifications.bedrooms}\n bathrooms={property.specifications.bathrooms}\n area={property.specifications.totalArea}\n imageUrl={property.images[0]?.url}\n status={property.status.replace('_', ' ')}\n type={property.category}\n onClick={() => navigate(`properties/${property.id}`)}\n />\n ))}\n </div>\n )}\n </GlassCard>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HomePage.js","names":[],"sources":["../../../src/housingvista/pages/HomePage.tsx"],"sourcesContent":["/**\n * HousingVista Dashboard / Home Page\n * Migrated from legacy housingvista-frontend dashboard.tsx\n * @module housingvista\n */\n\nimport { useMemo } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n AuroraBackground,\n GradientText,\n Button,\n GlassCard,\n EmphasisPanel,\n IllustratedEmptyState,\n PagePurpose,\n NextSteps,\n} from '@burdenoff/fe-libs/ui';\nimport { Building, TrendingUp, Eye, DollarSign, Plus, BarChart3, FileText, Home, ArrowRight } from 'lucide-react';\nimport { StatCard, PropertyCard } from '../components';\nimport { useDashboardStats, useRecentActivities, useProperties } from '../hooks';\nimport { cn, formatNumber, getRelativeTime } from '../utils/helpers';\nimport type { RecentActivity } from '../types';\n\n// ---- Activity icon mapping ----\nconst activityIconMap: Record<RecentActivity['type'], typeof Eye> = {\n property_listed: Home,\n transaction_update: FileText,\n payment_received: DollarSign,\n maintenance_request: Building,\n viewing_scheduled: Eye,\n document_uploaded: FileText,\n offer_received: TrendingUp,\n lease_signed: FileText,\n};\n\nconst activityColorMap: Record<RecentActivity['type'], string> = {\n property_listed: 'text-accent-purple',\n transaction_update: 'text-status-info-text',\n payment_received: 'text-status-success-text',\n maintenance_request: 'text-status-warning-text',\n viewing_scheduled: 'text-status-info-text',\n document_uploaded: 'text-status-info-text',\n offer_received: 'text-status-success-text',\n lease_signed: 'text-accent-teal',\n};\n\nexport const HomePage = () => {\n const navigate = useNavigate();\n const { t } = useI18n();\n const { data: stats } = useDashboardStats();\n const { data: activities } = useRecentActivities();\n const { data: properties } = useProperties();\n\n const tr = (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n\n const quickActions = [\n {\n title: tr('housingvista.home.quickActions.addProperty.title', 'Add Property'),\n description: tr('housingvista.home.quickActions.addProperty.description', 'List a new property'),\n icon: Plus,\n path: 'properties/new',\n },\n {\n title: tr('housingvista.home.quickActions.viewAnalytics.title', 'View Analytics'),\n description: tr('housingvista.home.quickActions.viewAnalytics.description', 'Check performance metrics'),\n icon: BarChart3,\n path: 'analytics',\n },\n {\n title: tr('housingvista.home.quickActions.manageTransactions.title', 'Manage Transactions'),\n description: tr('housingvista.home.quickActions.manageTransactions.description', 'Handle ongoing deals'),\n icon: FileText,\n path: 'transactions',\n },\n {\n title: tr('housingvista.home.quickActions.browseProperties.title', 'Browse Properties'),\n description: tr('housingvista.home.quickActions.browseProperties.description', 'Explore listings'),\n icon: Building,\n path: 'properties',\n },\n ];\n\n const recentProperties = useMemo(() => properties.slice(0, 6), [properties]);\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Welcome Header — Boff UI 2.0 aurora hero (one per MFE) */}\n <div className=\"relative overflow-hidden rounded-card border border-border-seam bg-gloss-card p-6 shadow-pop\">\n <AuroraBackground intensity=\"subtle\" />\n <div className=\"relative flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n <div>\n <h1 className=\"text-3xl font-bold text-text-primary\">\n <GradientText>{tr('housingvista.home.header.title', 'Welcome back!')}</GradientText>\n </h1>\n <p className=\"mt-1 text-text-secondary\">\n {tr(\n 'housingvista.home.header.description',\n \"Here's what's happening with your real estate portfolio today.\"\n )}\n </p>\n </div>\n <Button onClick={() => navigate('properties/new')} className=\"gap-2 self-start lg:self-auto\">\n <Plus className=\"h-4 w-4\" />\n {tr('housingvista.home.header.quickAdd', 'Quick Add')}\n </Button>\n </div>\n </div>\n\n <PagePurpose className=\"max-w-3xl\">\n {tr(\n 'housingvista.home.purpose',\n 'Your real-estate command center. See your portfolio at a glance, jump to the things you do most, and pick up where you left off — from listing a property to tracking deals and analytics.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"housingvista-home\"\n steps={[\n {\n id: 'add-property',\n label: tr('housingvista.home.nextSteps.addProperty', 'List your first property'),\n description: tr(\n 'housingvista.home.nextSteps.addPropertyDesc',\n 'Create a market-ready listing in a few guided steps.'\n ),\n onClick: () => navigate('properties/new'),\n },\n {\n id: 'browse',\n label: tr('housingvista.home.nextSteps.browse', 'Browse properties'),\n description: tr(\n 'housingvista.home.nextSteps.browseDesc',\n 'Explore listings and open one for full details.'\n ),\n onClick: () => navigate('properties'),\n },\n {\n id: 'analytics',\n label: tr('housingvista.home.nextSteps.analytics', 'Review your analytics'),\n description: tr(\n 'housingvista.home.nextSteps.analyticsDesc',\n 'Check portfolio performance and market insights.'\n ),\n onClick: () => navigate('analytics'),\n },\n ]}\n />\n\n {/* Stats Grid */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n label={tr('housingvista.home.stats.totalProperties', 'Total Properties')}\n value={stats.totalProperties}\n icon={<Building className=\"h-5 w-5\" />}\n trend=\"up\"\n change={12}\n featured\n />\n <StatCard\n label={tr('housingvista.home.stats.activeListings', 'Active Listings')}\n value={stats.activeTransactions}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n trend=\"up\"\n change={8}\n />\n <StatCard\n label={tr('housingvista.home.stats.totalViews', 'Total Views')}\n value={formatNumber(12540)}\n icon={<Eye className=\"h-5 w-5\" />}\n trend=\"down\"\n change={-2}\n />\n <StatCard\n label={tr('housingvista.home.stats.monthlyRevenue', 'Monthly Revenue')}\n value={stats.monthlyRevenue}\n prefix=\"$\"\n icon={<DollarSign className=\"h-5 w-5\" />}\n trend=\"up\"\n change={15}\n />\n </div>\n\n {/* Quick Actions + Recent Activity */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Quick Actions */}\n <div className=\"lg:col-span-1\">\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <div className=\"border-b border-border-seam px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n {tr('housingvista.home.quickActions.title', 'Quick Actions')}\n </h2>\n </div>\n <div className=\"space-y-2 p-4\">\n {quickActions.map((action) => {\n const Icon = action.icon;\n return (\n <button\n key={action.title}\n onClick={() => navigate(action.path)}\n className=\"flex min-h-[44px] w-full items-center gap-3 rounded-lg border border-border-seam p-3 text-left transition-colors hover:bg-bg-sunken/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <div className=\"rounded-lg bg-action-primary-bg/10 p-2\">\n <Icon className=\"h-5 w-5 text-text-primary\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">{action.title}</div>\n <div className=\"text-xs text-text-secondary\">{action.description}</div>\n </div>\n </button>\n );\n })}\n </div>\n </GlassCard>\n </div>\n\n {/* Recent Activity — primary insight zone (one EmphasisPanel per page) */}\n <div className=\"lg:col-span-2\">\n <EmphasisPanel className=\"p-0\">\n <div className=\"border-b border-border-seam px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n {tr('housingvista.home.recentActivity.title', 'Recent Activity')}\n </h2>\n </div>\n <div className=\"space-y-2 p-4\">\n {activities.slice(0, 6).map((activity) => {\n const Icon = activityIconMap[activity.type] ?? Eye;\n const colorClass = activityColorMap[activity.type] ?? 'text-text-secondary';\n return (\n <div\n key={activity.id}\n className=\"flex items-start gap-4 rounded-lg border border-border-seam bg-bg-surface/60 p-4 transition-colors hover:bg-bg-sunken/50\"\n >\n <div className=\"rounded-lg bg-bg-sunken p-2\">\n <Icon className={cn('h-5 w-5', colorClass)} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">{activity.title}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">{activity.description}</p>\n <p className=\"text-xs text-text-secondary mt-1\">\n {getRelativeTime(activity.timestamp)}\n {activity.actorName && ` · ${activity.actorName}`}\n </p>\n </div>\n </div>\n );\n })}\n </div>\n </EmphasisPanel>\n </div>\n </div>\n\n {/* Recent Properties */}\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <div className=\"flex items-center justify-between border-b border-border-seam px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n {tr('housingvista.home.recentProperties.title', 'Recent Properties')}\n </h2>\n <button\n onClick={() => navigate('properties')}\n className=\"inline-flex min-h-[44px] items-center gap-1 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] rounded-md px-1\"\n >\n {tr('housingvista.home.recentProperties.viewAll', 'View All')}\n <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {recentProperties.length === 0 ? (\n <div className=\"p-4\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tr('housingvista.home.recentProperties.empty.title', 'No properties yet')}\n description={tr(\n 'housingvista.home.recentProperties.empty.description',\n 'List your first property to see it appear here.'\n )}\n action={\n <Button onClick={() => navigate('properties/new')} className=\"gap-2\">\n <Plus className=\"h-4 w-4\" />\n {tr('housingvista.home.header.quickAdd', 'Quick Add')}\n </Button>\n }\n />\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 p-4 sm:grid-cols-2 lg:grid-cols-3\">\n {recentProperties.map((property) => (\n <PropertyCard\n key={property.id}\n id={property.id}\n title={property.title}\n price={property.price.amount}\n priceType={property.price.type === 'rent' ? 'rent' : 'sale'}\n pricePeriod={property.price.period}\n location={`${property.location.neighborhood}, ${property.location.city}`}\n bedrooms={property.specifications.bedrooms}\n bathrooms={property.specifications.bathrooms}\n area={property.specifications.totalArea}\n imageUrl={property.images[0]?.url}\n status={property.status.replace('_', ' ')}\n type={property.category}\n onClick={() => navigate(`properties/${property.id}`)}\n />\n ))}\n </div>\n )}\n </GlassCard>\n </div>\n );\n};\n"],"mappings":"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"}
|