@burdenoff/microfe-housingvista 2026.703.13 → 2026.709.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/generated/wspace-operations.js +67 -0
  2. package/dist/generated/wspace-operations.js.map +1 -0
  3. package/dist/housingvista/hooks/index.js +1 -0
  4. package/dist/housingvista/hooks/index.js.map +1 -1
  5. package/dist/housingvista/hooks/useHousingVistaAnalytics.js +57 -0
  6. package/dist/housingvista/hooks/useHousingVistaAnalytics.js.map +1 -0
  7. package/dist/housingvista/index.js +2 -0
  8. package/dist/housingvista/pages/HomePage.js +135 -132
  9. package/dist/housingvista/pages/HomePage.js.map +1 -1
  10. package/dist/housingvista/pages/analytics/AnalyticsPage.js +136 -134
  11. package/dist/housingvista/pages/analytics/AnalyticsPage.js.map +1 -1
  12. package/dist/housingvista/pages/assets/AssetManagementPage.js.map +1 -1
  13. package/dist/housingvista/pages/investments/InvestmentsPage.js.map +1 -1
  14. package/dist/housingvista/pages/properties/AddPropertyPage.js +279 -278
  15. package/dist/housingvista/pages/properties/AddPropertyPage.js.map +1 -1
  16. package/dist/housingvista/pages/properties/PropertiesPage.js +93 -86
  17. package/dist/housingvista/pages/properties/PropertiesPage.js.map +1 -1
  18. package/dist/housingvista/pages/properties/PropertyDetailPage.js +514 -511
  19. package/dist/housingvista/pages/properties/PropertyDetailPage.js.map +1 -1
  20. package/dist/housingvista/services/assistantApi.js +222 -0
  21. package/dist/housingvista/services/assistantApi.js.map +1 -0
  22. package/dist/housingvista/services/createSandboxAssistantTransport.js +120 -0
  23. package/dist/housingvista/services/createSandboxAssistantTransport.js.map +1 -0
  24. package/dist/housingvista/utils/pageContext.js +77 -0
  25. package/dist/housingvista/utils/pageContext.js.map +1 -0
  26. package/dist/index.js +5 -3
  27. package/package.json +6 -2
