@burdenoff/microfe-brainyrich 2026.706.4 → 2026.713.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.
- package/dist/brainyrich/BrainyRichRoot.d.ts +5 -0
- package/dist/brainyrich/BrainyRichRoot.d.ts.map +1 -0
- package/dist/brainyrich/BrainyRichRoutes.d.ts +2 -0
- package/dist/brainyrich/BrainyRichRoutes.d.ts.map +1 -0
- package/dist/brainyrich/BrainyRichRoutes.js +27 -15
- package/dist/brainyrich/BrainyRichRoutes.js.map +1 -1
- package/dist/brainyrich/components/ActorName.d.ts +26 -0
- package/dist/brainyrich/components/ActorName.d.ts.map +1 -0
- package/dist/brainyrich/components/ConfirmDialog.d.ts +20 -0
- package/dist/brainyrich/components/ConfirmDialog.d.ts.map +1 -0
- package/dist/brainyrich/components/LoadMore.d.ts +21 -0
- package/dist/brainyrich/components/LoadMore.d.ts.map +1 -0
- package/dist/brainyrich/components/index.d.ts +11 -0
- package/dist/brainyrich/components/index.d.ts.map +1 -0
- package/dist/brainyrich/components/notify.d.ts +33 -0
- package/dist/brainyrich/components/notify.d.ts.map +1 -0
- package/dist/brainyrich/components/pickers.d.ts +63 -0
- package/dist/brainyrich/components/pickers.d.ts.map +1 -0
- package/dist/brainyrich/components/reuse/CommentThread.d.ts +35 -0
- package/dist/brainyrich/components/reuse/CommentThread.d.ts.map +1 -0
- package/dist/brainyrich/components/reuse/DocumentsPanel.d.ts +33 -0
- package/dist/brainyrich/components/reuse/DocumentsPanel.d.ts.map +1 -0
- package/dist/brainyrich/components/reuse/TagField.d.ts +34 -0
- package/dist/brainyrich/components/reuse/TagField.d.ts.map +1 -0
- package/dist/brainyrich/components/reuse/transport.d.ts +20 -0
- package/dist/brainyrich/components/reuse/transport.d.ts.map +1 -0
- package/dist/brainyrich/components/viz.d.ts +129 -0
- package/dist/brainyrich/components/viz.d.ts.map +1 -0
- package/dist/brainyrich/components/viz.js.map +1 -1
- package/dist/brainyrich/hooks/useBrainyRichAnalytics.d.ts +17 -0
- package/dist/brainyrich/hooks/useBrainyRichAnalytics.d.ts.map +1 -0
- package/dist/brainyrich/hooks/useBrainyRichAnalytics.js +24 -0
- package/dist/brainyrich/hooks/useBrainyRichAnalytics.js.map +1 -0
- package/dist/brainyrich/hooks/useBrainyRichApi.d.ts +2125 -0
- package/dist/brainyrich/hooks/useBrainyRichApi.d.ts.map +1 -0
- package/dist/brainyrich/hooks/useBrainyRichApi.js +1 -1
- package/dist/brainyrich/hooks/useBrainyRichApi.js.map +1 -1
- package/dist/brainyrich/index.d.ts +6 -0
- package/dist/brainyrich/index.d.ts.map +1 -0
- package/dist/brainyrich/index.js +1 -0
- package/dist/brainyrich/pages/AccountsPage.d.ts +2 -0
- package/dist/brainyrich/pages/AccountsPage.d.ts.map +1 -0
- package/dist/brainyrich/pages/AccountsPage.js +608 -593
- package/dist/brainyrich/pages/AccountsPage.js.map +1 -1
- package/dist/brainyrich/pages/AiAssistantPage.d.ts +2 -0
- package/dist/brainyrich/pages/AiAssistantPage.d.ts.map +1 -0
- package/dist/brainyrich/pages/AnalyticsPage.d.ts +2 -0
- package/dist/brainyrich/pages/AnalyticsPage.d.ts.map +1 -0
- package/dist/brainyrich/pages/DashboardPage.d.ts +2 -0
- package/dist/brainyrich/pages/DashboardPage.d.ts.map +1 -0
- package/dist/brainyrich/pages/DashboardPage.js +264 -256
- package/dist/brainyrich/pages/DashboardPage.js.map +1 -1
- package/dist/brainyrich/pages/FamilyPage.d.ts +2 -0
- package/dist/brainyrich/pages/FamilyPage.d.ts.map +1 -0
- package/dist/brainyrich/pages/FamilyPage.js +467 -446
- package/dist/brainyrich/pages/FamilyPage.js.map +1 -1
- package/dist/brainyrich/pages/GoalsPage.d.ts +2 -0
- package/dist/brainyrich/pages/GoalsPage.d.ts.map +1 -0
- package/dist/brainyrich/pages/GoalsPage.js +553 -531
- package/dist/brainyrich/pages/GoalsPage.js.map +1 -1
- package/dist/brainyrich/pages/InvestmentsPage.d.ts +2 -0
- package/dist/brainyrich/pages/InvestmentsPage.d.ts.map +1 -0
- package/dist/brainyrich/pages/PortfoliosPage.d.ts +2 -0
- package/dist/brainyrich/pages/PortfoliosPage.d.ts.map +1 -0
- package/dist/brainyrich/pages/PortfoliosPage.js +378 -354
- package/dist/brainyrich/pages/PortfoliosPage.js.map +1 -1
- package/dist/brainyrich/pages/ReportsPage.d.ts +2 -0
- package/dist/brainyrich/pages/ReportsPage.d.ts.map +1 -0
- package/dist/brainyrich/pages/RiskPage.d.ts +2 -0
- package/dist/brainyrich/pages/RiskPage.d.ts.map +1 -0
- package/dist/brainyrich/pages/TaxPage.d.ts +2 -0
- package/dist/brainyrich/pages/TaxPage.d.ts.map +1 -0
- package/dist/brainyrich/pages/_shared.d.ts +77 -0
- package/dist/brainyrich/pages/_shared.d.ts.map +1 -0
- package/dist/brainyrich/providers/BrainyRichProvider.d.ts +6 -0
- package/dist/brainyrich/providers/BrainyRichProvider.d.ts.map +1 -0
- package/dist/brainyrich/services/createSandboxAssistantTransport.d.ts +24 -0
- package/dist/brainyrich/services/createSandboxAssistantTransport.d.ts.map +1 -0
- package/dist/brainyrich/services/createSandboxAssistantTransport.js +14 -0
- package/dist/brainyrich/services/createSandboxAssistantTransport.js.map +1 -0
- package/dist/brainyrich/types/bra.d.ts +394 -0
- package/dist/brainyrich/types/bra.d.ts.map +1 -0
- package/dist/brainyrich/types/index.d.ts +39 -0
- package/dist/brainyrich/types/index.d.ts.map +1 -0
- package/dist/brainyrich/utils/cn.d.ts +4 -0
- package/dist/brainyrich/utils/cn.d.ts.map +1 -0
- package/dist/brainyrich/utils/i18n.d.ts +6 -0
- package/dist/brainyrich/utils/i18n.d.ts.map +1 -0
- package/dist/brainyrich/utils/nativeControls.d.ts +12 -0
- package/dist/brainyrich/utils/nativeControls.d.ts.map +1 -0
- package/dist/brainyrich/utils/nativeControls.js +12 -0
- package/dist/brainyrich/utils/nativeControls.js.map +1 -0
- package/dist/brainyrich/utils/navigation.d.ts +2 -0
- package/dist/brainyrich/utils/navigation.d.ts.map +1 -0
- package/dist/brainyrich/utils/pageContext.d.ts +30 -0
- package/dist/brainyrich/utils/pageContext.d.ts.map +1 -0
- package/dist/brainyrich/utils/pageContext.js +46 -0
- package/dist/brainyrich/utils/pageContext.js.map +1 -0
- package/dist/dev/main.d.ts +2 -0
- package/dist/dev/main.d.ts.map +1 -0
- package/dist/generated/global-operations.d.ts +2 -0
- package/dist/generated/global-operations.d.ts.map +1 -0
- package/dist/generated/global-types.d.ts +18199 -0
- package/dist/generated/global-types.d.ts.map +1 -0
- package/dist/generated/wspace-operations.d.ts +2 -0
- package/dist/generated/wspace-operations.d.ts.map +1 -0
- package/dist/generated/wspace-types.d.ts +43046 -0
- package/dist/generated/wspace-types.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -1
- package/package.json +6 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PortfoliosPage.js","names":[],"sources":["../../../src/brainyrich/pages/PortfoliosPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport {\n ArrowDownRight,\n ArrowUpRight,\n Briefcase,\n MoreHorizontal,\n Pencil,\n Plus,\n Trash2,\n} from 'lucide-react';\n\nimport {\n useArchivePortfolio,\n useCreateHolding,\n useCreatePortfolio,\n useDeleteHolding,\n useHouseholds,\n useModelPortfolios,\n usePagedPortfolios,\n usePortfolio,\n useUpdateHolding,\n useUpdatePortfolio,\n} from '../hooks/useBrainyRichApi';\nimport {\n AccountPicker,\n ConfirmDialog,\n DonutChart,\n FadeIn,\n GlassCard,\n LoadMore,\n StatTile,\n type DonutDatum,\n type VizTone,\n} from '../components';\nimport { CommentThread } from '../components/reuse/CommentThread';\nimport { TagField } from '../components/reuse/TagField';\nimport type { BraAssetClass, BraHolding, BraPortfolio, BraPortfolioStatus } from '../types/bra';\nimport {\n AllocationBars,\n EmptyState,\n ErrorState,\n LoadingState,\n SectionShell,\n formatCompact,\n formatMoney,\n formatPct,\n statusBadgeVariant,\n type AllocationDatum,\n} from './_shared';\n\nconst ASSET_CLASSES: BraAssetClass[] = [\n 'EQUITY',\n 'FIXED_INCOME',\n 'CASH',\n 'REAL_ESTATE',\n 'COMMODITY',\n 'CRYPTO',\n 'ALTERNATIVE',\n];\n\nconst PORTFOLIO_STATUSES: BraPortfolioStatus[] = ['DRAFT', 'ACTIVE', 'ARCHIVED'];\n\nconst NONE = '__none__';\n\nconst prettyEnum = (value: string): string =>\n value\n .toLowerCase()\n .split('_')\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n\nconst toNumber = (value: string): number | undefined => {\n if (value.trim() === '') return undefined;\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n};\n\n/** Stable, semantic tone per asset class so donut/legend colours read consistently. */\nconst ASSET_TONE: Record<string, VizTone> = {\n EQUITY: 'brand',\n FIXED_INCOME: 'blue',\n CASH: 'teal',\n REAL_ESTATE: 'purple',\n COMMODITY: 'warning',\n CRYPTO: 'indigo',\n ALTERNATIVE: 'info',\n};\n\nconst toneForClass = (raw: string): VizTone => ASSET_TONE[raw.toUpperCase()] ?? 'neutral';\n\n/** Read a `targetAllocation` JSON blob as an assetClass → percentage map. */\nfunction parseTargetAllocation(raw: unknown): AllocationDatum[] {\n if (!raw || typeof raw !== 'object') return [];\n return Object.entries(raw as Record<string, unknown>)\n .map(([label, value]) => ({ label, value: Number(value) }))\n .filter((d) => Number.isFinite(d.value))\n .map((d) => ({ label: prettyEnum(d.label), value: d.value, tone: 'accent' as const }));\n}\n\n/** Read a `targetAllocation` JSON blob as tone-keyed donut slices (percent values). */\nfunction targetAllocationDonut(raw: unknown): DonutDatum[] {\n if (!raw || typeof raw !== 'object') return [];\n return Object.entries(raw as Record<string, unknown>)\n .map(([key, value]) => ({ key, value: Number(value) }))\n .filter((d) => Number.isFinite(d.value) && d.value > 0)\n .sort((a, b) => b.value - a.value)\n .map((d) => ({ label: prettyEnum(d.key), value: d.value, tone: toneForClass(d.key) }));\n}\n\n// ============================================================================\n// Gain cell — signed money with icon + semantic color (color never sole signal)\n// ============================================================================\n\nfunction GainValue({ value, currency }: { value: number; currency: string }) {\n const positive = value > 0;\n const negative = value < 0;\n const tone = positive\n ? 'text-status-success-text'\n : negative\n ? 'text-status-error-text'\n : 'text-text-secondary';\n const Icon = positive ? ArrowUpRight : negative ? ArrowDownRight : null;\n return (\n <span className={`inline-flex items-center justify-end gap-1 font-medium ${tone}`}>\n {Icon ? <Icon className=\"size-3.5\" aria-hidden=\"true\" /> : null}\n {positive ? '+' : ''}\n {formatMoney(value, currency)}\n </span>\n );\n}\n\n// ============================================================================\n// Portfolio create / edit dialog\n// ============================================================================\n\ninterface PortfolioFormState {\n name: string;\n baseCurrency: string;\n description: string;\n driftThresholdPct: string;\n status: BraPortfolioStatus;\n modelPortfolioId: string;\n householdId: string;\n}\n\nfunction emptyForm(): PortfolioFormState {\n return {\n name: '',\n baseCurrency: 'USD',\n description: '',\n driftThresholdPct: '5',\n status: 'DRAFT',\n modelPortfolioId: NONE,\n householdId: NONE,\n };\n}\n\nfunction PortfolioFormDialog({\n open,\n onOpenChange,\n editing,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n editing: BraPortfolio | null;\n}) {\n const { data: models } = useModelPortfolios();\n const { data: households } = useHouseholds();\n const createPortfolio = useCreatePortfolio();\n const updatePortfolio = useUpdatePortfolio();\n\n const [form, setForm] = useState<PortfolioFormState>(emptyForm);\n // Re-seed the form whenever the dialog target changes.\n const [seededFor, setSeededFor] = useState<string | null>(null);\n const targetKey = editing?.id ?? 'new';\n if (open && seededFor !== targetKey) {\n setSeededFor(targetKey);\n setForm(\n editing\n ? {\n name: editing.name,\n baseCurrency: editing.baseCurrency,\n description: editing.description ?? '',\n driftThresholdPct: String(editing.driftThresholdPct),\n status: editing.status,\n modelPortfolioId: editing.modelPortfolioId ?? NONE,\n householdId: editing.householdId ?? NONE,\n }\n : emptyForm()\n );\n }\n if (!open && seededFor !== null) {\n setSeededFor(null);\n }\n\n const pending = createPortfolio.isPending || updatePortfolio.isPending;\n const nameValid = form.name.trim().length > 0;\n\n const handleSubmit = () => {\n if (!nameValid) return;\n const drift = toNumber(form.driftThresholdPct);\n const modelPortfolioId = form.modelPortfolioId === NONE ? null : form.modelPortfolioId;\n const householdId = form.householdId === NONE ? null : form.householdId;\n\n if (editing) {\n updatePortfolio.mutate(\n {\n id: editing.id,\n input: {\n name: form.name.trim(),\n baseCurrency: form.baseCurrency.trim() || 'USD',\n description: form.description.trim() || null,\n driftThresholdPct: drift,\n status: form.status,\n modelPortfolioId,\n },\n },\n { onSuccess: () => onOpenChange(false) }\n );\n } else {\n createPortfolio.mutate(\n {\n name: form.name.trim(),\n baseCurrency: form.baseCurrency.trim() || 'USD',\n description: form.description.trim() || null,\n driftThresholdPct: drift,\n modelPortfolioId,\n householdId,\n },\n { onSuccess: () => onOpenChange(false) }\n );\n }\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{editing ? 'Edit portfolio' : 'New portfolio'}</DialogTitle>\n <DialogDescription>\n {editing\n ? 'Update this portfolio’s strategy settings.'\n : 'Create an investment portfolio to organize and rebalance holdings.'}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-name\">Name</Label>\n <Input\n id=\"pf-name\"\n value={form.name}\n onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}\n placeholder=\"Growth Portfolio\"\n aria-invalid={!nameValid}\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-currency\">Base currency</Label>\n <Input\n id=\"pf-currency\"\n value={form.baseCurrency}\n onChange={(e) =>\n setForm((f) => ({ ...f, baseCurrency: e.target.value.toUpperCase() }))\n }\n placeholder=\"USD\"\n maxLength={3}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-drift\">Drift threshold %</Label>\n <Input\n id=\"pf-drift\"\n type=\"number\"\n min={0}\n step=\"0.5\"\n value={form.driftThresholdPct}\n onChange={(e) => setForm((f) => ({ ...f, driftThresholdPct: e.target.value }))}\n />\n </div>\n </div>\n\n {editing ? (\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-status\">Status</Label>\n <Select\n value={form.status}\n onValueChange={(v) => setForm((f) => ({ ...f, status: v as BraPortfolioStatus }))}\n >\n <SelectTrigger id=\"pf-status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PORTFOLIO_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {prettyEnum(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-model\">Model portfolio</Label>\n <Select\n value={form.modelPortfolioId}\n onValueChange={(v) => setForm((f) => ({ ...f, modelPortfolioId: v }))}\n >\n <SelectTrigger id=\"pf-model\">\n <SelectValue placeholder=\"None\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NONE}>None</SelectItem>\n {models.map((m) => (\n <SelectItem key={m.id} value={m.id}>\n {m.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {!editing ? (\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-household\">Household</Label>\n <Select\n value={form.householdId}\n onValueChange={(v) => setForm((f) => ({ ...f, householdId: v }))}\n >\n <SelectTrigger id=\"pf-household\">\n <SelectValue placeholder=\"None\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NONE}>None</SelectItem>\n {households.map((h) => (\n <SelectItem key={h.id} value={h.id}>\n {h.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-desc\">Description</Label>\n <Textarea\n id=\"pf-desc\"\n value={form.description}\n onChange={(e) => setForm((f) => ({ ...f, description: e.target.value }))}\n placeholder=\"Optional notes about this strategy\"\n rows={3}\n />\n </div>\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={pending}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!nameValid || pending}>\n {pending ? 'Saving…' : editing ? 'Save changes' : 'Create portfolio'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n\n// ============================================================================\n// Holding create / edit dialog (scoped to a portfolio)\n// ============================================================================\n\ninterface HoldingFormState {\n accountId: string;\n symbol: string;\n name: string;\n assetClass: BraAssetClass;\n quantity: string;\n lastPrice: string;\n costBasis: string;\n}\n\nfunction emptyHoldingForm(): HoldingFormState {\n return {\n accountId: '',\n symbol: '',\n name: '',\n assetClass: 'EQUITY',\n quantity: '0',\n lastPrice: '0',\n costBasis: '0',\n };\n}\n\nfunction HoldingFormDialog({\n open,\n onOpenChange,\n portfolioId,\n editing,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n portfolioId: string;\n editing: BraHolding | null;\n}) {\n const createHolding = useCreateHolding();\n const updateHolding = useUpdateHolding();\n\n const [form, setForm] = useState<HoldingFormState>(emptyHoldingForm);\n const [seededFor, setSeededFor] = useState<string | null>(null);\n const targetKey = editing?.id ?? 'new';\n if (open && seededFor !== targetKey) {\n setSeededFor(targetKey);\n setForm(\n editing\n ? {\n accountId: editing.accountId,\n symbol: editing.symbol,\n name: editing.name ?? '',\n assetClass: editing.assetClass,\n quantity: String(editing.quantity),\n lastPrice: String(editing.lastPrice),\n costBasis: String(editing.costBasis),\n }\n : emptyHoldingForm()\n );\n }\n if (!open && seededFor !== null) {\n setSeededFor(null);\n }\n\n const pending = createHolding.isPending || updateHolding.isPending;\n const symbolValid = form.symbol.trim().length > 0;\n const accountValid = editing ? true : form.accountId.trim().length > 0;\n const canSubmit = symbolValid && accountValid && !pending;\n\n const handleSubmit = () => {\n if (!symbolValid || !accountValid) return;\n if (editing) {\n updateHolding.mutate(\n {\n id: editing.id,\n input: {\n name: form.name.trim() || null,\n assetClass: form.assetClass,\n quantity: toNumber(form.quantity),\n lastPrice: toNumber(form.lastPrice),\n costBasis: toNumber(form.costBasis),\n },\n },\n { onSuccess: () => onOpenChange(false) }\n );\n } else {\n createHolding.mutate(\n {\n accountId: form.accountId,\n portfolioId,\n symbol: form.symbol.trim().toUpperCase(),\n name: form.name.trim() || null,\n assetClass: form.assetClass,\n quantity: toNumber(form.quantity),\n lastPrice: toNumber(form.lastPrice),\n costBasis: toNumber(form.costBasis),\n },\n { onSuccess: () => onOpenChange(false) }\n );\n }\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{editing ? 'Edit holding' : 'Add holding'}</DialogTitle>\n <DialogDescription>\n {editing\n ? 'Update the position details for this holding.'\n : 'Add a position to this portfolio from one of your accounts.'}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {!editing ? (\n <AccountPicker\n id=\"h-account\"\n value={form.accountId}\n onChange={(v) => setForm((f) => ({ ...f, accountId: v }))}\n placeholder=\"Select account\"\n />\n ) : null}\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-symbol\">Symbol</Label>\n <Input\n id=\"h-symbol\"\n value={form.symbol}\n onChange={(e) => setForm((f) => ({ ...f, symbol: e.target.value }))}\n placeholder=\"AAPL\"\n disabled={Boolean(editing)}\n aria-invalid={!symbolValid}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-class\">Asset class</Label>\n <Select\n value={form.assetClass}\n onValueChange={(v) => setForm((f) => ({ ...f, assetClass: v as BraAssetClass }))}\n >\n <SelectTrigger id=\"h-class\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ASSET_CLASSES.map((c) => (\n <SelectItem key={c} value={c}>\n {prettyEnum(c)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-name\">Name</Label>\n <Input\n id=\"h-name\"\n value={form.name}\n onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}\n placeholder=\"Apple Inc.\"\n />\n </div>\n\n <div className=\"grid grid-cols-3 gap-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-qty\">Quantity</Label>\n <Input\n id=\"h-qty\"\n type=\"number\"\n step=\"any\"\n value={form.quantity}\n onChange={(e) => setForm((f) => ({ ...f, quantity: e.target.value }))}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-price\">Last price</Label>\n <Input\n id=\"h-price\"\n type=\"number\"\n step=\"any\"\n value={form.lastPrice}\n onChange={(e) => setForm((f) => ({ ...f, lastPrice: e.target.value }))}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-cost\">Cost basis</Label>\n <Input\n id=\"h-cost\"\n type=\"number\"\n step=\"any\"\n value={form.costBasis}\n onChange={(e) => setForm((f) => ({ ...f, costBasis: e.target.value }))}\n />\n </div>\n </div>\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={pending}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!canSubmit}>\n {pending ? 'Saving…' : editing ? 'Save changes' : 'Add holding'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n\n// ============================================================================\n// Portfolio detail sheet — holdings table + target vs actual allocation\n// ============================================================================\n\nfunction PortfolioDetailSheet({\n portfolioId,\n onOpenChange,\n}: {\n portfolioId: string | null;\n onOpenChange: (open: boolean) => void;\n}) {\n const { data: portfolio, isLoading, isError, refetch } = usePortfolio(portfolioId ?? undefined);\n const deleteHolding = useDeleteHolding();\n\n const [holdingDialogOpen, setHoldingDialogOpen] = useState(false);\n const [editingHolding, setEditingHolding] = useState<BraHolding | null>(null);\n const [holdingToDelete, setHoldingToDelete] = useState<BraHolding | null>(null);\n\n const holdings = portfolio?.holdings ?? [];\n const currency = portfolio?.baseCurrency ?? 'USD';\n const totalMarketValue = holdings.reduce((s, h) => s + h.marketValue, 0);\n const totalCostBasis = holdings.reduce((s, h) => s + h.costBasis, 0);\n const totalGain = totalMarketValue - totalCostBasis;\n const gainPct = totalCostBasis > 0 ? (totalGain / totalCostBasis) * 100 : 0;\n\n /** Live allocation by class in base-currency value — tone-keyed donut slices. */\n const actualDonut: DonutDatum[] = useMemo(() => {\n if (holdings.length === 0 || totalMarketValue <= 0) return [];\n const byClass = new Map<string, number>();\n holdings.forEach((h) =>\n byClass.set(h.assetClass, (byClass.get(h.assetClass) ?? 0) + h.marketValue)\n );\n return Array.from(byClass.entries())\n .map(([cls, mv]) => ({ label: prettyEnum(cls), value: mv, tone: toneForClass(cls) }))\n .sort((a, b) => b.value - a.value);\n }, [holdings, totalMarketValue]);\n\n /** Actual allocation as percentages, keyed by pretty class label, for drift comparison. */\n const actualPctByLabel = useMemo(() => {\n const map = new Map<string, number>();\n if (totalMarketValue <= 0) return map;\n holdings.forEach((h) =>\n map.set(\n prettyEnum(h.assetClass),\n (map.get(prettyEnum(h.assetClass)) ?? 0) + (h.marketValue / totalMarketValue) * 100\n )\n );\n return map;\n }, [holdings, totalMarketValue]);\n\n /** Target vs actual: each target row captioned with the live actual weight. */\n const targetVsActual: AllocationDatum[] = useMemo(() => {\n return parseTargetAllocation(portfolio?.targetAllocation).map((d) => {\n const actual = actualPctByLabel.get(d.label) ?? 0;\n return {\n label: d.label,\n value: d.value,\n tone: 'accent' as const,\n caption: `${formatPct(d.value, 0)} target · ${formatPct(actual, 0)} actual`,\n };\n });\n }, [portfolio?.targetAllocation, actualPctByLabel]);\n\n const openAddHolding = () => {\n setEditingHolding(null);\n setHoldingDialogOpen(true);\n };\n const openEditHolding = (h: BraHolding) => {\n setEditingHolding(h);\n setHoldingDialogOpen(true);\n };\n\n return (\n <Sheet open={portfolioId !== null} onOpenChange={onOpenChange}>\n <SheetContent className=\"flex w-full flex-col gap-0 overflow-y-auto p-0 sm:max-w-2xl\">\n {isLoading ? (\n <div className=\"p-6\">\n <LoadingState rows={5} />\n </div>\n ) : isError ? (\n <div className=\"p-6\">\n <ErrorState onRetry={() => refetch()} />\n </div>\n ) : !portfolio ? (\n <div className=\"p-6\">\n <EmptyState\n icon={<Briefcase className=\"size-6\" aria-hidden=\"true\" />}\n title=\"Portfolio not found\"\n description=\"This portfolio may have been archived or removed.\"\n />\n </div>\n ) : (\n <>\n <SheetHeader className=\"border-b border-border-subtle p-6\">\n <div className=\"flex items-center gap-2\">\n <SheetTitle className=\"text-xl\">{portfolio.name}</SheetTitle>\n <Badge variant={statusBadgeVariant(portfolio.status)}>\n {prettyEnum(portfolio.status)}\n </Badge>\n </div>\n <SheetDescription>\n {portfolio.description?.trim()\n ? portfolio.description\n : `${portfolio.baseCurrency} · Drift threshold ${formatPct(portfolio.driftThresholdPct)}`}\n </SheetDescription>\n </SheetHeader>\n\n <div className=\"space-y-6 p-6\">\n {/* Hero KPIs — market value carries an emerald glow + live gain delta */}\n <FadeIn className=\"grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <StatTile\n label=\"Market value\"\n value={formatMoney(totalMarketValue, currency)}\n glow\n />\n <StatTile\n label=\"Unrealized gain\"\n value={formatMoney(totalGain, currency)}\n delta={totalCostBasis > 0 ? gainPct : undefined}\n deltaTone={totalGain > 0 ? 'positive' : totalGain < 0 ? 'negative' : 'neutral'}\n />\n <StatTile label=\"Holdings\" value={String(holdings.length)} />\n <StatTile label=\"Drift threshold\" value={formatPct(portfolio.driftThresholdPct)} />\n </FadeIn>\n\n {/* Target vs actual allocation — live donut + drift-aware target bars */}\n <FadeIn index={1}>\n <GlassCard glow className=\"space-y-5\">\n <div className=\"flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n Target vs actual allocation\n </h3>\n <Badge variant=\"outline\">{currency}</Badge>\n </div>\n <div className=\"grid gap-6 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Actual — by market value\n </p>\n {actualDonut.length > 0 ? (\n <DonutChart\n data={actualDonut}\n centerValue={formatCompact(totalMarketValue, currency)}\n centerLabel=\"Market value\"\n valueFormatter={(v) => formatMoney(v, currency)}\n />\n ) : (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n Add holdings to see the live allocation.\n </p>\n )}\n </div>\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Target — vs live actual\n </p>\n {targetVsActual.length > 0 ? (\n <AllocationBars data={targetVsActual} />\n ) : (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n No target allocation set for this portfolio.\n </p>\n )}\n </div>\n </div>\n </GlassCard>\n </FadeIn>\n\n {/* Holdings table */}\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between\">\n <div>\n <CardTitle className=\"text-base\">Holdings</CardTitle>\n <CardDescription>Positions held in this portfolio</CardDescription>\n </div>\n <Button size=\"sm\" onClick={openAddHolding}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n Add holding\n </Button>\n </CardHeader>\n <CardContent>\n {holdings.length === 0 ? (\n <EmptyState\n icon={<Briefcase className=\"size-6\" aria-hidden=\"true\" />}\n title=\"No holdings yet\"\n description=\"Add positions to start tracking this portfolio.\"\n action={\n <Button size=\"sm\" onClick={openAddHolding}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n Add holding\n </Button>\n }\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Symbol</TableHead>\n <TableHead>Class</TableHead>\n <TableHead className=\"text-right\">Qty</TableHead>\n <TableHead className=\"text-right\">Price</TableHead>\n <TableHead className=\"text-right\">Market value</TableHead>\n <TableHead className=\"text-right\">Gain</TableHead>\n <TableHead className=\"w-10\" />\n </TableRow>\n </TableHeader>\n <TableBody>\n {holdings.map((h) => {\n const gain = h.marketValue - h.costBasis;\n const gainPctRow = h.costBasis > 0 ? (gain / h.costBasis) * 100 : 0;\n return (\n <TableRow\n key={h.id}\n className=\"transition-colors motion-safe:hover:bg-bg-sunken/60\"\n >\n <TableCell>\n <div className=\"font-semibold text-text-primary\">{h.symbol}</div>\n {h.name ? (\n <div className=\"text-xs text-text-secondary\">{h.name}</div>\n ) : null}\n </TableCell>\n <TableCell>\n <Badge variant=\"outline\">{prettyEnum(h.assetClass)}</Badge>\n </TableCell>\n <TableCell className=\"text-right tabular-nums\">\n {h.quantity}\n </TableCell>\n <TableCell className=\"text-right tabular-nums\">\n {formatMoney(h.lastPrice, h.currency)}\n </TableCell>\n <TableCell className=\"text-right font-medium tabular-nums\">\n {formatMoney(h.marketValue, h.currency)}\n </TableCell>\n <TableCell className=\"text-right tabular-nums\">\n <GainValue value={gain} currency={h.currency} />\n {h.costBasis > 0 ? (\n <div className=\"text-xs text-text-secondary\">\n {gain > 0 ? '+' : ''}\n {formatPct(gainPctRow)}\n </div>\n ) : null}\n </TableCell>\n <TableCell className=\"text-right\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-10\"\n aria-label={`Actions for ${h.symbol}`}\n >\n <MoreHorizontal className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuLabel>{h.symbol}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"gap-2\"\n onClick={() => openEditHolding(h)}\n >\n <Pencil className=\"size-4\" aria-hidden=\"true\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuItem\n className=\"gap-2 text-status-error-text focus:text-status-error-text\"\n onClick={() => setHoldingToDelete(h)}\n >\n <Trash2 className=\"size-4\" aria-hidden=\"true\" />\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Tags — real UniversalTag assignments keyed by entity */}\n <Card>\n <CardHeader className=\"pb-3\">\n <CardTitle className=\"text-sm\">Tags</CardTitle>\n <CardDescription>Organize and filter this portfolio.</CardDescription>\n </CardHeader>\n <CardContent>\n <TagField entityType=\"brainyrich-portfolio\" entityId={portfolio.id} hideLabel />\n </CardContent>\n </Card>\n\n {/* Discussion — threaded comments keyed by entity */}\n <Card>\n <CardContent className=\"pt-6\">\n <CommentThread entityType=\"brainyrich-portfolio\" entityId={portfolio.id} />\n </CardContent>\n </Card>\n </div>\n\n <HoldingFormDialog\n open={holdingDialogOpen}\n onOpenChange={setHoldingDialogOpen}\n portfolioId={portfolio.id}\n editing={editingHolding}\n />\n\n <ConfirmDialog\n open={holdingToDelete !== null}\n onOpenChange={(open) => {\n if (!open) setHoldingToDelete(null);\n }}\n title=\"Delete holding?\"\n description={\n holdingToDelete\n ? `Remove ${holdingToDelete.symbol} from this portfolio. This cannot be undone.`\n : undefined\n }\n confirmLabel=\"Delete\"\n pendingLabel=\"Deleting…\"\n destructive\n pending={deleteHolding.isPending}\n onConfirm={() => {\n if (!holdingToDelete) return;\n deleteHolding.mutate(holdingToDelete.id, {\n onSuccess: () => setHoldingToDelete(null),\n });\n }}\n />\n </>\n )}\n </SheetContent>\n </Sheet>\n );\n}\n\n// ============================================================================\n// Portfolio card — allocation donut + drift gauge (list grid)\n// ============================================================================\n\nfunction PortfolioCard({\n portfolio: p,\n index,\n onView,\n onEdit,\n onArchive,\n}: {\n portfolio: BraPortfolio;\n index: number;\n onView: () => void;\n onEdit: () => void;\n onArchive: () => void;\n}) {\n const donut = useMemo(() => targetAllocationDonut(p.targetAllocation), [p.targetAllocation]);\n const top = donut[0];\n const tags = p.tags ?? [];\n\n return (\n <FadeIn index={index} as=\"div\" className=\"h-full\">\n <Card className=\"flex h-full flex-col transition-shadow motion-safe:hover:shadow-[var(--shadow-pop-hover,var(--shadow-elevation-2))]\">\n <CardHeader className=\"pb-3\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-[var(--color-action-primary-bg)]/12 text-[var(--color-action-primary-text)]\">\n <Briefcase className=\"size-4\" aria-hidden=\"true\" />\n </span>\n <CardTitle className=\"truncate text-base\">{p.name}</CardTitle>\n <Badge variant={statusBadgeVariant(p.status)}>{prettyEnum(p.status)}</Badge>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-9 shrink-0\"\n aria-label={`Actions for ${p.name}`}\n >\n <MoreHorizontal className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuLabel>{p.name}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem className=\"gap-2\" onClick={onEdit}>\n <Pencil className=\"size-4\" aria-hidden=\"true\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuItem\n className=\"gap-2 text-status-error-text focus:text-status-error-text\"\n disabled={p.status === 'ARCHIVED'}\n onClick={onArchive}\n >\n <Trash2 className=\"size-4\" aria-hidden=\"true\" />\n Archive\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n {p.description?.trim() ? (\n <CardDescription className=\"line-clamp-2\">{p.description}</CardDescription>\n ) : null}\n </CardHeader>\n\n <CardContent className=\"flex flex-1 flex-col justify-between gap-4\">\n <div className=\"grid grid-cols-[auto_1fr] items-center gap-4\">\n {donut.length > 0 ? (\n <div className=\"w-28 shrink-0\">\n <DonutChart\n data={donut}\n legend={false}\n height={112}\n centerValue={top ? formatPct(top.value, 0) : undefined}\n centerLabel={top?.label}\n valueFormatter={(v) => formatPct(v, 0)}\n />\n </div>\n ) : (\n <div className=\"flex size-28 shrink-0 items-center justify-center rounded-full border border-dashed border-border-subtle text-center text-[10px] leading-tight text-text-secondary\">\n No target\n <br />\n allocation\n </div>\n )}\n <div className=\"min-w-0 space-y-2\">\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <Badge variant=\"outline\">{p.baseCurrency}</Badge>\n <Badge variant=\"secondary\">Drift ≤ {formatPct(p.driftThresholdPct)}</Badge>\n </div>\n {donut.length > 0 ? (\n <ul className=\"space-y-1\" aria-label=\"Target allocation\">\n {donut.slice(0, 3).map((d) => (\n <li key={d.label} className=\"flex items-center gap-1.5 text-xs\">\n <span className=\"truncate text-text-secondary\">{d.label}</span>\n <span className=\"ml-auto font-medium tabular-nums text-text-primary\">\n {formatPct(d.value, 0)}\n </span>\n </li>\n ))}\n </ul>\n ) : null}\n {tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {tags.slice(0, 3).map((t) => (\n <Badge key={t} variant=\"outline\" className=\"text-[10px]\">\n {t}\n </Badge>\n ))}\n </div>\n ) : null}\n </div>\n </div>\n <Button variant=\"outline\" size=\"sm\" className=\"w-full\" onClick={onView}>\n View holdings\n </Button>\n </CardContent>\n </Card>\n </FadeIn>\n );\n}\n\n// ============================================================================\n// Page\n// ============================================================================\n\nexport function PortfoliosPage() {\n const {\n items: portfolios,\n hasMore,\n loadMore,\n isLoading,\n isFetchingNextPage,\n isError,\n refetch,\n } = usePagedPortfolios();\n const archivePortfolio = useArchivePortfolio();\n\n const [formOpen, setFormOpen] = useState(false);\n const [editingPortfolio, setEditingPortfolio] = useState<BraPortfolio | null>(null);\n const [detailId, setDetailId] = useState<string | null>(null);\n const [portfolioToArchive, setPortfolioToArchive] = useState<BraPortfolio | null>(null);\n\n const activeCount = portfolios.filter((p) => p.status === 'ACTIVE').length;\n const draftCount = portfolios.filter((p) => p.status === 'DRAFT').length;\n const avgDrift =\n portfolios.length > 0\n ? portfolios.reduce((s, p) => s + p.driftThresholdPct, 0) / portfolios.length\n : 0;\n\n const openCreate = () => {\n setEditingPortfolio(null);\n setFormOpen(true);\n };\n const openEdit = (p: BraPortfolio) => {\n setEditingPortfolio(p);\n setFormOpen(true);\n };\n\n return (\n <SectionShell\n title=\"Portfolios\"\n description=\"Organize holdings into strategies and keep allocations on target.\"\n badges={portfolios.length > 0 ? [`${portfolios.length} total`] : []}\n actions={\n <Button size=\"sm\" onClick={openCreate}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n New portfolio\n </Button>\n }\n >\n {isLoading ? (\n <LoadingState rows={4} />\n ) : isError ? (\n <ErrorState onRetry={() => refetch()} />\n ) : portfolios.length === 0 ? (\n <EmptyState\n icon={<Briefcase className=\"size-6\" aria-hidden=\"true\" />}\n title=\"No portfolios yet\"\n description=\"Create your first portfolio to start organizing holdings and tracking drift.\"\n action={\n <Button size=\"sm\" onClick={openCreate}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n New portfolio\n </Button>\n }\n />\n ) : (\n <>\n <FadeIn className=\"grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <StatTile label=\"Portfolios\" value={String(portfolios.length)} glow />\n <StatTile label=\"Active\" value={String(activeCount)} />\n <StatTile label=\"Drafts\" value={String(draftCount)} />\n <StatTile label=\"Avg drift threshold\" value={formatPct(avgDrift)} />\n </FadeIn>\n\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {portfolios.map((p, i) => (\n <PortfolioCard\n key={p.id}\n portfolio={p}\n index={i}\n onView={() => setDetailId(p.id)}\n onEdit={() => openEdit(p)}\n onArchive={() => setPortfolioToArchive(p)}\n />\n ))}\n </div>\n\n <LoadMore hasMore={hasMore} onClick={loadMore} loading={isFetchingNextPage} />\n </>\n )}\n\n <PortfolioFormDialog open={formOpen} onOpenChange={setFormOpen} editing={editingPortfolio} />\n\n <PortfolioDetailSheet\n portfolioId={detailId}\n onOpenChange={(open) => {\n if (!open) setDetailId(null);\n }}\n />\n\n <ConfirmDialog\n open={portfolioToArchive !== null}\n onOpenChange={(open) => {\n if (!open) setPortfolioToArchive(null);\n }}\n title=\"Archive portfolio?\"\n description={\n portfolioToArchive\n ? `Archive “${portfolioToArchive.name}”. It will move to archived status and stop appearing in active views.`\n : undefined\n }\n confirmLabel=\"Archive\"\n pendingLabel=\"Archiving…\"\n destructive\n pending={archivePortfolio.isPending}\n onConfirm={() => {\n if (!portfolioToArchive) return;\n archivePortfolio.mutate(portfolioToArchive.id, {\n onSuccess: () => setPortfolioToArchive(null),\n });\n }}\n />\n </SectionShell>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PortfoliosPage.js","names":[],"sources":["../../../src/brainyrich/pages/PortfoliosPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport {\n ArrowDownRight,\n ArrowUpRight,\n Briefcase,\n MoreHorizontal,\n Pencil,\n Plus,\n Trash2,\n} from 'lucide-react';\n\nimport {\n useArchivePortfolio,\n useCreateHolding,\n useCreatePortfolio,\n useDeleteHolding,\n useHouseholds,\n useModelPortfolios,\n usePagedPortfolios,\n usePortfolio,\n useUpdateHolding,\n useUpdatePortfolio,\n} from '../hooks/useBrainyRichApi';\nimport {\n AccountPicker,\n ConfirmDialog,\n DonutChart,\n FadeIn,\n GlassCard,\n LoadMore,\n StatTile,\n type DonutDatum,\n type VizTone,\n} from '../components';\nimport { CommentThread } from '../components/reuse/CommentThread';\nimport { TagField } from '../components/reuse/TagField';\nimport { hapticSuccess, showToast } from '../utils/nativeControls';\nimport { useTrackBrainyRichPageView } from '../hooks/useBrainyRichAnalytics';\nimport type { BraAssetClass, BraHolding, BraPortfolio, BraPortfolioStatus } from '../types/bra';\nimport {\n AllocationBars,\n EmptyState,\n ErrorState,\n LoadingState,\n SectionShell,\n formatCompact,\n formatMoney,\n formatPct,\n statusBadgeVariant,\n type AllocationDatum,\n} from './_shared';\n\nconst ASSET_CLASSES: BraAssetClass[] = [\n 'EQUITY',\n 'FIXED_INCOME',\n 'CASH',\n 'REAL_ESTATE',\n 'COMMODITY',\n 'CRYPTO',\n 'ALTERNATIVE',\n];\n\nconst PORTFOLIO_STATUSES: BraPortfolioStatus[] = ['DRAFT', 'ACTIVE', 'ARCHIVED'];\n\nconst NONE = '__none__';\n\nconst prettyEnum = (value: string): string =>\n value\n .toLowerCase()\n .split('_')\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n\nconst toNumber = (value: string): number | undefined => {\n if (value.trim() === '') return undefined;\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n};\n\n/** Stable, semantic tone per asset class so donut/legend colours read consistently. */\nconst ASSET_TONE: Record<string, VizTone> = {\n EQUITY: 'brand',\n FIXED_INCOME: 'blue',\n CASH: 'teal',\n REAL_ESTATE: 'purple',\n COMMODITY: 'warning',\n CRYPTO: 'indigo',\n ALTERNATIVE: 'info',\n};\n\nconst toneForClass = (raw: string): VizTone => ASSET_TONE[raw.toUpperCase()] ?? 'neutral';\n\n/** Read a `targetAllocation` JSON blob as an assetClass → percentage map. */\nfunction parseTargetAllocation(raw: unknown): AllocationDatum[] {\n if (!raw || typeof raw !== 'object') return [];\n return Object.entries(raw as Record<string, unknown>)\n .map(([label, value]) => ({ label, value: Number(value) }))\n .filter((d) => Number.isFinite(d.value))\n .map((d) => ({ label: prettyEnum(d.label), value: d.value, tone: 'accent' as const }));\n}\n\n/** Read a `targetAllocation` JSON blob as tone-keyed donut slices (percent values). */\nfunction targetAllocationDonut(raw: unknown): DonutDatum[] {\n if (!raw || typeof raw !== 'object') return [];\n return Object.entries(raw as Record<string, unknown>)\n .map(([key, value]) => ({ key, value: Number(value) }))\n .filter((d) => Number.isFinite(d.value) && d.value > 0)\n .sort((a, b) => b.value - a.value)\n .map((d) => ({ label: prettyEnum(d.key), value: d.value, tone: toneForClass(d.key) }));\n}\n\n// ============================================================================\n// Gain cell — signed money with icon + semantic color (color never sole signal)\n// ============================================================================\n\nfunction GainValue({ value, currency }: { value: number; currency: string }) {\n const positive = value > 0;\n const negative = value < 0;\n const tone = positive\n ? 'text-status-success-text'\n : negative\n ? 'text-status-error-text'\n : 'text-text-secondary';\n const Icon = positive ? ArrowUpRight : negative ? ArrowDownRight : null;\n return (\n <span className={`inline-flex items-center justify-end gap-1 font-medium ${tone}`}>\n {Icon ? <Icon className=\"size-3.5\" aria-hidden=\"true\" /> : null}\n {positive ? '+' : ''}\n {formatMoney(value, currency)}\n </span>\n );\n}\n\n// ============================================================================\n// Portfolio create / edit dialog\n// ============================================================================\n\ninterface PortfolioFormState {\n name: string;\n baseCurrency: string;\n description: string;\n driftThresholdPct: string;\n status: BraPortfolioStatus;\n modelPortfolioId: string;\n householdId: string;\n}\n\nfunction emptyForm(): PortfolioFormState {\n return {\n name: '',\n baseCurrency: 'USD',\n description: '',\n driftThresholdPct: '5',\n status: 'DRAFT',\n modelPortfolioId: NONE,\n householdId: NONE,\n };\n}\n\nfunction PortfolioFormDialog({\n open,\n onOpenChange,\n editing,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n editing: BraPortfolio | null;\n}) {\n const { data: models } = useModelPortfolios();\n const { data: households } = useHouseholds();\n const createPortfolio = useCreatePortfolio();\n const updatePortfolio = useUpdatePortfolio();\n\n const [form, setForm] = useState<PortfolioFormState>(emptyForm);\n // Re-seed the form whenever the dialog target changes.\n const [seededFor, setSeededFor] = useState<string | null>(null);\n const targetKey = editing?.id ?? 'new';\n if (open && seededFor !== targetKey) {\n setSeededFor(targetKey);\n setForm(\n editing\n ? {\n name: editing.name,\n baseCurrency: editing.baseCurrency,\n description: editing.description ?? '',\n driftThresholdPct: String(editing.driftThresholdPct),\n status: editing.status,\n modelPortfolioId: editing.modelPortfolioId ?? NONE,\n householdId: editing.householdId ?? NONE,\n }\n : emptyForm()\n );\n }\n if (!open && seededFor !== null) {\n setSeededFor(null);\n }\n\n const pending = createPortfolio.isPending || updatePortfolio.isPending;\n const nameValid = form.name.trim().length > 0;\n\n const handleSubmit = () => {\n if (!nameValid) return;\n const drift = toNumber(form.driftThresholdPct);\n const modelPortfolioId = form.modelPortfolioId === NONE ? null : form.modelPortfolioId;\n const householdId = form.householdId === NONE ? null : form.householdId;\n\n if (editing) {\n updatePortfolio.mutate(\n {\n id: editing.id,\n input: {\n name: form.name.trim(),\n baseCurrency: form.baseCurrency.trim() || 'USD',\n description: form.description.trim() || null,\n driftThresholdPct: drift,\n status: form.status,\n modelPortfolioId,\n },\n },\n {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Portfolio saved');\n onOpenChange(false);\n },\n }\n );\n } else {\n createPortfolio.mutate(\n {\n name: form.name.trim(),\n baseCurrency: form.baseCurrency.trim() || 'USD',\n description: form.description.trim() || null,\n driftThresholdPct: drift,\n modelPortfolioId,\n householdId,\n },\n {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Portfolio created');\n onOpenChange(false);\n },\n }\n );\n }\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{editing ? 'Edit portfolio' : 'New portfolio'}</DialogTitle>\n <DialogDescription>\n {editing\n ? 'Update this portfolio’s strategy settings.'\n : 'Create an investment portfolio to organize and rebalance holdings.'}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-name\">Name</Label>\n <Input\n id=\"pf-name\"\n value={form.name}\n onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}\n placeholder=\"Growth Portfolio\"\n aria-invalid={!nameValid}\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-currency\">Base currency</Label>\n <Input\n id=\"pf-currency\"\n value={form.baseCurrency}\n onChange={(e) =>\n setForm((f) => ({ ...f, baseCurrency: e.target.value.toUpperCase() }))\n }\n placeholder=\"USD\"\n maxLength={3}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-drift\">Drift threshold %</Label>\n <Input\n id=\"pf-drift\"\n type=\"number\"\n min={0}\n step=\"0.5\"\n value={form.driftThresholdPct}\n onChange={(e) => setForm((f) => ({ ...f, driftThresholdPct: e.target.value }))}\n />\n </div>\n </div>\n\n {editing ? (\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-status\">Status</Label>\n <Select\n value={form.status}\n onValueChange={(v) => setForm((f) => ({ ...f, status: v as BraPortfolioStatus }))}\n >\n <SelectTrigger id=\"pf-status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PORTFOLIO_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {prettyEnum(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-model\">Model portfolio</Label>\n <Select\n value={form.modelPortfolioId}\n onValueChange={(v) => setForm((f) => ({ ...f, modelPortfolioId: v }))}\n >\n <SelectTrigger id=\"pf-model\">\n <SelectValue placeholder=\"None\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NONE}>None</SelectItem>\n {models.map((m) => (\n <SelectItem key={m.id} value={m.id}>\n {m.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {!editing ? (\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-household\">Household</Label>\n <Select\n value={form.householdId}\n onValueChange={(v) => setForm((f) => ({ ...f, householdId: v }))}\n >\n <SelectTrigger id=\"pf-household\">\n <SelectValue placeholder=\"None\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NONE}>None</SelectItem>\n {households.map((h) => (\n <SelectItem key={h.id} value={h.id}>\n {h.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-desc\">Description</Label>\n <Textarea\n id=\"pf-desc\"\n value={form.description}\n onChange={(e) => setForm((f) => ({ ...f, description: e.target.value }))}\n placeholder=\"Optional notes about this strategy\"\n rows={3}\n />\n </div>\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={pending}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!nameValid || pending}>\n {pending ? 'Saving…' : editing ? 'Save changes' : 'Create portfolio'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n\n// ============================================================================\n// Holding create / edit dialog (scoped to a portfolio)\n// ============================================================================\n\ninterface HoldingFormState {\n accountId: string;\n symbol: string;\n name: string;\n assetClass: BraAssetClass;\n quantity: string;\n lastPrice: string;\n costBasis: string;\n}\n\nfunction emptyHoldingForm(): HoldingFormState {\n return {\n accountId: '',\n symbol: '',\n name: '',\n assetClass: 'EQUITY',\n quantity: '0',\n lastPrice: '0',\n costBasis: '0',\n };\n}\n\nfunction HoldingFormDialog({\n open,\n onOpenChange,\n portfolioId,\n editing,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n portfolioId: string;\n editing: BraHolding | null;\n}) {\n const createHolding = useCreateHolding();\n const updateHolding = useUpdateHolding();\n\n const [form, setForm] = useState<HoldingFormState>(emptyHoldingForm);\n const [seededFor, setSeededFor] = useState<string | null>(null);\n const targetKey = editing?.id ?? 'new';\n if (open && seededFor !== targetKey) {\n setSeededFor(targetKey);\n setForm(\n editing\n ? {\n accountId: editing.accountId,\n symbol: editing.symbol,\n name: editing.name ?? '',\n assetClass: editing.assetClass,\n quantity: String(editing.quantity),\n lastPrice: String(editing.lastPrice),\n costBasis: String(editing.costBasis),\n }\n : emptyHoldingForm()\n );\n }\n if (!open && seededFor !== null) {\n setSeededFor(null);\n }\n\n const pending = createHolding.isPending || updateHolding.isPending;\n const symbolValid = form.symbol.trim().length > 0;\n const accountValid = editing ? true : form.accountId.trim().length > 0;\n const canSubmit = symbolValid && accountValid && !pending;\n\n const handleSubmit = () => {\n if (!symbolValid || !accountValid) return;\n if (editing) {\n updateHolding.mutate(\n {\n id: editing.id,\n input: {\n name: form.name.trim() || null,\n assetClass: form.assetClass,\n quantity: toNumber(form.quantity),\n lastPrice: toNumber(form.lastPrice),\n costBasis: toNumber(form.costBasis),\n },\n },\n {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Holding saved');\n onOpenChange(false);\n },\n }\n );\n } else {\n createHolding.mutate(\n {\n accountId: form.accountId,\n portfolioId,\n symbol: form.symbol.trim().toUpperCase(),\n name: form.name.trim() || null,\n assetClass: form.assetClass,\n quantity: toNumber(form.quantity),\n lastPrice: toNumber(form.lastPrice),\n costBasis: toNumber(form.costBasis),\n },\n {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Holding added');\n onOpenChange(false);\n },\n }\n );\n }\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{editing ? 'Edit holding' : 'Add holding'}</DialogTitle>\n <DialogDescription>\n {editing\n ? 'Update the position details for this holding.'\n : 'Add a position to this portfolio from one of your accounts.'}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {!editing ? (\n <AccountPicker\n id=\"h-account\"\n value={form.accountId}\n onChange={(v) => setForm((f) => ({ ...f, accountId: v }))}\n placeholder=\"Select account\"\n />\n ) : null}\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-symbol\">Symbol</Label>\n <Input\n id=\"h-symbol\"\n value={form.symbol}\n onChange={(e) => setForm((f) => ({ ...f, symbol: e.target.value }))}\n placeholder=\"AAPL\"\n disabled={Boolean(editing)}\n aria-invalid={!symbolValid}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-class\">Asset class</Label>\n <Select\n value={form.assetClass}\n onValueChange={(v) => setForm((f) => ({ ...f, assetClass: v as BraAssetClass }))}\n >\n <SelectTrigger id=\"h-class\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ASSET_CLASSES.map((c) => (\n <SelectItem key={c} value={c}>\n {prettyEnum(c)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-name\">Name</Label>\n <Input\n id=\"h-name\"\n value={form.name}\n onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}\n placeholder=\"Apple Inc.\"\n />\n </div>\n\n <div className=\"grid grid-cols-3 gap-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-qty\">Quantity</Label>\n <Input\n id=\"h-qty\"\n type=\"number\"\n step=\"any\"\n value={form.quantity}\n onChange={(e) => setForm((f) => ({ ...f, quantity: e.target.value }))}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-price\">Last price</Label>\n <Input\n id=\"h-price\"\n type=\"number\"\n step=\"any\"\n value={form.lastPrice}\n onChange={(e) => setForm((f) => ({ ...f, lastPrice: e.target.value }))}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-cost\">Cost basis</Label>\n <Input\n id=\"h-cost\"\n type=\"number\"\n step=\"any\"\n value={form.costBasis}\n onChange={(e) => setForm((f) => ({ ...f, costBasis: e.target.value }))}\n />\n </div>\n </div>\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={pending}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!canSubmit}>\n {pending ? 'Saving…' : editing ? 'Save changes' : 'Add holding'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n\n// ============================================================================\n// Portfolio detail sheet — holdings table + target vs actual allocation\n// ============================================================================\n\nfunction PortfolioDetailSheet({\n portfolioId,\n onOpenChange,\n}: {\n portfolioId: string | null;\n onOpenChange: (open: boolean) => void;\n}) {\n const { data: portfolio, isLoading, isError, refetch } = usePortfolio(portfolioId ?? undefined);\n const deleteHolding = useDeleteHolding();\n\n const [holdingDialogOpen, setHoldingDialogOpen] = useState(false);\n const [editingHolding, setEditingHolding] = useState<BraHolding | null>(null);\n const [holdingToDelete, setHoldingToDelete] = useState<BraHolding | null>(null);\n\n const holdings = portfolio?.holdings ?? [];\n const currency = portfolio?.baseCurrency ?? 'USD';\n const totalMarketValue = holdings.reduce((s, h) => s + h.marketValue, 0);\n const totalCostBasis = holdings.reduce((s, h) => s + h.costBasis, 0);\n const totalGain = totalMarketValue - totalCostBasis;\n const gainPct = totalCostBasis > 0 ? (totalGain / totalCostBasis) * 100 : 0;\n\n /** Live allocation by class in base-currency value — tone-keyed donut slices. */\n const actualDonut: DonutDatum[] = useMemo(() => {\n if (holdings.length === 0 || totalMarketValue <= 0) return [];\n const byClass = new Map<string, number>();\n holdings.forEach((h) =>\n byClass.set(h.assetClass, (byClass.get(h.assetClass) ?? 0) + h.marketValue)\n );\n return Array.from(byClass.entries())\n .map(([cls, mv]) => ({ label: prettyEnum(cls), value: mv, tone: toneForClass(cls) }))\n .sort((a, b) => b.value - a.value);\n }, [holdings, totalMarketValue]);\n\n /** Actual allocation as percentages, keyed by pretty class label, for drift comparison. */\n const actualPctByLabel = useMemo(() => {\n const map = new Map<string, number>();\n if (totalMarketValue <= 0) return map;\n holdings.forEach((h) =>\n map.set(\n prettyEnum(h.assetClass),\n (map.get(prettyEnum(h.assetClass)) ?? 0) + (h.marketValue / totalMarketValue) * 100\n )\n );\n return map;\n }, [holdings, totalMarketValue]);\n\n /** Target vs actual: each target row captioned with the live actual weight. */\n const targetVsActual: AllocationDatum[] = useMemo(() => {\n return parseTargetAllocation(portfolio?.targetAllocation).map((d) => {\n const actual = actualPctByLabel.get(d.label) ?? 0;\n return {\n label: d.label,\n value: d.value,\n tone: 'accent' as const,\n caption: `${formatPct(d.value, 0)} target · ${formatPct(actual, 0)} actual`,\n };\n });\n }, [portfolio?.targetAllocation, actualPctByLabel]);\n\n const openAddHolding = () => {\n setEditingHolding(null);\n setHoldingDialogOpen(true);\n };\n const openEditHolding = (h: BraHolding) => {\n setEditingHolding(h);\n setHoldingDialogOpen(true);\n };\n\n return (\n <Sheet open={portfolioId !== null} onOpenChange={onOpenChange}>\n <SheetContent className=\"flex w-full flex-col gap-0 overflow-y-auto p-0 sm:max-w-2xl\">\n {isLoading ? (\n <div className=\"p-6\">\n <LoadingState rows={5} />\n </div>\n ) : isError ? (\n <div className=\"p-6\">\n <ErrorState onRetry={() => refetch()} />\n </div>\n ) : !portfolio ? (\n <div className=\"p-6\">\n <EmptyState\n icon={<Briefcase className=\"size-6\" aria-hidden=\"true\" />}\n title=\"Portfolio not found\"\n description=\"This portfolio may have been archived or removed.\"\n />\n </div>\n ) : (\n <>\n <SheetHeader className=\"border-b border-border-subtle p-6\">\n <div className=\"flex items-center gap-2\">\n <SheetTitle className=\"text-xl\">{portfolio.name}</SheetTitle>\n <Badge variant={statusBadgeVariant(portfolio.status)}>\n {prettyEnum(portfolio.status)}\n </Badge>\n </div>\n <SheetDescription>\n {portfolio.description?.trim()\n ? portfolio.description\n : `${portfolio.baseCurrency} · Drift threshold ${formatPct(portfolio.driftThresholdPct)}`}\n </SheetDescription>\n </SheetHeader>\n\n <div className=\"space-y-6 p-6\">\n {/* Hero KPIs — market value carries an emerald glow + live gain delta */}\n <FadeIn className=\"grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <StatTile\n label=\"Market value\"\n value={formatMoney(totalMarketValue, currency)}\n glow\n />\n <StatTile\n label=\"Unrealized gain\"\n value={formatMoney(totalGain, currency)}\n delta={totalCostBasis > 0 ? gainPct : undefined}\n deltaTone={totalGain > 0 ? 'positive' : totalGain < 0 ? 'negative' : 'neutral'}\n />\n <StatTile label=\"Holdings\" value={String(holdings.length)} />\n <StatTile label=\"Drift threshold\" value={formatPct(portfolio.driftThresholdPct)} />\n </FadeIn>\n\n {/* Target vs actual allocation — live donut + drift-aware target bars */}\n <FadeIn index={1}>\n <GlassCard glow className=\"space-y-5\">\n <div className=\"flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n Target vs actual allocation\n </h3>\n <Badge variant=\"outline\">{currency}</Badge>\n </div>\n <div className=\"grid gap-6 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Actual — by market value\n </p>\n {actualDonut.length > 0 ? (\n <DonutChart\n data={actualDonut}\n centerValue={formatCompact(totalMarketValue, currency)}\n centerLabel=\"Market value\"\n valueFormatter={(v) => formatMoney(v, currency)}\n />\n ) : (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n Add holdings to see the live allocation.\n </p>\n )}\n </div>\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Target — vs live actual\n </p>\n {targetVsActual.length > 0 ? (\n <AllocationBars data={targetVsActual} />\n ) : (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n No target allocation set for this portfolio.\n </p>\n )}\n </div>\n </div>\n </GlassCard>\n </FadeIn>\n\n {/* Tags */}\n <Card>\n <CardContent className=\"p-4\">\n <TagField entityType=\"brainyrich-portfolio\" entityId={portfolio.id} />\n </CardContent>\n </Card>\n\n {/* Holdings table */}\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between\">\n <div>\n <CardTitle className=\"text-base\">Holdings</CardTitle>\n <CardDescription>Positions held in this portfolio</CardDescription>\n </div>\n <Button size=\"sm\" onClick={openAddHolding}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n Add holding\n </Button>\n </CardHeader>\n <CardContent>\n {holdings.length === 0 ? (\n <EmptyState\n icon={<Briefcase className=\"size-6\" aria-hidden=\"true\" />}\n title=\"No holdings yet\"\n description=\"Add positions to start tracking this portfolio.\"\n action={\n <Button size=\"sm\" onClick={openAddHolding}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n Add holding\n </Button>\n }\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Symbol</TableHead>\n <TableHead>Class</TableHead>\n <TableHead className=\"text-right\">Qty</TableHead>\n <TableHead className=\"text-right\">Price</TableHead>\n <TableHead className=\"text-right\">Market value</TableHead>\n <TableHead className=\"text-right\">Gain</TableHead>\n <TableHead className=\"w-10\" />\n </TableRow>\n </TableHeader>\n <TableBody>\n {holdings.map((h) => {\n const gain = h.marketValue - h.costBasis;\n const gainPctRow = h.costBasis > 0 ? (gain / h.costBasis) * 100 : 0;\n return (\n <TableRow\n key={h.id}\n className=\"transition-colors motion-safe:hover:bg-bg-sunken/60\"\n >\n <TableCell>\n <div className=\"font-semibold text-text-primary\">{h.symbol}</div>\n {h.name ? (\n <div className=\"text-xs text-text-secondary\">{h.name}</div>\n ) : null}\n </TableCell>\n <TableCell>\n <Badge variant=\"outline\">{prettyEnum(h.assetClass)}</Badge>\n </TableCell>\n <TableCell className=\"text-right tabular-nums\">\n {h.quantity}\n </TableCell>\n <TableCell className=\"text-right tabular-nums\">\n {formatMoney(h.lastPrice, h.currency)}\n </TableCell>\n <TableCell className=\"text-right font-medium tabular-nums\">\n {formatMoney(h.marketValue, h.currency)}\n </TableCell>\n <TableCell className=\"text-right tabular-nums\">\n <GainValue value={gain} currency={h.currency} />\n {h.costBasis > 0 ? (\n <div className=\"text-xs text-text-secondary\">\n {gain > 0 ? '+' : ''}\n {formatPct(gainPctRow)}\n </div>\n ) : null}\n </TableCell>\n <TableCell className=\"text-right\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-10\"\n aria-label={`Actions for ${h.symbol}`}\n >\n <MoreHorizontal className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuLabel>{h.symbol}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"gap-2\"\n onClick={() => openEditHolding(h)}\n >\n <Pencil className=\"size-4\" aria-hidden=\"true\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuItem\n className=\"gap-2 text-status-error-text focus:text-status-error-text\"\n onClick={() => setHoldingToDelete(h)}\n >\n <Trash2 className=\"size-4\" aria-hidden=\"true\" />\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Tags — real UniversalTag assignments keyed by entity */}\n <Card>\n <CardHeader className=\"pb-3\">\n <CardTitle className=\"text-sm\">Tags</CardTitle>\n <CardDescription>Organize and filter this portfolio.</CardDescription>\n </CardHeader>\n <CardContent>\n <TagField entityType=\"brainyrich-portfolio\" entityId={portfolio.id} hideLabel />\n </CardContent>\n </Card>\n\n {/* Discussion — threaded comments keyed by entity */}\n <Card>\n <CardContent className=\"pt-6\">\n <CommentThread entityType=\"brainyrich-portfolio\" entityId={portfolio.id} />\n </CardContent>\n </Card>\n </div>\n\n <HoldingFormDialog\n open={holdingDialogOpen}\n onOpenChange={setHoldingDialogOpen}\n portfolioId={portfolio.id}\n editing={editingHolding}\n />\n\n <ConfirmDialog\n open={holdingToDelete !== null}\n onOpenChange={(open) => {\n if (!open) setHoldingToDelete(null);\n }}\n title=\"Delete holding?\"\n description={\n holdingToDelete\n ? `Remove ${holdingToDelete.symbol} from this portfolio. This cannot be undone.`\n : undefined\n }\n confirmLabel=\"Delete\"\n pendingLabel=\"Deleting…\"\n destructive\n pending={deleteHolding.isPending}\n onConfirm={() => {\n if (!holdingToDelete) return;\n deleteHolding.mutate(holdingToDelete.id, {\n onSuccess: () => setHoldingToDelete(null),\n });\n }}\n />\n </>\n )}\n </SheetContent>\n </Sheet>\n );\n}\n\n// ============================================================================\n// Portfolio card — allocation donut + drift gauge (list grid)\n// ============================================================================\n\nfunction PortfolioCard({\n portfolio: p,\n index,\n onView,\n onEdit,\n onArchive,\n}: {\n portfolio: BraPortfolio;\n index: number;\n onView: () => void;\n onEdit: () => void;\n onArchive: () => void;\n}) {\n const donut = useMemo(() => targetAllocationDonut(p.targetAllocation), [p.targetAllocation]);\n const top = donut[0];\n const tags = p.tags ?? [];\n\n return (\n <FadeIn index={index} as=\"div\" className=\"h-full\">\n <Card className=\"flex h-full flex-col transition-shadow motion-safe:hover:shadow-[var(--shadow-pop-hover,var(--shadow-elevation-2))]\">\n <CardHeader className=\"pb-3\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-[var(--color-action-primary-bg)]/12 text-[var(--color-action-primary-text)]\">\n <Briefcase className=\"size-4\" aria-hidden=\"true\" />\n </span>\n <CardTitle className=\"truncate text-base\">{p.name}</CardTitle>\n <Badge variant={statusBadgeVariant(p.status)}>{prettyEnum(p.status)}</Badge>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-9 shrink-0\"\n aria-label={`Actions for ${p.name}`}\n >\n <MoreHorizontal className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuLabel>{p.name}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem className=\"gap-2\" onClick={onEdit}>\n <Pencil className=\"size-4\" aria-hidden=\"true\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuItem\n className=\"gap-2 text-status-error-text focus:text-status-error-text\"\n disabled={p.status === 'ARCHIVED'}\n onClick={onArchive}\n >\n <Trash2 className=\"size-4\" aria-hidden=\"true\" />\n Archive\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n {p.description?.trim() ? (\n <CardDescription className=\"line-clamp-2\">{p.description}</CardDescription>\n ) : null}\n </CardHeader>\n\n <CardContent className=\"flex flex-1 flex-col justify-between gap-4\">\n <div className=\"grid grid-cols-[auto_1fr] items-center gap-4\">\n {donut.length > 0 ? (\n <div className=\"w-28 shrink-0\">\n <DonutChart\n data={donut}\n legend={false}\n height={112}\n centerValue={top ? formatPct(top.value, 0) : undefined}\n centerLabel={top?.label}\n valueFormatter={(v) => formatPct(v, 0)}\n />\n </div>\n ) : (\n <div className=\"flex size-28 shrink-0 items-center justify-center rounded-full border border-dashed border-border-subtle text-center text-[10px] leading-tight text-text-secondary\">\n No target\n <br />\n allocation\n </div>\n )}\n <div className=\"min-w-0 space-y-2\">\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <Badge variant=\"outline\">{p.baseCurrency}</Badge>\n <Badge variant=\"secondary\">Drift ≤ {formatPct(p.driftThresholdPct)}</Badge>\n </div>\n {donut.length > 0 ? (\n <ul className=\"space-y-1\" aria-label=\"Target allocation\">\n {donut.slice(0, 3).map((d) => (\n <li key={d.label} className=\"flex items-center gap-1.5 text-xs\">\n <span className=\"truncate text-text-secondary\">{d.label}</span>\n <span className=\"ml-auto font-medium tabular-nums text-text-primary\">\n {formatPct(d.value, 0)}\n </span>\n </li>\n ))}\n </ul>\n ) : null}\n {tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {tags.slice(0, 3).map((t) => (\n <Badge key={t} variant=\"outline\" className=\"text-[10px]\">\n {t}\n </Badge>\n ))}\n </div>\n ) : null}\n </div>\n </div>\n <Button variant=\"outline\" size=\"sm\" className=\"w-full\" onClick={onView}>\n View holdings\n </Button>\n </CardContent>\n </Card>\n </FadeIn>\n );\n}\n\n// ============================================================================\n// Page\n// ============================================================================\n\nexport function PortfoliosPage() {\n useTrackBrainyRichPageView('brainyrich.portfolios.viewed');\n\n const {\n items: portfolios,\n hasMore,\n loadMore,\n isLoading,\n isFetchingNextPage,\n isError,\n refetch,\n } = usePagedPortfolios();\n const archivePortfolio = useArchivePortfolio();\n\n const [formOpen, setFormOpen] = useState(false);\n const [editingPortfolio, setEditingPortfolio] = useState<BraPortfolio | null>(null);\n const [detailId, setDetailId] = useState<string | null>(null);\n const [portfolioToArchive, setPortfolioToArchive] = useState<BraPortfolio | null>(null);\n\n const activeCount = portfolios.filter((p) => p.status === 'ACTIVE').length;\n const draftCount = portfolios.filter((p) => p.status === 'DRAFT').length;\n const avgDrift =\n portfolios.length > 0\n ? portfolios.reduce((s, p) => s + p.driftThresholdPct, 0) / portfolios.length\n : 0;\n\n const openCreate = () => {\n setEditingPortfolio(null);\n setFormOpen(true);\n };\n const openEdit = (p: BraPortfolio) => {\n setEditingPortfolio(p);\n setFormOpen(true);\n };\n\n return (\n <div data-tour=\"brainyrich-page-portfolios\" className=\"contents\">\n <SectionShell\n title=\"Portfolios\"\n description=\"Organize holdings into strategies and keep allocations on target.\"\n badges={portfolios.length > 0 ? [`${portfolios.length} total`] : []}\n actions={\n <Button size=\"sm\" onClick={openCreate}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n New portfolio\n </Button>\n }\n >\n {isLoading ? (\n <LoadingState rows={4} />\n ) : isError ? (\n <ErrorState onRetry={() => refetch()} />\n ) : portfolios.length === 0 ? (\n <EmptyState\n icon={<Briefcase className=\"size-6\" aria-hidden=\"true\" />}\n title=\"No portfolios yet\"\n description=\"Create your first portfolio to start organizing holdings and tracking drift.\"\n action={\n <Button size=\"sm\" onClick={openCreate}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n New portfolio\n </Button>\n }\n />\n ) : (\n <>\n <FadeIn className=\"grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <StatTile label=\"Portfolios\" value={String(portfolios.length)} glow />\n <StatTile label=\"Active\" value={String(activeCount)} />\n <StatTile label=\"Drafts\" value={String(draftCount)} />\n <StatTile label=\"Avg drift threshold\" value={formatPct(avgDrift)} />\n </FadeIn>\n\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {portfolios.map((p, i) => (\n <PortfolioCard\n key={p.id}\n portfolio={p}\n index={i}\n onView={() => setDetailId(p.id)}\n onEdit={() => openEdit(p)}\n onArchive={() => setPortfolioToArchive(p)}\n />\n ))}\n </div>\n\n <LoadMore hasMore={hasMore} onClick={loadMore} loading={isFetchingNextPage} />\n </>\n )}\n\n <PortfolioFormDialog\n open={formOpen}\n onOpenChange={setFormOpen}\n editing={editingPortfolio}\n />\n\n <PortfolioDetailSheet\n portfolioId={detailId}\n onOpenChange={(open) => {\n if (!open) setDetailId(null);\n }}\n />\n\n <ConfirmDialog\n open={portfolioToArchive !== null}\n onOpenChange={(open) => {\n if (!open) setPortfolioToArchive(null);\n }}\n title=\"Archive portfolio?\"\n description={\n portfolioToArchive\n ? `Archive “${portfolioToArchive.name}”. It will move to archived status and stop appearing in active views.`\n : undefined\n }\n confirmLabel=\"Archive\"\n pendingLabel=\"Archiving…\"\n destructive\n pending={archivePortfolio.isPending}\n onConfirm={() => {\n if (!portfolioToArchive) return;\n archivePortfolio.mutate(portfolioToArchive.id, {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Portfolio archived');\n setPortfolioToArchive(null);\n },\n });\n }}\n />\n </SectionShell>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA4FA,IAAM,KAAiC;CACrC;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,KAA2C;CAAC;CAAS;CAAU;CAAW,EAE1E,IAAO,YAEP,KAAc,MAClB,EACG,aAAa,CACb,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,OAAO,EAAE,CAAC,aAAa,GAAG,EAAE,MAAM,EAAE,CAAC,CAClD,KAAK,IAAI,EAER,KAAY,MAAsC;AACtD,KAAI,EAAM,MAAM,KAAK,GAAI;CACzB,IAAM,IAAI,OAAO,EAAM;AACvB,QAAO,OAAO,SAAS,EAAE,GAAG,IAAI,KAAA;GAI5B,KAAsC;CAC1C,QAAQ;CACR,cAAc;CACd,MAAM;CACN,aAAa;CACb,WAAW;CACX,QAAQ;CACR,aAAa;CACd,EAEK,MAAgB,MAAyB,GAAW,EAAI,aAAa,KAAK;AAGhF,SAAS,GAAsB,GAAiC;AAE9D,QADI,CAAC,KAAO,OAAO,KAAQ,WAAiB,EAAE,GACvC,OAAO,QAAQ,EAA+B,CAClD,KAAK,CAAC,GAAO,QAAY;EAAE;EAAO,OAAO,OAAO,EAAM;EAAE,EAAE,CAC1D,QAAQ,MAAM,OAAO,SAAS,EAAE,MAAM,CAAC,CACvC,KAAK,OAAO;EAAE,OAAO,EAAW,EAAE,MAAM;EAAE,OAAO,EAAE;EAAO,MAAM;EAAmB,EAAE;;AAI1F,SAAS,GAAsB,GAA4B;AAEzD,QADI,CAAC,KAAO,OAAO,KAAQ,WAAiB,EAAE,GACvC,OAAO,QAAQ,EAA+B,CAClD,KAAK,CAAC,GAAK,QAAY;EAAE;EAAK,OAAO,OAAO,EAAM;EAAE,EAAE,CACtD,QAAQ,MAAM,OAAO,SAAS,EAAE,MAAM,IAAI,EAAE,QAAQ,EAAE,CACtD,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,CACjC,KAAK,OAAO;EAAE,OAAO,EAAW,EAAE,IAAI;EAAE,OAAO,EAAE;EAAO,MAAM,GAAa,EAAE,IAAI;EAAE,EAAE;;AAO1F,SAAS,GAAU,EAAE,UAAO,eAAiD;CAC3E,IAAM,IAAW,IAAQ,GACnB,IAAW,IAAQ,GACnB,IAAO,IACT,6BACA,IACE,2BACA,uBACA,IAAO,IAAW,IAAe,IAAW,IAAiB;AACnE,QACE,kBAAC,QAAD;EAAM,WAAW,0DAA0D;YAA3E;GACG,IAAO,kBAAC,GAAD;IAAM,WAAU;IAAW,eAAY;IAAS,CAAA,GAAG;GAC1D,IAAW,MAAM;GACjB,EAAY,GAAO,EAAS;GACxB;;;AAkBX,SAAS,KAAgC;AACvC,QAAO;EACL,MAAM;EACN,cAAc;EACd,aAAa;EACb,mBAAmB;EACnB,QAAQ;EACR,kBAAkB;EAClB,aAAa;EACd;;AAGH,SAAS,GAAoB,EAC3B,SACA,iBACA,cAKC;CACD,IAAM,EAAE,MAAM,MAAW,GAAoB,EACvC,EAAE,MAAM,MAAe,GAAe,EACtC,IAAkB,GAAoB,EACtC,IAAkB,GAAoB,EAEtC,CAAC,GAAM,KAAW,EAA6B,GAAU,EAEzD,CAAC,GAAW,KAAgB,EAAwB,KAAK,EACzD,IAAY,GAAS,MAAM;AAiBjC,CAhBI,KAAQ,MAAc,MACxB,EAAa,EAAU,EACvB,EACE,IACI;EACE,MAAM,EAAQ;EACd,cAAc,EAAQ;EACtB,aAAa,EAAQ,eAAe;EACpC,mBAAmB,OAAO,EAAQ,kBAAkB;EACpD,QAAQ,EAAQ;EAChB,kBAAkB,EAAQ,oBAAoB;EAC9C,aAAa,EAAQ,eAAe;EACrC,GACD,IAAW,CAChB,GAEC,CAAC,KAAQ,MAAc,QACzB,EAAa,KAAK;CAGpB,IAAM,IAAU,EAAgB,aAAa,EAAgB,WACvD,IAAY,EAAK,KAAK,MAAM,CAAC,SAAS;AAkD5C,QACE,kBAAC,GAAD;EAAc;EAAoB;YAChC,kBAAC,GAAD;GAAe,WAAU;aAAzB;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAc,IAAU,mBAAmB,iBAA8B,CAAA,EACzE,kBAAC,IAAD,EAAA,UACG,IACG,+CACA,sEACc,CAAA,CACP,EAAA,CAAA;IAEf,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAU;QAAY,CAAA,EACrC,kBAAC,GAAD;QACE,IAAG;QACH,OAAO,EAAK;QACZ,WAAW,MAAM,GAAS,OAAO;SAAE,GAAG;SAAG,MAAM,EAAE,OAAO;SAAO,EAAE;QACjE,aAAY;QACZ,gBAAc,CAAC;QACf,CAAA,CACE;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBAAc;SAAqB,CAAA,EAClD,kBAAC,GAAD;SACE,IAAG;SACH,OAAO,EAAK;SACZ,WAAW,MACT,GAAS,OAAO;UAAE,GAAG;UAAG,cAAc,EAAE,OAAO,MAAM,aAAa;UAAE,EAAE;SAExE,aAAY;SACZ,WAAW;SACX,CAAA,CACE;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBAAW;SAAyB,CAAA,EACnD,kBAAC,GAAD;SACE,IAAG;SACH,MAAK;SACL,KAAK;SACL,MAAK;SACL,OAAO,EAAK;SACZ,WAAW,MAAM,GAAS,OAAO;UAAE,GAAG;UAAG,mBAAmB,EAAE,OAAO;UAAO,EAAE;SAC9E,CAAA,CACE;UACF;;MAEL,IACC,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAY;QAAc,CAAA,EACzC,kBAAC,GAAD;QACE,OAAO,EAAK;QACZ,gBAAgB,MAAM,GAAS,OAAO;SAAE,GAAG;SAAG,QAAQ;SAAyB,EAAE;kBAFnF,CAIE,kBAAC,GAAD;SAAe,IAAG;mBAChB,kBAAC,GAAD,EAAe,CAAA;SACD,CAAA,EAChB,kBAAC,GAAD,EAAA,UACG,GAAmB,KAAK,MACvB,kBAAC,GAAD;SAAoB,OAAO;mBACxB,EAAW,EAAE;SACH,EAFI,EAEJ,CACb,EACY,CAAA,CACT;UACL;WACJ;MAEJ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAW;QAAuB,CAAA,EACjD,kBAAC,GAAD;QACE,OAAO,EAAK;QACZ,gBAAgB,MAAM,GAAS,OAAO;SAAE,GAAG;SAAG,kBAAkB;SAAG,EAAE;kBAFvE,CAIE,kBAAC,GAAD;SAAe,IAAG;mBAChB,kBAAC,GAAD,EAAa,aAAY,QAAS,CAAA;SACpB,CAAA,EAChB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;SAAY,OAAO;mBAAM;SAAiB,CAAA,EACzC,EAAO,KAAK,MACX,kBAAC,GAAD;SAAuB,OAAO,EAAE;mBAC7B,EAAE;SACQ,EAFI,EAAE,GAEN,CACb,CACY,EAAA,CAAA,CACT;UACL;;MAEJ,IAoBE,OAnBF,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAe;QAAiB,CAAA,EAC/C,kBAAC,GAAD;QACE,OAAO,EAAK;QACZ,gBAAgB,MAAM,GAAS,OAAO;SAAE,GAAG;SAAG,aAAa;SAAG,EAAE;kBAFlE,CAIE,kBAAC,GAAD;SAAe,IAAG;mBAChB,kBAAC,GAAD,EAAa,aAAY,QAAS,CAAA;SACpB,CAAA,EAChB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;SAAY,OAAO;mBAAM;SAAiB,CAAA,EACzC,EAAW,KAAK,MACf,kBAAC,GAAD;SAAuB,OAAO,EAAE;mBAC7B,EAAE;SACQ,EAFI,EAAE,GAEN,CACb,CACY,EAAA,CAAA,CACT;UACL;;MAGR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAU;QAAmB,CAAA,EAC5C,kBAAC,IAAD;QACE,IAAG;QACH,OAAO,EAAK;QACZ,WAAW,MAAM,GAAS,OAAO;SAAE,GAAG;SAAG,aAAa,EAAE,OAAO;SAAO,EAAE;QACxE,aAAY;QACZ,MAAM;QACN,CAAA,CACE;;MACF;;IAEN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KAAQ,SAAQ;KAAU,eAAe,EAAa,GAAM;KAAE,UAAU;eAAS;KAExE,CAAA,EACT,kBAAC,GAAD;KAAQ,eAjLW;AACzB,UAAI,CAAC,EAAW;MAChB,IAAM,IAAQ,EAAS,EAAK,kBAAkB,EACxC,IAAmB,EAAK,qBAAqB,IAAO,OAAO,EAAK,kBAChE,IAAc,EAAK,gBAAgB,IAAO,OAAO,EAAK;AAE5D,MAAI,IACF,EAAgB,OACd;OACE,IAAI,EAAQ;OACZ,OAAO;QACL,MAAM,EAAK,KAAK,MAAM;QACtB,cAAc,EAAK,aAAa,MAAM,IAAI;QAC1C,aAAa,EAAK,YAAY,MAAM,IAAI;QACxC,mBAAmB;QACnB,QAAQ,EAAK;QACb;QACD;OACF,EACD,EACE,iBAAiB;AAGf,OAFA,GAAe,EACV,EAAU,kBAAkB,EACjC,EAAa,GAAM;SAEtB,CACF,GAED,EAAgB,OACd;OACE,MAAM,EAAK,KAAK,MAAM;OACtB,cAAc,EAAK,aAAa,MAAM,IAAI;OAC1C,aAAa,EAAK,YAAY,MAAM,IAAI;OACxC,mBAAmB;OACnB;OACA;OACD,EACD,EACE,iBAAiB;AAGf,OAFA,GAAe,EACV,EAAU,oBAAoB,EACnC,EAAa,GAAM;SAEtB,CACF;;KAqIkC,UAAU,CAAC,KAAa;eACpD,IAAU,YAAY,IAAU,iBAAiB;KAC3C,CAAA,CACI,EAAA,CAAA;IACD;;EACT,CAAA;;AAkBb,SAAS,KAAqC;AAC5C,QAAO;EACL,WAAW;EACX,QAAQ;EACR,MAAM;EACN,YAAY;EACZ,UAAU;EACV,WAAW;EACX,WAAW;EACZ;;AAGH,SAAS,GAAkB,EACzB,SACA,iBACA,gBACA,cAMC;CACD,IAAM,IAAgB,GAAkB,EAClC,IAAgB,GAAkB,EAElC,CAAC,GAAM,KAAW,EAA2B,GAAiB,EAC9D,CAAC,GAAW,KAAgB,EAAwB,KAAK,EACzD,IAAY,GAAS,MAAM;AAiBjC,CAhBI,KAAQ,MAAc,MACxB,EAAa,EAAU,EACvB,EACE,IACI;EACE,WAAW,EAAQ;EACnB,QAAQ,EAAQ;EAChB,MAAM,EAAQ,QAAQ;EACtB,YAAY,EAAQ;EACpB,UAAU,OAAO,EAAQ,SAAS;EAClC,WAAW,OAAO,EAAQ,UAAU;EACpC,WAAW,OAAO,EAAQ,UAAU;EACrC,GACD,IAAkB,CACvB,GAEC,CAAC,KAAQ,MAAc,QACzB,EAAa,KAAK;CAGpB,IAAM,IAAU,EAAc,aAAa,EAAc,WACnD,IAAc,EAAK,OAAO,MAAM,CAAC,SAAS,GAC1C,IAAe,IAAU,KAAO,EAAK,UAAU,MAAM,CAAC,SAAS,GAC/D,IAAY,KAAe,KAAgB,CAAC;AA+ClD,QACE,kBAAC,GAAD;EAAc;EAAoB;YAChC,kBAAC,GAAD;GAAe,WAAU;aAAzB;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAc,IAAU,iBAAiB,eAA4B,CAAA,EACrE,kBAAC,IAAD,EAAA,UACG,IACG,kDACA,+DACc,CAAA,CACP,EAAA,CAAA;IAEf,kBAAC,OAAD;KAAK,WAAU;eAAf;MACI,IAOE,OANF,kBAAC,GAAD;OACE,IAAG;OACH,OAAO,EAAK;OACZ,WAAW,MAAM,GAAS,OAAO;QAAE,GAAG;QAAG,WAAW;QAAG,EAAE;OACzD,aAAY;OACZ,CAAA;MAGJ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBAAW;SAAc,CAAA,EACxC,kBAAC,GAAD;SACE,IAAG;SACH,OAAO,EAAK;SACZ,WAAW,MAAM,GAAS,OAAO;UAAE,GAAG;UAAG,QAAQ,EAAE,OAAO;UAAO,EAAE;SACnE,aAAY;SACZ,UAAU,EAAQ;SAClB,gBAAc,CAAC;SACf,CAAA,CACE;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBAAU;SAAmB,CAAA,EAC5C,kBAAC,GAAD;SACE,OAAO,EAAK;SACZ,gBAAgB,MAAM,GAAS,OAAO;UAAE,GAAG;UAAG,YAAY;UAAoB,EAAE;mBAFlF,CAIE,kBAAC,GAAD;UAAe,IAAG;oBAChB,kBAAC,GAAD,EAAe,CAAA;UACD,CAAA,EAChB,kBAAC,GAAD,EAAA,UACG,GAAc,KAAK,MAClB,kBAAC,GAAD;UAAoB,OAAO;oBACxB,EAAW,EAAE;UACH,EAFI,EAEJ,CACb,EACY,CAAA,CACT;WACL;UACF;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAS;QAAY,CAAA,EACpC,kBAAC,GAAD;QACE,IAAG;QACH,OAAO,EAAK;QACZ,WAAW,MAAM,GAAS,OAAO;SAAE,GAAG;SAAG,MAAM,EAAE,OAAO;SAAO,EAAE;QACjE,aAAY;QACZ,CAAA,CACE;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBAAQ;UAAgB,CAAA,EACvC,kBAAC,GAAD;UACE,IAAG;UACH,MAAK;UACL,MAAK;UACL,OAAO,EAAK;UACZ,WAAW,MAAM,GAAS,OAAO;WAAE,GAAG;WAAG,UAAU,EAAE,OAAO;WAAO,EAAE;UACrE,CAAA,CACE;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBAAU;UAAkB,CAAA,EAC3C,kBAAC,GAAD;UACE,IAAG;UACH,MAAK;UACL,MAAK;UACL,OAAO,EAAK;UACZ,WAAW,MAAM,GAAS,OAAO;WAAE,GAAG;WAAG,WAAW,EAAE,OAAO;WAAO,EAAE;UACtE,CAAA,CACE;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBAAS;UAAkB,CAAA,EAC1C,kBAAC,GAAD;UACE,IAAG;UACH,MAAK;UACL,MAAK;UACL,OAAO,EAAK;UACZ,WAAW,MAAM,GAAS,OAAO;WAAE,GAAG;WAAG,WAAW,EAAE,OAAO;WAAO,EAAE;UACtE,CAAA,CACE;;QACF;;MACF;;IAEN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KAAQ,SAAQ;KAAU,eAAe,EAAa,GAAM;KAAE,UAAU;eAAS;KAExE,CAAA,EACT,kBAAC,GAAD;KAAQ,eAnJW;AACrB,OAAC,KAAe,CAAC,MACjB,IACF,EAAc,OACZ;OACE,IAAI,EAAQ;OACZ,OAAO;QACL,MAAM,EAAK,KAAK,MAAM,IAAI;QAC1B,YAAY,EAAK;QACjB,UAAU,EAAS,EAAK,SAAS;QACjC,WAAW,EAAS,EAAK,UAAU;QACnC,WAAW,EAAS,EAAK,UAAU;QACpC;OACF,EACD,EACE,iBAAiB;AAGf,OAFA,GAAe,EACV,EAAU,gBAAgB,EAC/B,EAAa,GAAM;SAEtB,CACF,GAED,EAAc,OACZ;OACE,WAAW,EAAK;OAChB;OACA,QAAQ,EAAK,OAAO,MAAM,CAAC,aAAa;OACxC,MAAM,EAAK,KAAK,MAAM,IAAI;OAC1B,YAAY,EAAK;OACjB,UAAU,EAAS,EAAK,SAAS;OACjC,WAAW,EAAS,EAAK,UAAU;OACnC,WAAW,EAAS,EAAK,UAAU;OACpC,EACD,EACE,iBAAiB;AAGf,OAFA,GAAe,EACV,EAAU,gBAAgB,EAC/B,EAAa,GAAM;SAEtB,CACF;;KA0GkC,UAAU,CAAC;eACvC,IAAU,YAAY,IAAU,iBAAiB;KAC3C,CAAA,CACI,EAAA,CAAA;IACD;;EACT,CAAA;;AAQb,SAAS,GAAqB,EAC5B,gBACA,mBAIC;CACD,IAAM,EAAE,MAAM,GAAW,cAAW,YAAS,eAAY,EAAa,KAAe,KAAA,EAAU,EACzF,IAAgB,GAAkB,EAElC,CAAC,GAAmB,KAAwB,EAAS,GAAM,EAC3D,CAAC,GAAgB,MAAqB,EAA4B,KAAK,EACvE,CAAC,GAAiB,KAAsB,EAA4B,KAAK,EAEzE,IAAW,GAAW,YAAY,EAAE,EACpC,IAAW,GAAW,gBAAgB,OACtC,IAAmB,EAAS,QAAQ,GAAG,MAAM,IAAI,EAAE,aAAa,EAAE,EAClE,IAAiB,EAAS,QAAQ,GAAG,MAAM,IAAI,EAAE,WAAW,EAAE,EAC9D,IAAY,IAAmB,GAC/B,KAAU,IAAiB,IAAK,IAAY,IAAkB,MAAM,GAGpE,IAA4B,QAAc;AAC9C,MAAI,EAAS,WAAW,KAAK,KAAoB,EAAG,QAAO,EAAE;EAC7D,IAAM,oBAAU,IAAI,KAAqB;AAIzC,SAHA,EAAS,SAAS,MAChB,EAAQ,IAAI,EAAE,aAAa,EAAQ,IAAI,EAAE,WAAW,IAAI,KAAK,EAAE,YAAY,CAC5E,EACM,MAAM,KAAK,EAAQ,SAAS,CAAC,CACjC,KAAK,CAAC,GAAK,QAAS;GAAE,OAAO,EAAW,EAAI;GAAE,OAAO;GAAI,MAAM,GAAa,EAAI;GAAE,EAAE,CACpF,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;IACnC,CAAC,GAAU,EAAiB,CAAC,EAG1B,IAAmB,QAAc;EACrC,IAAM,oBAAM,IAAI,KAAqB;AAQrC,SAPI,KAAoB,KACxB,EAAS,SAAS,MAChB,EAAI,IACF,EAAW,EAAE,WAAW,GACvB,EAAI,IAAI,EAAW,EAAE,WAAW,CAAC,IAAI,KAAM,EAAE,cAAc,IAAoB,IACjF,CACF,EANiC;IAQjC,CAAC,GAAU,EAAiB,CAAC,EAG1B,KAAoC,QACjC,GAAsB,GAAW,iBAAiB,CAAC,KAAK,MAAM;EACnE,IAAM,IAAS,EAAiB,IAAI,EAAE,MAAM,IAAI;AAChD,SAAO;GACL,OAAO,EAAE;GACT,OAAO,EAAE;GACT,MAAM;GACN,SAAS,GAAG,EAAU,EAAE,OAAO,EAAE,CAAC,YAAY,EAAU,GAAQ,EAAE,CAAC;GACpE;GACD,EACD,CAAC,GAAW,kBAAkB,EAAiB,CAAC,EAE7C,UAAuB;AAE3B,EADA,GAAkB,KAAK,EACvB,EAAqB,GAAK;IAEtB,KAAmB,MAAkB;AAEzC,EADA,GAAkB,EAAE,EACpB,EAAqB,GAAK;;AAG5B,QACE,kBAAC,IAAD;EAAO,MAAM,MAAgB;EAAoB;YAC/C,kBAAC,IAAD;GAAc,WAAU;aACrB,IACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,IAAD,EAAc,MAAM,GAAK,CAAA;IACrB,CAAA,GACJ,IACF,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,IAAD,EAAY,eAAe,GAAS,EAAI,CAAA;IACpC,CAAA,GACH,IASH,kBAAA,IAAA,EAAA,UAAA;IACE,kBAAC,IAAD;KAAa,WAAU;eAAvB,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,IAAD;OAAY,WAAU;iBAAW,EAAU;OAAkB,CAAA,EAC7D,kBAAC,GAAD;OAAO,SAAS,EAAmB,EAAU,OAAO;iBACjD,EAAW,EAAU,OAAO;OACvB,CAAA,CACJ;SACN,kBAAC,IAAD,EAAA,UACG,EAAU,aAAa,MAAM,GAC1B,EAAU,cACV,GAAG,EAAU,aAAa,qBAAqB,EAAU,EAAU,kBAAkB,IACxE,CAAA,CACP;;IAEd,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEE,kBAAC,GAAD;OAAQ,WAAU;iBAAlB;QACE,kBAAC,GAAD;SACE,OAAM;SACN,OAAO,EAAY,GAAkB,EAAS;SAC9C,MAAA;SACA,CAAA;QACF,kBAAC,GAAD;SACE,OAAM;SACN,OAAO,EAAY,GAAW,EAAS;SACvC,OAAO,IAAiB,IAAI,KAAU,KAAA;SACtC,WAAW,IAAY,IAAI,aAAa,IAAY,IAAI,aAAa;SACrE,CAAA;QACF,kBAAC,GAAD;SAAU,OAAM;SAAW,OAAO,OAAO,EAAS,OAAO;SAAI,CAAA;QAC7D,kBAAC,GAAD;SAAU,OAAM;SAAkB,OAAO,EAAU,EAAU,kBAAkB;SAAI,CAAA;QAC5E;;MAGT,kBAAC,GAAD;OAAQ,OAAO;iBACb,kBAAC,GAAD;QAAW,MAAA;QAAK,WAAU;kBAA1B,CACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,MAAD;UAAI,WAAU;oBAA0C;UAEnD,CAAA,EACL,kBAAC,GAAD;UAAO,SAAQ;oBAAW;UAAiB,CAAA,CACvC;YACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAkE;WAE3E,CAAA,EACH,EAAY,SAAS,IACpB,kBAAC,GAAD;WACE,MAAM;WACN,aAAa,GAAc,GAAkB,EAAS;WACtD,aAAY;WACZ,iBAAiB,MAAM,EAAY,GAAG,EAAS;WAC/C,CAAA,GAEF,kBAAC,KAAD;WAAG,WAAU;qBAA+C;WAExD,CAAA,CAEF;aACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAkE;WAE3E,CAAA,EACH,GAAe,SAAS,IACvB,kBAAC,GAAD,EAAgB,MAAM,IAAkB,CAAA,GAExC,kBAAC,KAAD;WAAG,WAAU;qBAA+C;WAExD,CAAA,CAEF;YACF;WACI;;OACL,CAAA;MAGT,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;OAAa,WAAU;iBACrB,kBAAC,GAAD;QAAU,YAAW;QAAuB,UAAU,EAAU;QAAM,CAAA;OAC1D,CAAA,EACT,CAAA;MAGP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,IAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD;QAAW,WAAU;kBAAY;QAAoB,CAAA,EACrD,kBAAC,IAAD,EAAA,UAAiB,oCAAkD,CAAA,CAC/D,EAAA,CAAA,EACN,kBAAC,GAAD;QAAQ,MAAK;QAAK,SAAS;kBAA3B,CACE,kBAAC,GAAD;SAAM,WAAU;SAAS,eAAY;SAAS,CAAA,EAAA,cAEvC;UACE;UACb,kBAAC,GAAD,EAAA,UACG,EAAS,WAAW,IACnB,kBAAC,IAAD;OACE,MAAM,kBAAC,GAAD;QAAW,WAAU;QAAS,eAAY;QAAS,CAAA;OACzD,OAAM;OACN,aAAY;OACZ,QACE,kBAAC,GAAD;QAAQ,MAAK;QAAK,SAAS;kBAA3B,CACE,kBAAC,GAAD;SAAM,WAAU;SAAS,eAAY;SAAS,CAAA,EAAA,cAEvC;;OAEX,CAAA,GAEF,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UACE,kBAAC,IAAD,EAAA,UAAA;QACE,kBAAC,GAAD,EAAA,UAAW,UAAkB,CAAA;QAC7B,kBAAC,GAAD,EAAA,UAAW,SAAiB,CAAA;QAC5B,kBAAC,GAAD;SAAW,WAAU;mBAAa;SAAe,CAAA;QACjD,kBAAC,GAAD;SAAW,WAAU;mBAAa;SAAiB,CAAA;QACnD,kBAAC,GAAD;SAAW,WAAU;mBAAa;SAAwB,CAAA;QAC1D,kBAAC,GAAD;SAAW,WAAU;mBAAa;SAAgB,CAAA;QAClD,kBAAC,GAAD,EAAW,WAAU,QAAS,CAAA;QACrB,EAAA,CAAA,EACC,CAAA,EACd,kBAAC,IAAD,EAAA,UACG,EAAS,KAAK,MAAM;QACnB,IAAM,IAAO,EAAE,cAAc,EAAE,WACzB,IAAa,EAAE,YAAY,IAAK,IAAO,EAAE,YAAa,MAAM;AAClE,eACE,kBAAC,IAAD;SAEE,WAAU;mBAFZ;UAIE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;WAAK,WAAU;qBAAmC,EAAE;WAAa,CAAA,EAChE,EAAE,OACD,kBAAC,OAAD;WAAK,WAAU;qBAA+B,EAAE;WAAW,CAAA,GACzD,KACM,EAAA,CAAA;UACZ,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;WAAO,SAAQ;qBAAW,EAAW,EAAE,WAAW;WAAS,CAAA,EACjD,CAAA;UACZ,kBAAC,GAAD;WAAW,WAAU;qBAClB,EAAE;WACO,CAAA;UACZ,kBAAC,GAAD;WAAW,WAAU;qBAClB,EAAY,EAAE,WAAW,EAAE,SAAS;WAC3B,CAAA;UACZ,kBAAC,GAAD;WAAW,WAAU;qBAClB,EAAY,EAAE,aAAa,EAAE,SAAS;WAC7B,CAAA;UACZ,kBAAC,GAAD;WAAW,WAAU;qBAArB,CACE,kBAAC,IAAD;YAAW,OAAO;YAAM,UAAU,EAAE;YAAY,CAAA,EAC/C,EAAE,YAAY,IACb,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACG,IAAO,IAAI,MAAM,IACjB,EAAU,EAAW,CAClB;gBACJ,KACM;;UACZ,kBAAC,GAAD;WAAW,WAAU;qBACnB,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD;YAAqB,SAAA;sBACnB,kBAAC,GAAD;aACE,SAAQ;aACR,MAAK;aACL,WAAU;aACV,cAAY,eAAe,EAAE;uBAE7B,kBAAC,IAAD;cAAgB,WAAU;cAAS,eAAY;cAAS,CAAA;aACjD,CAAA;YACW,CAAA,EACtB,kBAAC,IAAD;YAAqB,OAAM;sBAA3B;aACE,kBAAC,IAAD,EAAA,UAAoB,EAAE,QAA2B,CAAA;aACjD,kBAAC,IAAD,EAAyB,CAAA;aACzB,kBAAC,GAAD;cACE,WAAU;cACV,eAAe,EAAgB,EAAE;wBAFnC,CAIE,kBAAC,IAAD;eAAQ,WAAU;eAAS,eAAY;eAAS,CAAA,EAAA,OAE/B;;aACnB,kBAAC,GAAD;cACE,WAAU;cACV,eAAe,EAAmB,EAAE;wBAFtC,CAIE,kBAAC,IAAD;eAAQ,WAAU;eAAS,eAAY;eAAS,CAAA,EAAA,SAE/B;;aACC;cACT,EAAA,CAAA;WACL,CAAA;UACH;WA9DJ,EAAE,GA8DE;SAEb,EACQ,CAAA,CACN,EAAA,CAAA;OACJ,CAAA,EAEI,CAAA,CACT,EAAA,CAAA;MAGP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,IAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,IAAD;QAAW,WAAU;kBAAU;QAAgB,CAAA,EAC/C,kBAAC,IAAD,EAAA,UAAiB,uCAAqD,CAAA,CAC3D;UACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;OAAU,YAAW;OAAuB,UAAU,EAAU;OAAI,WAAA;OAAY,CAAA,EACpE,CAAA,CACT,EAAA,CAAA;MAGP,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;OAAa,WAAU;iBACrB,kBAAC,IAAD;QAAe,YAAW;QAAuB,UAAU,EAAU;QAAM,CAAA;OAC/D,CAAA,EACT,CAAA;MACH;;IAEN,kBAAC,IAAD;KACE,MAAM;KACN,cAAc;KACd,aAAa,EAAU;KACvB,SAAS;KACT,CAAA;IAEF,kBAAC,GAAD;KACE,MAAM,MAAoB;KAC1B,eAAe,MAAS;AACtB,MAAK,KAAM,EAAmB,KAAK;;KAErC,OAAM;KACN,aACE,IACI,UAAU,EAAgB,OAAO,gDACjC,KAAA;KAEN,cAAa;KACb,cAAa;KACb,aAAA;KACA,SAAS,EAAc;KACvB,iBAAiB;AACV,WACL,EAAc,OAAO,EAAgB,IAAI,EACvC,iBAAiB,EAAmB,KAAK,EAC1C,CAAC;;KAEJ,CAAA;IACD,EAAA,CAAA,GAhQH,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,IAAD;KACE,MAAM,kBAAC,GAAD;MAAW,WAAU;MAAS,eAAY;MAAS,CAAA;KACzD,OAAM;KACN,aAAY;KACZ,CAAA;IACE,CAAA;GA4PK,CAAA;EACT,CAAA;;AAQZ,SAAS,GAAc,EACrB,WAAW,GACX,UACA,WACA,WACA,gBAOC;CACD,IAAM,IAAQ,QAAc,GAAsB,EAAE,iBAAiB,EAAE,CAAC,EAAE,iBAAiB,CAAC,EACtF,IAAM,EAAM,IACZ,IAAO,EAAE,QAAQ,EAAE;AAEzB,QACE,kBAAC,GAAD;EAAe;EAAO,IAAG;EAAM,WAAU;YACvC,kBAAC,GAAD;GAAM,WAAU;aAAhB,CACE,kBAAC,IAAD;IAAY,WAAU;cAAtB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,QAAD;QAAM,WAAU;kBACd,kBAAC,GAAD;SAAW,WAAU;SAAS,eAAY;SAAS,CAAA;QAC9C,CAAA;OACP,kBAAC,IAAD;QAAW,WAAU;kBAAsB,EAAE;QAAiB,CAAA;OAC9D,kBAAC,GAAD;QAAO,SAAS,EAAmB,EAAE,OAAO;kBAAG,EAAW,EAAE,OAAO;QAAS,CAAA;OACxE;SACN,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD;MAAqB,SAAA;gBACnB,kBAAC,GAAD;OACE,SAAQ;OACR,MAAK;OACL,WAAU;OACV,cAAY,eAAe,EAAE;iBAE7B,kBAAC,IAAD;QAAgB,WAAU;QAAS,eAAY;QAAS,CAAA;OACjD,CAAA;MACW,CAAA,EACtB,kBAAC,IAAD;MAAqB,OAAM;gBAA3B;OACE,kBAAC,IAAD,EAAA,UAAoB,EAAE,MAAyB,CAAA;OAC/C,kBAAC,IAAD,EAAyB,CAAA;OACzB,kBAAC,GAAD;QAAkB,WAAU;QAAQ,SAAS;kBAA7C,CACE,kBAAC,IAAD;SAAQ,WAAU;SAAS,eAAY;SAAS,CAAA,EAAA,OAE/B;;OACnB,kBAAC,GAAD;QACE,WAAU;QACV,UAAU,EAAE,WAAW;QACvB,SAAS;kBAHX,CAKE,kBAAC,IAAD;SAAQ,WAAU;SAAS,eAAY;SAAS,CAAA,EAAA,UAE/B;;OACC;QACT,EAAA,CAAA,CACX;QACL,EAAE,aAAa,MAAM,GACpB,kBAAC,IAAD;KAAiB,WAAU;eAAgB,EAAE;KAA8B,CAAA,GACzE,KACO;OAEb,kBAAC,GAAD;IAAa,WAAU;cAAvB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACG,EAAM,SAAS,IACd,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OACE,MAAM;OACN,QAAQ;OACR,QAAQ;OACR,aAAa,IAAM,EAAU,EAAI,OAAO,EAAE,GAAG,KAAA;OAC7C,aAAa,GAAK;OAClB,iBAAiB,MAAM,EAAU,GAAG,EAAE;OACtC,CAAA;MACE,CAAA,GAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OAAoL;OAElL,kBAAC,MAAD,EAAM,CAAA;;OAEF;SAER,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBAAW,EAAE;SAAqB,CAAA,EACjD,kBAAC,GAAD;SAAO,SAAQ;mBAAf,CAA2B,YAAS,EAAU,EAAE,kBAAkB,CAAS;WACvE;;OACL,EAAM,SAAS,IACd,kBAAC,MAAD;QAAI,WAAU;QAAY,cAAW;kBAClC,EAAM,MAAM,GAAG,EAAE,CAAC,KAAK,MACtB,kBAAC,MAAD;SAAkB,WAAU;mBAA5B,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAgC,EAAE;UAAa,CAAA,EAC/D,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAU,EAAE,OAAO,EAAE;UACjB,CAAA,CACJ;WALI,EAAE,MAKN,CACL;QACC,CAAA,GACH;OACH,EAAK,SAAS,IACb,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAK,MAAM,GAAG,EAAE,CAAC,KAAK,MACrB,kBAAC,GAAD;SAAe,SAAQ;SAAU,WAAU;mBACxC;SACK,EAFI,EAEJ,CACR;QACE,CAAA,GACJ;OACA;QACF;QACN,kBAAC,GAAD;KAAQ,SAAQ;KAAU,MAAK;KAAK,WAAU;KAAS,SAAS;eAAQ;KAE/D,CAAA,CACG;MACT;;EACA,CAAA;;AAQb,SAAgB,KAAiB;AAC/B,GAA2B,+BAA+B;CAE1D,IAAM,EACJ,OAAO,GACP,YACA,aACA,cACA,uBACA,YACA,eACE,GAAoB,EAClB,IAAmB,GAAqB,EAExC,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAkB,KAAuB,EAA8B,KAAK,EAC7E,CAAC,IAAU,KAAe,EAAwB,KAAK,EACvD,CAAC,GAAoB,KAAyB,EAA8B,KAAK,EAEjF,KAAc,EAAW,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC,QAC9D,IAAa,EAAW,QAAQ,MAAM,EAAE,WAAW,QAAQ,CAAC,QAC5D,IACJ,EAAW,SAAS,IAChB,EAAW,QAAQ,GAAG,MAAM,IAAI,EAAE,mBAAmB,EAAE,GAAG,EAAW,SACrE,GAEA,UAAmB;AAEvB,EADA,EAAoB,KAAK,EACzB,EAAY,GAAK;IAEb,MAAY,MAAoB;AAEpC,EADA,EAAoB,EAAE,EACtB,EAAY,GAAK;;AAGnB,QACE,kBAAC,OAAD;EAAK,aAAU;EAA6B,WAAU;YACpD,kBAAC,IAAD;GACE,OAAM;GACN,aAAY;GACZ,QAAQ,EAAW,SAAS,IAAI,CAAC,GAAG,EAAW,OAAO,QAAQ,GAAG,EAAE;GACnE,SACE,kBAAC,GAAD;IAAQ,MAAK;IAAK,SAAS;cAA3B,CACE,kBAAC,GAAD;KAAM,WAAU;KAAS,eAAY;KAAS,CAAA,EAAA,gBAEvC;;aARb;IAWG,IACC,kBAAC,IAAD,EAAc,MAAM,GAAK,CAAA,GACvB,IACF,kBAAC,IAAD,EAAY,eAAe,GAAS,EAAI,CAAA,GACtC,EAAW,WAAW,IACxB,kBAAC,IAAD;KACE,MAAM,kBAAC,GAAD;MAAW,WAAU;MAAS,eAAY;MAAS,CAAA;KACzD,OAAM;KACN,aAAY;KACZ,QACE,kBAAC,GAAD;MAAQ,MAAK;MAAK,SAAS;gBAA3B,CACE,kBAAC,GAAD;OAAM,WAAU;OAAS,eAAY;OAAS,CAAA,EAAA,gBAEvC;;KAEX,CAAA,GAEF,kBAAA,IAAA,EAAA,UAAA;KACE,kBAAC,GAAD;MAAQ,WAAU;gBAAlB;OACE,kBAAC,GAAD;QAAU,OAAM;QAAa,OAAO,OAAO,EAAW,OAAO;QAAE,MAAA;QAAO,CAAA;OACtE,kBAAC,GAAD;QAAU,OAAM;QAAS,OAAO,OAAO,GAAY;QAAI,CAAA;OACvD,kBAAC,GAAD;QAAU,OAAM;QAAS,OAAO,OAAO,EAAW;QAAI,CAAA;OACtD,kBAAC,GAAD;QAAU,OAAM;QAAsB,OAAO,EAAU,EAAS;QAAI,CAAA;OAC7D;;KAET,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAW,KAAK,GAAG,MAClB,kBAAC,IAAD;OAEE,WAAW;OACX,OAAO;OACP,cAAc,EAAY,EAAE,GAAG;OAC/B,cAAc,GAAS,EAAE;OACzB,iBAAiB,EAAsB,EAAE;OACzC,EANK,EAAE,GAMP,CACF;MACE,CAAA;KAEN,kBAAC,GAAD;MAAmB;MAAS,SAAS;MAAU,SAAS;MAAsB,CAAA;KAC7E,EAAA,CAAA;IAGL,kBAAC,IAAD;KACE,MAAM;KACN,cAAc;KACd,SAAS;KACT,CAAA;IAEF,kBAAC,IAAD;KACE,aAAa;KACb,eAAe,MAAS;AACtB,MAAK,KAAM,EAAY,KAAK;;KAE9B,CAAA;IAEF,kBAAC,GAAD;KACE,MAAM,MAAuB;KAC7B,eAAe,MAAS;AACtB,MAAK,KAAM,EAAsB,KAAK;;KAExC,OAAM;KACN,aACE,IACI,YAAY,EAAmB,KAAK,0EACpC,KAAA;KAEN,cAAa;KACb,cAAa;KACb,aAAA;KACA,SAAS,EAAiB;KAC1B,iBAAiB;AACV,WACL,EAAiB,OAAO,EAAmB,IAAI,EAC7C,iBAAiB;AAGf,OAFA,GAAe,EACV,EAAU,qBAAqB,EACpC,EAAsB,KAAK;SAE9B,CAAC;;KAEJ,CAAA;IACW;;EACX,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReportsPage.d.ts","sourceRoot":"","sources":["../../../src/brainyrich/pages/ReportsPage.tsx"],"names":[],"mappings":"AAgUA,wBAAgB,WAAW,4CAgS1B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RiskPage.d.ts","sourceRoot":"","sources":["../../../src/brainyrich/pages/RiskPage.tsx"],"names":[],"mappings":"AAqPA,wBAAgB,QAAQ,4CA2lBvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaxPage.d.ts","sourceRoot":"","sources":["../../../src/brainyrich/pages/TaxPage.tsx"],"names":[],"mappings":"AAmKA,wBAAgB,OAAO,4CA6mBtB"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/** USD currency, no cents. Pass a currency code to override. */
|
|
4
|
+
export declare function formatMoney(value: number, currency?: string): string;
|
|
5
|
+
/** Compact money/number, e.g. 1.2M, 950K. */
|
|
6
|
+
export declare function formatCompact(value: number, currency?: string): string;
|
|
7
|
+
/** Percentage from a whole-number percent (e.g. 12.5 → "12.5%"). */
|
|
8
|
+
export declare function formatPct(value: number | null | undefined, fractionDigits?: number): string;
|
|
9
|
+
/**
|
|
10
|
+
* Guarded date formatter. Only wraps a present ISO string in `new Date()` at
|
|
11
|
+
* render time — never calls `.getTime()` on a raw scalar and never `new Date(0)`.
|
|
12
|
+
* Returns an em-dash for absent/invalid values.
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatDate(iso?: string | null): string;
|
|
15
|
+
/**
|
|
16
|
+
* Map a domain status/severity string to a shadcn Badge variant. Accepts the
|
|
17
|
+
* Bra* enum members (ACTIVE, AT_RISK, HIGH, …) case-insensitively so pages can
|
|
18
|
+
* pass raw enum values.
|
|
19
|
+
*/
|
|
20
|
+
export declare function statusBadgeVariant(status: string): 'default' | 'secondary' | 'destructive' | 'outline';
|
|
21
|
+
/** Semantic text-color token for a signed delta (color is never the only signal). */
|
|
22
|
+
export declare function deltaToneClass(value: number): string;
|
|
23
|
+
interface SectionShellProps {
|
|
24
|
+
title: string;
|
|
25
|
+
description: string;
|
|
26
|
+
actionLabel?: string;
|
|
27
|
+
onAction?: () => void;
|
|
28
|
+
actions?: ReactNode;
|
|
29
|
+
badges?: string[];
|
|
30
|
+
children: ReactNode;
|
|
31
|
+
}
|
|
32
|
+
export declare function SectionShell({ title, description, actionLabel, onAction, actions, badges, children, }: SectionShellProps): import("react/jsx-runtime").JSX.Element;
|
|
33
|
+
export interface KpiItem {
|
|
34
|
+
label: string;
|
|
35
|
+
value: string;
|
|
36
|
+
/** Optional signed delta rendered as a DeltaBadge (icon + sign + color). */
|
|
37
|
+
delta?: number;
|
|
38
|
+
/** Optional free-text trend line (neutral tone unless deltaTone provided). */
|
|
39
|
+
trend?: string;
|
|
40
|
+
deltaTone?: 'positive' | 'negative' | 'neutral';
|
|
41
|
+
}
|
|
42
|
+
export declare function KpiGrid({ items }: {
|
|
43
|
+
items: KpiItem[];
|
|
44
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
45
|
+
export declare function DeltaBadge({ value, suffix, className, }: {
|
|
46
|
+
value: number;
|
|
47
|
+
suffix?: string;
|
|
48
|
+
className?: string;
|
|
49
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
50
|
+
export type AllocationTone = 'default' | 'positive' | 'negative' | 'warning' | 'accent';
|
|
51
|
+
export interface AllocationDatum {
|
|
52
|
+
label: string;
|
|
53
|
+
/** Percentage 0–100 for the bar width. */
|
|
54
|
+
value: number;
|
|
55
|
+
tone?: AllocationTone;
|
|
56
|
+
/** Optional right-aligned caption (e.g. formatted money). Defaults to the %. */
|
|
57
|
+
caption?: string;
|
|
58
|
+
}
|
|
59
|
+
export declare function AllocationBars({ data }: {
|
|
60
|
+
data: AllocationDatum[];
|
|
61
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
62
|
+
export declare function EmptyState({ icon, title, description, action, }: {
|
|
63
|
+
icon?: ReactNode;
|
|
64
|
+
title: string;
|
|
65
|
+
description?: string;
|
|
66
|
+
action?: ReactNode;
|
|
67
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
68
|
+
export declare function LoadingState({ rows }: {
|
|
69
|
+
rows?: number;
|
|
70
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
71
|
+
export declare function ErrorState({ title, description, onRetry, }: {
|
|
72
|
+
title?: string;
|
|
73
|
+
description?: string;
|
|
74
|
+
onRetry?: () => void;
|
|
75
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
76
|
+
export {};
|
|
77
|
+
//# sourceMappingURL=_shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../../../src/brainyrich/pages/_shared.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { FC, PropsWithChildren } from 'react';
|
|
2
|
+
import { BrainyRichRootProps } from '../types';
|
|
3
|
+
|
|
4
|
+
export declare const useBrainyRich: () => BrainyRichRootProps;
|
|
5
|
+
export declare const BrainyRichProvider: FC<PropsWithChildren<BrainyRichRootProps>>;
|
|
6
|
+
//# sourceMappingURL=BrainyRichProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrainyRichProvider.d.ts","sourceRoot":"","sources":["../../../src/brainyrich/providers/BrainyRichProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAInD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAMpD,eAAO,MAAM,aAAa,2BAIzB,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,EAAE,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,CAsBzE,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* brainyrich adapter for the shared fe-libs AssistantWidget transport.
|
|
3
|
+
*
|
|
4
|
+
* The sandbox lifecycle + poll/stream loop now lives in
|
|
5
|
+
* `@burdenoff/fe-libs/shared/assistant` (one implementation for all products);
|
|
6
|
+
* this is a thin wrapper. The exported name/shape are unchanged so app-shell
|
|
7
|
+
* imports don't move. The public return type is a LOCALLY-declared structural
|
|
8
|
+
* mirror — never re-export fe-libs types (vite-plugin-dts path rewriting).
|
|
9
|
+
*/
|
|
10
|
+
interface AssistantSendArgs {
|
|
11
|
+
prompt: string;
|
|
12
|
+
onProgress: (partialText: string) => void;
|
|
13
|
+
signal: AbortSignal;
|
|
14
|
+
}
|
|
15
|
+
export interface AssistantTransport {
|
|
16
|
+
sendPrompt: (args: AssistantSendArgs) => Promise<{
|
|
17
|
+
text: string;
|
|
18
|
+
}>;
|
|
19
|
+
prewarm?: (level?: 'probe' | 'full') => void;
|
|
20
|
+
suspend?: () => void;
|
|
21
|
+
}
|
|
22
|
+
export declare function useSandboxAssistantTransport(): AssistantTransport;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=createSandboxAssistantTransport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createSandboxAssistantTransport.d.ts","sourceRoot":"","sources":["../../../src/brainyrich/services/createSandboxAssistantTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,UAAU,iBAAiB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnE,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,4BAA4B,IAAI,kBAAkB,CAMjE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { gatherPageContext as e } from "../utils/pageContext.js";
|
|
2
|
+
import { useSandboxAssistantTransport as t } from "@burdenoff/fe-libs/shared/assistant";
|
|
3
|
+
//#region src/brainyrich/services/createSandboxAssistantTransport.ts
|
|
4
|
+
function n() {
|
|
5
|
+
return t({
|
|
6
|
+
product: "brainyrich",
|
|
7
|
+
gatherPageContext: e,
|
|
8
|
+
enableCredits: !0
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { n as useSandboxAssistantTransport };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=createSandboxAssistantTransport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createSandboxAssistantTransport.js","names":[],"sources":["../../../src/brainyrich/services/createSandboxAssistantTransport.ts"],"sourcesContent":["/**\n * brainyrich adapter for the shared fe-libs AssistantWidget transport.\n *\n * The sandbox lifecycle + poll/stream loop now lives in\n * `@burdenoff/fe-libs/shared/assistant` (one implementation for all products);\n * this is a thin wrapper. The exported name/shape are unchanged so app-shell\n * imports don't move. The public return type is a LOCALLY-declared structural\n * mirror — never re-export fe-libs types (vite-plugin-dts path rewriting).\n */\n\nimport { useSandboxAssistantTransport as useSharedTransport } from '@burdenoff/fe-libs/shared/assistant';\nimport { gatherPageContext } from '../utils/pageContext';\n\ninterface AssistantSendArgs {\n prompt: string;\n onProgress: (partialText: string) => void;\n signal: AbortSignal;\n}\n\nexport interface AssistantTransport {\n sendPrompt: (args: AssistantSendArgs) => Promise<{ text: string }>;\n prewarm?: (level?: 'probe' | 'full') => void;\n suspend?: () => void;\n}\n\nexport function useSandboxAssistantTransport(): AssistantTransport {\n return useSharedTransport({\n product: 'brainyrich',\n gatherPageContext: gatherPageContext,\n enableCredits: true,\n });\n}\n"],"mappings":";;;AAyBA,SAAgB,IAAmD;AACjE,QAAO,EAAmB;EACxB,SAAS;EACU;EACnB,eAAe;EAChB,CAAC"}
|