@@ -1 +1 @@
1
- {"version":3,"file":"InvestmentsPage.js","names":[],"sources":["../../../../src/housingvista/pages/investments/InvestmentsPage.tsx"],"sourcesContent":["import { useState, useMemo, useEffect, type FormEvent } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n DollarSign,\n TrendingUp,\n PieChart,\n BarChart3,\n Plus,\n Building,\n ArrowUpRight,\n ArrowDownRight,\n Eye,\n Target,\n Calendar,\n} from 'lucide-react';\nimport {\n EmphasisPanel,\n NextSteps,\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { PieChart as RePieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts';\nimport { PageLayout, StatCard, FilterBar, StatusBadge, EmptyState, ChartCard } from '../../components';\nimport { useInvestments, useCreateInvestment, useProperties } from '../../hooks';\nimport { useServerPagination } from '../../hooks/useServerPagination';\nimport { notify } from '../../lib/toast';\nimport { cn, formatCurrency, formatDate } from '../../utils/helpers';\nimport type { InvestmentStatus, InvestmentType } from '../../types';\n\nconst inputClass =\n 'h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\n// Full oklch chart tokens (see fe-libs theme) used for the allocation donut.\nconst CHART_COLORS = ['var(--color-chart-1)', 'var(--color-chart-2)', 'var(--color-chart-3)', 'var(--color-chart-4)', 'var(--color-chart-5)'];\nconst tooltipContentStyle: React.CSSProperties = {\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-subtle)',\n borderRadius: 10,\n fontSize: 12,\n boxShadow: '0 8px 24px rgb(0 0 0 / 0.12)',\n};\n\nconst investmentTypeLabels: Record<InvestmentType, string> = {\n full_ownership: 'Full ownership',\n fractional: 'Fractional',\n reits: 'REITs',\n fund: 'Fund',\n};\n\nfunction riskBadgeVariant(risk: string): 'success' | 'warning' | 'error' | 'muted' {\n switch (risk) {\n case 'low':\n return 'success';\n case 'medium':\n return 'warning';\n case 'high':\n return 'error';\n default:\n return 'muted';\n }\n}\n\nconst statusTabs: { value: InvestmentStatus | 'all'; label: string }[] = [\n { value: 'all', label: 'All' },\n { value: 'active', label: 'Active' },\n { value: 'closed', label: 'Completed' },\n { value: 'funding', label: 'Funding' },\n];\n\nconst investmentTypeOptions: { value: InvestmentType; label: string }[] = [\n { value: 'full_ownership', label: 'Full ownership' },\n { value: 'fractional', label: 'Fractional' },\n { value: 'reits', label: 'REITs' },\n { value: 'fund', label: 'Fund' },\n];\n\nconst statusOptions: { value: InvestmentStatus; label: string }[] = [\n { value: 'draft', label: 'Draft' },\n { value: 'open', label: 'Open' },\n { value: 'funding', label: 'Funding' },\n { value: 'active', label: 'Active' },\n { value: 'mature', label: 'Mature' },\n { value: 'closed', label: 'Closed' },\n];\n\nconst riskOptions: { value: 'low' | 'medium' | 'high'; label: string }[] = [\n { value: 'low', label: 'Low' },\n { value: 'medium', label: 'Medium' },\n { value: 'high', label: 'High' },\n];\n\n// ---------------------------------------------------------------------------\n// Safe accessors — `investors` and `returns` are opaque JSON columns and the\n// date fields arrive as ISO strings at runtime, so aggregates never trust the\n// domain type's shape.\n// ---------------------------------------------------------------------------\nfunction toNumber(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : 0;\n}\n\nfunction safeArray(value: unknown): unknown[] {\n return Array.isArray(value) ? value : [];\n}\n\nfunction sumField(value: unknown, field: string): number {\n return safeArray(value).reduce<number>((sum, item) => {\n const rec = typeof item === 'object' && item !== null ? (item as Record<string, unknown>) : {};\n return sum + toNumber(rec[field]);\n }, 0);\n}\n\nfunction toIsoString(value: unknown): string {\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? '' : value.toISOString();\n if (typeof value === 'string') return value;\n return '';\n}\n\nfunction formatDateSafe(value: unknown): string {\n const iso = toIsoString(value);\n if (!iso) return '—';\n const ms = new Date(iso).getTime();\n return Number.isNaN(ms) ? '—' : formatDate(iso);\n}\n\nfunction getStatusVariant(status: InvestmentStatus): 'success' | 'warning' | 'error' | 'info' | 'muted' | 'default' {\n switch (status) {\n case 'active':\n return 'success';\n case 'funding':\n return 'warning';\n case 'closed':\n case 'mature':\n return 'info';\n case 'draft':\n case 'open':\n return 'muted';\n default:\n return 'default';\n }\n}\n\nfunction getReturnColor(returnValue: number) {\n if (returnValue > 0) return 'text-status-success-text';\n if (returnValue < 0) return 'text-status-error-text';\n return 'text-text-secondary';\n}\n\nexport function InvestmentsPage() {\n const navigate = useNavigate();\n const pagination = useServerPagination(10);\n const [search, setSearch] = useState('');\n const [debouncedSearch, setDebouncedSearch] = useState('');\n const [activeTab, setActiveTab] = useState<InvestmentStatus | 'all'>('all');\n\n // Debounce the raw search input into the value that feeds the server filter.\n useEffect(() => {\n const timer = setTimeout(() => setDebouncedSearch(search), 300);\n return () => clearTimeout(timer);\n }, [search]);\n\n const { data: investments } = useInvestments({\n filter: {\n search: debouncedSearch.trim() || undefined,\n status: activeTab === 'all' ? undefined : activeTab,\n },\n pagination: pagination.pagination,\n });\n // Portfolio summary reflects the whole portfolio, not just the current page.\n const { data: allInvestments } = useInvestments({ pagination: { limit: 100, offset: 0 } });\n const { data: properties } = useProperties({ pagination: { limit: 50, offset: 0 } });\n const { createInvestment, loading: creating } = useCreateInvestment();\n\n // Search and tab changes narrow the result set — always return to the first page.\n const handleSearchChange = (value: string) => {\n setSearch(value);\n pagination.setPage(1);\n };\n\n const handleTabChange = (tab: InvestmentStatus | 'all') => {\n setActiveTab(tab);\n pagination.setPage(1);\n };\n\n // Create-investment form state\n const [createOpen, setCreateOpen] = useState(false);\n const [propertyId, setPropertyId] = useState('');\n const [title, setTitle] = useState('');\n const [investmentType, setInvestmentType] = useState<InvestmentType>('full_ownership');\n const [status, setStatus] = useState<InvestmentStatus>('draft');\n const [riskLevel, setRiskLevel] = useState<'low' | 'medium' | 'high'>('medium');\n const [totalValue, setTotalValue] = useState('');\n const [startDate, setStartDate] = useState('');\n const [maturityDate, setMaturityDate] = useState('');\n\n const resetForm = () => {\n setPropertyId('');\n setTitle('');\n setInvestmentType('full_ownership');\n setStatus('draft');\n setRiskLevel('medium');\n setTotalValue('');\n setStartDate('');\n setMaturityDate('');\n };\n\n const openCreate = () => {\n resetForm();\n setCreateOpen(true);\n };\n\n const handleCreate = async (e: FormEvent) => {\n e.preventDefault();\n if (!propertyId) {\n notify.warning('Select a property');\n return;\n }\n if (!title.trim()) {\n notify.warning('Enter an investment title');\n return;\n }\n const value = Number(totalValue);\n if (!Number.isFinite(value) || value <= 0) {\n notify.warning('Enter a valid total value');\n return;\n }\n try {\n await createInvestment({\n propertyId,\n title: title.trim(),\n status,\n investmentType,\n riskLevel,\n totalValue: value,\n startDate: startDate ? new Date(startDate).toISOString() : undefined,\n maturityDate: maturityDate ? new Date(maturityDate).toISOString() : undefined,\n });\n notify.success('Investment created', `${title.trim()} was added to your portfolio.`);\n setCreateOpen(false);\n resetForm();\n } catch (err) {\n notify.error('Could not create investment', (err as Error).message);\n }\n };\n\n const portfolioStats = useMemo(() => {\n const totalInvested = allInvestments.reduce((sum, inv) => sum + sumField(inv.investors, 'investmentAmount'), 0);\n const totalReturns = allInvestments.reduce((sum, inv) => sum + sumField(inv.returns, 'amount'), 0);\n const currentValue = totalInvested + totalReturns;\n const activeCount = allInvestments.filter((inv) => inv.status === 'active').length;\n const returnPct = totalInvested > 0 ? (totalReturns / totalInvested) * 100 : 0;\n return { totalInvested, currentValue, totalReturns, activeCount, returnPct };\n }, [allInvestments]);\n\n // Portfolio allocation by investment type, weighted by current value (invested\n // + returns) — real figures from every holding, never fabricated.\n const allocation = useMemo(() => {\n const byType = new Map<string, number>();\n for (const inv of allInvestments) {\n const invested = sumField(inv.investors, 'investmentAmount');\n const returns = sumField(inv.returns, 'amount');\n const value = invested + returns || toNumber(inv.totalValue);\n const key = inv.investmentType;\n byType.set(key, (byType.get(key) ?? 0) + value);\n }\n const rows = Array.from(byType.entries())\n .filter(([, value]) => value > 0)\n .map(([type, value], idx) => ({\n type,\n label: investmentTypeLabels[type as InvestmentType] ?? type.replace('_', ' '),\n value,\n color: CHART_COLORS[idx % CHART_COLORS.length],\n }))\n .sort((a, b) => b.value - a.value);\n const total = rows.reduce((s, r) => s + r.value, 0);\n return { rows, total };\n }, [allInvestments]);\n\n return (\n <PageLayout\n title=\"Investments\"\n description=\"Track your real estate investment portfolio and performance\"\n purpose=\"Follow every real-estate investment you hold in one portfolio — how much you put in, what it is worth now, the returns it has paid, and its risk level. Use it to judge performance and decide where to put your next dollar.\"\n actions={\n <button\n onClick={openCreate}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg-hover\"\n >\n <Plus className=\"h-4 w-4\" />\n New Investment\n </button>\n }\n >\n {/* Portfolio Summary Stats — primary insight zone (one EmphasisPanel per page) */}\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n label=\"Total Invested\"\n value={portfolioStats.totalInvested}\n prefix=\"$\"\n icon={<DollarSign className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Current Value\"\n value={portfolioStats.currentValue}\n prefix=\"$\"\n icon={<TrendingUp className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Total Returns\"\n value={portfolioStats.totalReturns}\n prefix=\"$\"\n icon={<PieChart className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Active Investments\"\n value={portfolioStats.activeCount}\n icon={<BarChart3 className=\"h-4 w-4\" />}\n />\n </div>\n </EmphasisPanel>\n\n <NextSteps\n storageKey=\"housingvista-investments\"\n steps={[\n {\n id: 'review-active',\n label: 'Review your active investments',\n description: 'Filter to the active deals currently paying returns.',\n onClick: () => handleTabChange('active'),\n },\n {\n id: 'find-funding',\n label: 'Find funding opportunities',\n description: 'See deals still raising capital that you can join.',\n onClick: () => handleTabChange('funding'),\n },\n {\n id: 'open-detail',\n label: 'Open an investment for full details',\n description: 'Drill into returns, investors, and risk for any holding.',\n onClick: () => {\n const first = investments[0];\n if (first) navigate(`../investments/${first.id}`);\n },\n },\n ]}\n />\n\n {/* Filter Tabs */}\n <div className=\"flex flex-wrap items-center gap-2\">\n {statusTabs.map((tab) => (\n <button\n key={tab.value}\n onClick={() => handleTabChange(tab.value)}\n className={cn(\n 'rounded-md px-3 py-1.5 text-sm font-medium transition-colors',\n activeTab === tab.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {tab.label}\n </button>\n ))}\n </div>\n\n {/* Search */}\n <FilterBar\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder=\"Search by property or investment title...\"\n />\n\n {/* Results count */}\n <p className=\"text-sm text-text-secondary\">\n Showing {investments.length} {investments.length === 1 ? 'investment' : 'investments'} on this page\n </p>\n\n {/* Investment list (left) + portfolio allocation donut (aside) */}\n <div className=\"grid grid-cols-1 gap-6 xl:grid-cols-3\">\n <div className=\"space-y-4 xl:col-span-2\">\n {investments.length > 0 ? (\n <>\n <div className=\"space-y-4\">\n {investments.map((investment) => {\n const propertyTitle = investment.property?.title ?? 'Untitled property';\n const totalInvested = sumField(investment.investors, 'investmentAmount');\n const totalReturns = sumField(investment.returns, 'amount');\n const currentValue = totalInvested + totalReturns;\n const returnPercentage = totalInvested > 0 ? (totalReturns / totalInvested) * 100 : 0;\n const returnsCount = safeArray(investment.returns).length;\n\n return (\n <div\n key={investment.id}\n className=\"cursor-pointer rounded-lg border border-border-subtle bg-bg-surface p-5 shadow-sm transition-shadow hover:shadow-md\"\n onClick={() => navigate(`../investments/${investment.id}`)}\n >\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex-1\">\n <div className=\"flex items-start gap-3\">\n <div className=\"rounded-lg bg-bg-sunken p-2\">\n <Building className=\"h-5 w-5 text-text-secondary\" />\n </div>\n <div className=\"flex-1\">\n <div className=\"mb-2 flex flex-wrap items-center gap-2\">\n <h3 className=\"text-lg font-semibold text-text-primary\">{propertyTitle}</h3>\n <StatusBadge label={investment.status} variant={getStatusVariant(investment.status)} />\n <StatusBadge label={investment.investmentType.replace('_', ' ')} variant=\"muted\" />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 text-sm sm:grid-cols-2 lg:grid-cols-4\">\n <div>\n <p className=\"text-text-secondary\">Invested Amount</p>\n <p className=\"text-lg font-medium text-text-primary\">\n {formatCurrency(totalInvested)}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Current Value</p>\n <p className=\"text-lg font-medium text-text-primary\">\n {formatCurrency(currentValue)}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Total Return</p>\n <div className={cn('font-medium', getReturnColor(totalReturns))}>\n <div className=\"flex items-center gap-1\">\n {totalReturns > 0 && <ArrowUpRight className=\"h-3 w-3\" />}\n {totalReturns < 0 && <ArrowDownRight className=\"h-3 w-3\" />}\n {formatCurrency(totalReturns)}\n </div>\n <div className=\"text-sm\">{returnPercentage.toFixed(1)}%</div>\n </div>\n </div>\n <div>\n <p className=\"text-text-secondary\">Risk Level</p>\n <div className=\"mt-1\">\n <StatusBadge\n label={`${investment.riskLevel} risk`}\n variant={riskBadgeVariant(investment.riskLevel)}\n dot\n />\n </div>\n </div>\n </div>\n\n <div className=\"mt-3 flex flex-wrap items-center gap-5 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <TrendingUp className=\"h-3 w-3\" />\n Start: {formatDateSafe(investment.startDate)}\n </span>\n <span className=\"flex items-center gap-1\">\n <Target className=\"h-3 w-3\" />\n Type: {investment.investmentType.replace('_', ' ')}\n </span>\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n Returns: {returnsCount} payments\n </span>\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"flex shrink-0 gap-2\">\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(`../investments/${investment.id}`);\n }}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Eye className=\"h-4 w-4\" />\n View Details\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n <Pagination\n page={pagination.page}\n pageSize={pagination.pageSize}\n hasNextPage={investments.length >= pagination.pageSize}\n hasPreviousPage={pagination.page > 1}\n onPageChange={pagination.setPage}\n onPageSizeChange={pagination.setPageSize}\n />\n </>\n ) : (\n <EmptyState\n illustration=\"empty-search\"\n icon={<PieChart className=\"h-12 w-12\" />}\n title=\"No investments found\"\n description=\"Try adjusting your search criteria or start a new investment.\"\n action={\n <button\n onClick={openCreate}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg-hover\"\n >\n <Plus className=\"h-4 w-4\" />\n New Investment\n </button>\n }\n />\n )}\n </div>\n\n {/* Portfolio Allocation — real value split by investment type */}\n {allocation.rows.length > 0 && (\n <aside className=\"xl:col-span-1\">\n <ChartCard title=\"Portfolio Allocation\" description=\"Current value by investment type\">\n <div className=\"relative h-52 w-full\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <RePieChart>\n <Pie\n data={allocation.rows}\n dataKey=\"value\"\n nameKey=\"label\"\n innerRadius={56}\n outerRadius={82}\n paddingAngle={2}\n stroke=\"var(--color-bg-surface)\"\n strokeWidth={2}\n >\n {allocation.rows.map((entry) => (\n <Cell key={entry.type} fill={entry.color} />\n ))}\n </Pie>\n <Tooltip\n contentStyle={tooltipContentStyle}\n formatter={(value: number, _n, item) => [formatCurrency(value), item?.payload?.label]}\n />\n </RePieChart>\n </ResponsiveContainer>\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center\">\n <span className=\"text-xs text-text-secondary\">Total value</span>\n <span className=\"text-lg font-bold text-text-primary\">{formatCurrency(allocation.total)}</span>\n </div>\n </div>\n <ul className=\"mt-4 space-y-2.5\">\n {allocation.rows.map((entry) => (\n <li key={entry.type} className=\"flex items-center justify-between text-sm\">\n <span className=\"flex items-center gap-2\">\n <span className=\"h-2.5 w-2.5 rounded-full\" style={{ background: entry.color }} />\n <span className=\"text-text-primary\">{entry.label}</span>\n </span>\n <span className=\"font-medium text-text-secondary\">{formatCurrency(entry.value)}</span>\n </li>\n ))}\n </ul>\n </ChartCard>\n </aside>\n )}\n </div>\n\n {/* Create Investment dialog */}\n <ResponsiveDialog open={createOpen} onOpenChange={setCreateOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleCreate}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New investment</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Register a new holding against one of your properties.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Property *</label>\n <select\n required\n value={propertyId}\n onChange={(e) => setPropertyId(e.target.value)}\n className={inputClass}\n >\n <option value=\"\" disabled>\n {properties.length > 0 ? 'Select a property' : 'No properties available'}\n </option>\n {properties.map((property) => (\n <option key={property.id} value={property.id}>\n {property.title}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Title *</label>\n <input\n type=\"text\"\n required\n placeholder=\"e.g. Riverside Villa fractional stake\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Type</label>\n <select\n value={investmentType}\n onChange={(e) => setInvestmentType(e.target.value as InvestmentType)}\n className={inputClass}\n >\n {investmentTypeOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Status</label>\n <select\n value={status}\n onChange={(e) => setStatus(e.target.value as InvestmentStatus)}\n className={inputClass}\n >\n {statusOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Risk level</label>\n <select\n value={riskLevel}\n onChange={(e) => setRiskLevel(e.target.value as 'low' | 'medium' | 'high')}\n className={inputClass}\n >\n {riskOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Total value (USD) *</label>\n <input\n type=\"number\"\n required\n min={1}\n step=\"any\"\n placeholder=\"0\"\n value={totalValue}\n onChange={(e) => setTotalValue(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Start date</label>\n <input\n type=\"date\"\n value={startDate}\n onChange={(e) => setStartDate(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Maturity date</label>\n <input\n type=\"date\"\n value={maturityDate}\n onChange={(e) => setMaturityDate(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setCreateOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={creating || !propertyId || !title.trim() || !totalValue}>\n {creating ? 'Creating…' : 'Create investment'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"InvestmentsPage.js","names":[],"sources":["../../../../src/housingvista/pages/investments/InvestmentsPage.tsx"],"sourcesContent":["import { useState, useMemo, useEffect, type FormEvent } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n DollarSign,\n TrendingUp,\n PieChart,\n BarChart3,\n Plus,\n Building,\n ArrowUpRight,\n ArrowDownRight,\n Eye,\n Target,\n Calendar,\n} from 'lucide-react';\nimport {\n EmphasisPanel,\n NextSteps,\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { PieChart as RePieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts';\nimport { PageLayout, StatCard, FilterBar, StatusBadge, EmptyState, ChartCard } from '../../components';\nimport { useInvestments, useCreateInvestment, useProperties } from '../../hooks';\nimport { useServerPagination } from '../../hooks/useServerPagination';\nimport { notify } from '../../lib/toast';\nimport { cn, formatCurrency, formatDate } from '../../utils/helpers';\nimport type { InvestmentStatus, InvestmentType } from '../../types';\n\nconst inputClass =\n 'h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\n// Full oklch chart tokens (see fe-libs theme) used for the allocation donut.\nconst CHART_COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n];\nconst tooltipContentStyle: React.CSSProperties = {\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-subtle)',\n borderRadius: 10,\n fontSize: 12,\n boxShadow: '0 8px 24px rgb(0 0 0 / 0.12)',\n};\n\nconst investmentTypeLabels: Record<InvestmentType, string> = {\n full_ownership: 'Full ownership',\n fractional: 'Fractional',\n reits: 'REITs',\n fund: 'Fund',\n};\n\nfunction riskBadgeVariant(risk: string): 'success' | 'warning' | 'error' | 'muted' {\n switch (risk) {\n case 'low':\n return 'success';\n case 'medium':\n return 'warning';\n case 'high':\n return 'error';\n default:\n return 'muted';\n }\n}\n\nconst statusTabs: { value: InvestmentStatus | 'all'; label: string }[] = [\n { value: 'all', label: 'All' },\n { value: 'active', label: 'Active' },\n { value: 'closed', label: 'Completed' },\n { value: 'funding', label: 'Funding' },\n];\n\nconst investmentTypeOptions: { value: InvestmentType; label: string }[] = [\n { value: 'full_ownership', label: 'Full ownership' },\n { value: 'fractional', label: 'Fractional' },\n { value: 'reits', label: 'REITs' },\n { value: 'fund', label: 'Fund' },\n];\n\nconst statusOptions: { value: InvestmentStatus; label: string }[] = [\n { value: 'draft', label: 'Draft' },\n { value: 'open', label: 'Open' },\n { value: 'funding', label: 'Funding' },\n { value: 'active', label: 'Active' },\n { value: 'mature', label: 'Mature' },\n { value: 'closed', label: 'Closed' },\n];\n\nconst riskOptions: { value: 'low' | 'medium' | 'high'; label: string }[] = [\n { value: 'low', label: 'Low' },\n { value: 'medium', label: 'Medium' },\n { value: 'high', label: 'High' },\n];\n\n// ---------------------------------------------------------------------------\n// Safe accessors — `investors` and `returns` are opaque JSON columns and the\n// date fields arrive as ISO strings at runtime, so aggregates never trust the\n// domain type's shape.\n// ---------------------------------------------------------------------------\nfunction toNumber(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : 0;\n}\n\nfunction safeArray(value: unknown): unknown[] {\n return Array.isArray(value) ? value : [];\n}\n\nfunction sumField(value: unknown, field: string): number {\n return safeArray(value).reduce<number>((sum, item) => {\n const rec = typeof item === 'object' && item !== null ? (item as Record<string, unknown>) : {};\n return sum + toNumber(rec[field]);\n }, 0);\n}\n\nfunction toIsoString(value: unknown): string {\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? '' : value.toISOString();\n if (typeof value === 'string') return value;\n return '';\n}\n\nfunction formatDateSafe(value: unknown): string {\n const iso = toIsoString(value);\n if (!iso) return '—';\n const ms = new Date(iso).getTime();\n return Number.isNaN(ms) ? '—' : formatDate(iso);\n}\n\nfunction getStatusVariant(status: InvestmentStatus): 'success' | 'warning' | 'error' | 'info' | 'muted' | 'default' {\n switch (status) {\n case 'active':\n return 'success';\n case 'funding':\n return 'warning';\n case 'closed':\n case 'mature':\n return 'info';\n case 'draft':\n case 'open':\n return 'muted';\n default:\n return 'default';\n }\n}\n\nfunction getReturnColor(returnValue: number) {\n if (returnValue > 0) return 'text-status-success-text';\n if (returnValue < 0) return 'text-status-error-text';\n return 'text-text-secondary';\n}\n\nexport function InvestmentsPage() {\n const navigate = useNavigate();\n const pagination = useServerPagination(10);\n const [search, setSearch] = useState('');\n const [debouncedSearch, setDebouncedSearch] = useState('');\n const [activeTab, setActiveTab] = useState<InvestmentStatus | 'all'>('all');\n\n // Debounce the raw search input into the value that feeds the server filter.\n useEffect(() => {\n const timer = setTimeout(() => setDebouncedSearch(search), 300);\n return () => clearTimeout(timer);\n }, [search]);\n\n const { data: investments } = useInvestments({\n filter: {\n search: debouncedSearch.trim() || undefined,\n status: activeTab === 'all' ? undefined : activeTab,\n },\n pagination: pagination.pagination,\n });\n // Portfolio summary reflects the whole portfolio, not just the current page.\n const { data: allInvestments } = useInvestments({ pagination: { limit: 100, offset: 0 } });\n const { data: properties } = useProperties({ pagination: { limit: 50, offset: 0 } });\n const { createInvestment, loading: creating } = useCreateInvestment();\n\n // Search and tab changes narrow the result set — always return to the first page.\n const handleSearchChange = (value: string) => {\n setSearch(value);\n pagination.setPage(1);\n };\n\n const handleTabChange = (tab: InvestmentStatus | 'all') => {\n setActiveTab(tab);\n pagination.setPage(1);\n };\n\n // Create-investment form state\n const [createOpen, setCreateOpen] = useState(false);\n const [propertyId, setPropertyId] = useState('');\n const [title, setTitle] = useState('');\n const [investmentType, setInvestmentType] = useState<InvestmentType>('full_ownership');\n const [status, setStatus] = useState<InvestmentStatus>('draft');\n const [riskLevel, setRiskLevel] = useState<'low' | 'medium' | 'high'>('medium');\n const [totalValue, setTotalValue] = useState('');\n const [startDate, setStartDate] = useState('');\n const [maturityDate, setMaturityDate] = useState('');\n\n const resetForm = () => {\n setPropertyId('');\n setTitle('');\n setInvestmentType('full_ownership');\n setStatus('draft');\n setRiskLevel('medium');\n setTotalValue('');\n setStartDate('');\n setMaturityDate('');\n };\n\n const openCreate = () => {\n resetForm();\n setCreateOpen(true);\n };\n\n const handleCreate = async (e: FormEvent) => {\n e.preventDefault();\n if (!propertyId) {\n notify.warning('Select a property');\n return;\n }\n if (!title.trim()) {\n notify.warning('Enter an investment title');\n return;\n }\n const value = Number(totalValue);\n if (!Number.isFinite(value) || value <= 0) {\n notify.warning('Enter a valid total value');\n return;\n }\n try {\n await createInvestment({\n propertyId,\n title: title.trim(),\n status,\n investmentType,\n riskLevel,\n totalValue: value,\n startDate: startDate ? new Date(startDate).toISOString() : undefined,\n maturityDate: maturityDate ? new Date(maturityDate).toISOString() : undefined,\n });\n notify.success('Investment created', `${title.trim()} was added to your portfolio.`);\n setCreateOpen(false);\n resetForm();\n } catch (err) {\n notify.error('Could not create investment', (err as Error).message);\n }\n };\n\n const portfolioStats = useMemo(() => {\n const totalInvested = allInvestments.reduce((sum, inv) => sum + sumField(inv.investors, 'investmentAmount'), 0);\n const totalReturns = allInvestments.reduce((sum, inv) => sum + sumField(inv.returns, 'amount'), 0);\n const currentValue = totalInvested + totalReturns;\n const activeCount = allInvestments.filter((inv) => inv.status === 'active').length;\n const returnPct = totalInvested > 0 ? (totalReturns / totalInvested) * 100 : 0;\n return { totalInvested, currentValue, totalReturns, activeCount, returnPct };\n }, [allInvestments]);\n\n // Portfolio allocation by investment type, weighted by current value (invested\n // + returns) — real figures from every holding, never fabricated.\n const allocation = useMemo(() => {\n const byType = new Map<string, number>();\n for (const inv of allInvestments) {\n const invested = sumField(inv.investors, 'investmentAmount');\n const returns = sumField(inv.returns, 'amount');\n const value = invested + returns || toNumber(inv.totalValue);\n const key = inv.investmentType;\n byType.set(key, (byType.get(key) ?? 0) + value);\n }\n const rows = Array.from(byType.entries())\n .filter(([, value]) => value > 0)\n .map(([type, value], idx) => ({\n type,\n label: investmentTypeLabels[type as InvestmentType] ?? type.replace('_', ' '),\n value,\n color: CHART_COLORS[idx % CHART_COLORS.length],\n }))\n .sort((a, b) => b.value - a.value);\n const total = rows.reduce((s, r) => s + r.value, 0);\n return { rows, total };\n }, [allInvestments]);\n\n return (\n <PageLayout\n title=\"Investments\"\n description=\"Track your real estate investment portfolio and performance\"\n purpose=\"Follow every real-estate investment you hold in one portfolio — how much you put in, what it is worth now, the returns it has paid, and its risk level. Use it to judge performance and decide where to put your next dollar.\"\n actions={\n <button\n onClick={openCreate}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg-hover\"\n >\n <Plus className=\"h-4 w-4\" />\n New Investment\n </button>\n }\n >\n {/* Portfolio Summary Stats — primary insight zone (one EmphasisPanel per page) */}\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n label=\"Total Invested\"\n value={portfolioStats.totalInvested}\n prefix=\"$\"\n icon={<DollarSign className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Current Value\"\n value={portfolioStats.currentValue}\n prefix=\"$\"\n icon={<TrendingUp className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Total Returns\"\n value={portfolioStats.totalReturns}\n prefix=\"$\"\n icon={<PieChart className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Active Investments\"\n value={portfolioStats.activeCount}\n icon={<BarChart3 className=\"h-4 w-4\" />}\n />\n </div>\n </EmphasisPanel>\n\n <NextSteps\n storageKey=\"housingvista-investments\"\n steps={[\n {\n id: 'review-active',\n label: 'Review your active investments',\n description: 'Filter to the active deals currently paying returns.',\n onClick: () => handleTabChange('active'),\n },\n {\n id: 'find-funding',\n label: 'Find funding opportunities',\n description: 'See deals still raising capital that you can join.',\n onClick: () => handleTabChange('funding'),\n },\n {\n id: 'open-detail',\n label: 'Open an investment for full details',\n description: 'Drill into returns, investors, and risk for any holding.',\n onClick: () => {\n const first = investments[0];\n if (first) navigate(`../investments/${first.id}`);\n },\n },\n ]}\n />\n\n {/* Filter Tabs */}\n <div className=\"flex flex-wrap items-center gap-2\">\n {statusTabs.map((tab) => (\n <button\n key={tab.value}\n onClick={() => handleTabChange(tab.value)}\n className={cn(\n 'rounded-md px-3 py-1.5 text-sm font-medium transition-colors',\n activeTab === tab.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {tab.label}\n </button>\n ))}\n </div>\n\n {/* Search */}\n <FilterBar\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder=\"Search by property or investment title...\"\n />\n\n {/* Results count */}\n <p className=\"text-sm text-text-secondary\">\n Showing {investments.length} {investments.length === 1 ? 'investment' : 'investments'} on this page\n </p>\n\n {/* Investment list (left) + portfolio allocation donut (aside) */}\n <div className=\"grid grid-cols-1 gap-6 xl:grid-cols-3\">\n <div className=\"space-y-4 xl:col-span-2\">\n {investments.length > 0 ? (\n <>\n <div className=\"space-y-4\">\n {investments.map((investment) => {\n const propertyTitle = investment.property?.title ?? 'Untitled property';\n const totalInvested = sumField(investment.investors, 'investmentAmount');\n const totalReturns = sumField(investment.returns, 'amount');\n const currentValue = totalInvested + totalReturns;\n const returnPercentage = totalInvested > 0 ? (totalReturns / totalInvested) * 100 : 0;\n const returnsCount = safeArray(investment.returns).length;\n\n return (\n <div\n key={investment.id}\n className=\"cursor-pointer rounded-lg border border-border-subtle bg-bg-surface p-5 shadow-sm transition-shadow hover:shadow-md\"\n onClick={() => navigate(`../investments/${investment.id}`)}\n >\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex-1\">\n <div className=\"flex items-start gap-3\">\n <div className=\"rounded-lg bg-bg-sunken p-2\">\n <Building className=\"h-5 w-5 text-text-secondary\" />\n </div>\n <div className=\"flex-1\">\n <div className=\"mb-2 flex flex-wrap items-center gap-2\">\n <h3 className=\"text-lg font-semibold text-text-primary\">{propertyTitle}</h3>\n <StatusBadge label={investment.status} variant={getStatusVariant(investment.status)} />\n <StatusBadge label={investment.investmentType.replace('_', ' ')} variant=\"muted\" />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 text-sm sm:grid-cols-2 lg:grid-cols-4\">\n <div>\n <p className=\"text-text-secondary\">Invested Amount</p>\n <p className=\"text-lg font-medium text-text-primary\">\n {formatCurrency(totalInvested)}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Current Value</p>\n <p className=\"text-lg font-medium text-text-primary\">\n {formatCurrency(currentValue)}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Total Return</p>\n <div className={cn('font-medium', getReturnColor(totalReturns))}>\n <div className=\"flex items-center gap-1\">\n {totalReturns > 0 && <ArrowUpRight className=\"h-3 w-3\" />}\n {totalReturns < 0 && <ArrowDownRight className=\"h-3 w-3\" />}\n {formatCurrency(totalReturns)}\n </div>\n <div className=\"text-sm\">{returnPercentage.toFixed(1)}%</div>\n </div>\n </div>\n <div>\n <p className=\"text-text-secondary\">Risk Level</p>\n <div className=\"mt-1\">\n <StatusBadge\n label={`${investment.riskLevel} risk`}\n variant={riskBadgeVariant(investment.riskLevel)}\n dot\n />\n </div>\n </div>\n </div>\n\n <div className=\"mt-3 flex flex-wrap items-center gap-5 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <TrendingUp className=\"h-3 w-3\" />\n Start: {formatDateSafe(investment.startDate)}\n </span>\n <span className=\"flex items-center gap-1\">\n <Target className=\"h-3 w-3\" />\n Type: {investment.investmentType.replace('_', ' ')}\n </span>\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n Returns: {returnsCount} payments\n </span>\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"flex shrink-0 gap-2\">\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(`../investments/${investment.id}`);\n }}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Eye className=\"h-4 w-4\" />\n View Details\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n <Pagination\n page={pagination.page}\n pageSize={pagination.pageSize}\n hasNextPage={investments.length >= pagination.pageSize}\n hasPreviousPage={pagination.page > 1}\n onPageChange={pagination.setPage}\n onPageSizeChange={pagination.setPageSize}\n />\n </>\n ) : (\n <EmptyState\n illustration=\"empty-search\"\n icon={<PieChart className=\"h-12 w-12\" />}\n title=\"No investments found\"\n description=\"Try adjusting your search criteria or start a new investment.\"\n action={\n <button\n onClick={openCreate}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg-hover\"\n >\n <Plus className=\"h-4 w-4\" />\n New Investment\n </button>\n }\n />\n )}\n </div>\n\n {/* Portfolio Allocation — real value split by investment type */}\n {allocation.rows.length > 0 && (\n <aside className=\"xl:col-span-1\">\n <ChartCard title=\"Portfolio Allocation\" description=\"Current value by investment type\">\n <div className=\"relative h-52 w-full\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <RePieChart>\n <Pie\n data={allocation.rows}\n dataKey=\"value\"\n nameKey=\"label\"\n innerRadius={56}\n outerRadius={82}\n paddingAngle={2}\n stroke=\"var(--color-bg-surface)\"\n strokeWidth={2}\n >\n {allocation.rows.map((entry) => (\n <Cell key={entry.type} fill={entry.color} />\n ))}\n </Pie>\n <Tooltip\n contentStyle={tooltipContentStyle}\n formatter={(value: number, _n, item) => [formatCurrency(value), item?.payload?.label]}\n />\n </RePieChart>\n </ResponsiveContainer>\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center\">\n <span className=\"text-xs text-text-secondary\">Total value</span>\n <span className=\"text-lg font-bold text-text-primary\">{formatCurrency(allocation.total)}</span>\n </div>\n </div>\n <ul className=\"mt-4 space-y-2.5\">\n {allocation.rows.map((entry) => (\n <li key={entry.type} className=\"flex items-center justify-between text-sm\">\n <span className=\"flex items-center gap-2\">\n <span className=\"h-2.5 w-2.5 rounded-full\" style={{ background: entry.color }} />\n <span className=\"text-text-primary\">{entry.label}</span>\n </span>\n <span className=\"font-medium text-text-secondary\">{formatCurrency(entry.value)}</span>\n </li>\n ))}\n </ul>\n </ChartCard>\n </aside>\n )}\n </div>\n\n {/* Create Investment dialog */}\n <ResponsiveDialog open={createOpen} onOpenChange={setCreateOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleCreate}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New investment</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Register a new holding against one of your properties.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Property *</label>\n <select\n required\n value={propertyId}\n onChange={(e) => setPropertyId(e.target.value)}\n className={inputClass}\n >\n <option value=\"\" disabled>\n {properties.length > 0 ? 'Select a property' : 'No properties available'}\n </option>\n {properties.map((property) => (\n <option key={property.id} value={property.id}>\n {property.title}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Title *</label>\n <input\n type=\"text\"\n required\n placeholder=\"e.g. Riverside Villa fractional stake\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Type</label>\n <select\n value={investmentType}\n onChange={(e) => setInvestmentType(e.target.value as InvestmentType)}\n className={inputClass}\n >\n {investmentTypeOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Status</label>\n <select\n value={status}\n onChange={(e) => setStatus(e.target.value as InvestmentStatus)}\n className={inputClass}\n >\n {statusOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Risk level</label>\n <select\n value={riskLevel}\n onChange={(e) => setRiskLevel(e.target.value as 'low' | 'medium' | 'high')}\n className={inputClass}\n >\n {riskOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Total value (USD) *</label>\n <input\n type=\"number\"\n required\n min={1}\n step=\"any\"\n placeholder=\"0\"\n value={totalValue}\n onChange={(e) => setTotalValue(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Start date</label>\n <input\n type=\"date\"\n value={startDate}\n onChange={(e) => setStartDate(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Maturity date</label>\n <input\n type=\"date\"\n value={maturityDate}\n onChange={(e) => setMaturityDate(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setCreateOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={creating || !propertyId || !title.trim() || !totalValue}>\n {creating ? 'Creating…' : 'Create investment'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}