@burdenoff/microfe-brainyrich 2026.1028.2 → 2026.1101.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/components/HouseholdDetailRail.d.ts +22 -0
- package/dist/brainyrich/components/HouseholdDetailRail.d.ts.map +1 -0
- package/dist/brainyrich/components/HouseholdDetailRail.js +39 -0
- package/dist/brainyrich/components/HouseholdDetailRail.js.map +1 -0
- package/dist/brainyrich/pages/FamilyPage.d.ts.map +1 -1
- package/dist/brainyrich/pages/FamilyPage.js +334 -314
- package/dist/brainyrich/pages/FamilyPage.js.map +1 -1
- package/dist/brainyrich/pages/FirmPage.js +8 -1
- package/dist/brainyrich/pages/FirmPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FamilyPage.js","names":[],"sources":["../../../src/brainyrich/pages/FamilyPage.tsx"],"sourcesContent":["import { useMemo, useState, type ReactNode } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n ResponsiveDialog,\n ResponsiveDialogClose,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Separator,\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Users,\n MoreHorizontal,\n Pencil,\n Archive,\n Eye,\n Mail,\n Wallet,\n Target,\n Briefcase,\n Building2,\n TrendingUp,\n UserCircle2,\n} from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport {\n usePagedHouseholds,\n useHousehold,\n useCreateHousehold,\n useUpdateHousehold,\n useArchiveHousehold,\n useAccounts,\n usePortfolios,\n useGoals,\n} from '../hooks/useBrainyRichApi';\nimport {\n ConfirmDialog,\n LoadMore,\n ActorName,\n StatTile,\n GlassCard,\n FadeIn,\n DonutChart,\n type DonutDatum,\n} from '../components';\nimport { TagField } from '../components/reuse/TagField';\nimport { DocumentsPanel } from '../components/reuse/DocumentsPanel';\nimport { hapticSuccess, showToast } from '../utils/nativeControls';\nimport { useTrackBrainyRichPageView } from '../hooks/useBrainyRichAnalytics';\nimport { CommentThread } from '../components/reuse/CommentThread';\nimport type {\n BraHousehold,\n BraHouseholdStatus,\n BraRiskProfile,\n BraCreateHouseholdInput,\n BraUpdateHouseholdInput,\n BraAccount,\n BraGoal,\n} from '../types/bra';\nimport {\n SectionShell,\n EmptyState,\n LoadingState,\n ErrorState,\n formatMoney,\n formatCompact,\n formatDate,\n statusBadgeVariant,\n} from './_shared';\n\nconst STATUS_OPTIONS: BraHouseholdStatus[] = ['PROSPECT', 'ONBOARDING', 'ACTIVE', 'INACTIVE'];\nconst RISK_OPTIONS: BraRiskProfile[] = [\n 'CONSERVATIVE',\n 'MODERATE',\n 'BALANCED',\n 'GROWTH',\n 'AGGRESSIVE',\n];\nconst CURRENCY_OPTIONS = ['USD', 'EUR', 'GBP', 'INR', 'AED', 'SGD'];\n\n/** Per-status donut tone (semantic — colour is never the only signal). */\nconst STATUS_TONE: Record<BraHouseholdStatus, DonutDatum['tone']> = {\n ACTIVE: 'brand',\n ONBOARDING: 'info',\n PROSPECT: 'warning',\n INACTIVE: 'neutral',\n};\n\ninterface HouseholdFormState {\n name: string;\n status: BraHouseholdStatus;\n riskProfile: BraRiskProfile;\n currency: string;\n advisorId: string;\n primaryContactName: string;\n primaryContactEmail: string;\n notes: string;\n tags: string;\n}\n\nconst emptyForm: HouseholdFormState = {\n name: '',\n status: 'PROSPECT',\n riskProfile: 'BALANCED',\n currency: 'USD',\n advisorId: '',\n primaryContactName: '',\n primaryContactEmail: '',\n notes: '',\n tags: '',\n};\n\nfunction toTags(raw: string): string[] {\n return raw\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n}\n\n/** Real market value of an account: settled cash + marked-to-market holdings. */\nfunction accountValue(a: BraAccount): number {\n const holdings = (a.holdings ?? []).reduce(\n (sum, h) => sum + (Number.isFinite(h.marketValue) ? h.marketValue : 0),\n 0\n );\n const cash = Number.isFinite(a.cashBalance) ? a.cashBalance : 0;\n return cash + holdings;\n}\n\nexport function FamilyPage() {\n const { t } = useI18n();\n const tr = (key: string, fallback: string, params?: Record<string, string | number>) =>\n tWithFallback(t, key, fallback, params);\n\n useTrackBrainyRichPageView('brainyrich.family.viewed');\n\n const {\n items: households,\n hasMore,\n loadMore,\n isLoading,\n isFetchingNextPage,\n isError,\n refetch,\n } = usePagedHouseholds();\n\n const createHousehold = useCreateHousehold();\n const updateHousehold = useUpdateHousehold();\n const archiveHousehold = useArchiveHousehold();\n\n const [formOpen, setFormOpen] = useState(false);\n const [editing, setEditing] = useState<BraHousehold | null>(null);\n const [form, setForm] = useState<HouseholdFormState>(emptyForm);\n const [detailId, setDetailId] = useState<string | undefined>(undefined);\n const [archiveTarget, setArchiveTarget] = useState<BraHousehold | null>(null);\n\n const setField = <K extends keyof HouseholdFormState>(key: K, value: HouseholdFormState[K]) =>\n setForm((prev) => ({ ...prev, [key]: value }));\n\n const openCreate = () => {\n setEditing(null);\n setForm(emptyForm);\n setFormOpen(true);\n };\n\n const openEdit = (h: BraHousehold) => {\n setEditing(h);\n setForm({\n name: h.name,\n status: h.status,\n riskProfile: h.riskProfile,\n currency: h.currency,\n advisorId: h.advisorId ?? '',\n primaryContactName: h.primaryContactName ?? '',\n primaryContactEmail: h.primaryContactEmail ?? '',\n notes: h.notes ?? '',\n tags: h.tags.join(', '),\n });\n setFormOpen(true);\n };\n\n const firm = useMemo(() => {\n const currency = households[0]?.currency ?? 'USD';\n const total = households.length;\n const active = households.filter((h) => h.status === 'ACTIVE').length;\n const prospects = households.filter((h) => h.status === 'PROSPECT').length;\n const onboarding = households.filter((h) => h.status === 'ONBOARDING').length;\n const aum = households.reduce((sum, h) => sum + (Number.isFinite(h.aum) ? h.aum : 0), 0);\n\n // AUM grouped by advisor (empty advisorId → unassigned bucket).\n const byAdvisor = new Map<string, number>();\n for (const h of households) {\n const key = h.advisorId?.trim() || '';\n byAdvisor.set(key, (byAdvisor.get(key) ?? 0) + (Number.isFinite(h.aum) ? h.aum : 0));\n }\n const advisors = Array.from(byAdvisor.entries())\n .map(([advisorId, value]) => ({ advisorId, value }))\n .sort((a, b) => b.value - a.value);\n const advisorCount = advisors.filter((a) => a.advisorId).length;\n const advisorMax = advisors.reduce((m, a) => Math.max(m, a.value), 0);\n\n // Book mix by status → donut.\n const statusMix: DonutDatum[] = (STATUS_OPTIONS as BraHouseholdStatus[])\n .map((s) => ({\n label: s.charAt(0) + s.slice(1).toLowerCase(),\n value: households.filter((h) => h.status === s).length,\n tone: STATUS_TONE[s],\n }))\n .filter((d) => d.value > 0);\n\n return {\n currency,\n total,\n active,\n prospects,\n onboarding,\n aum,\n advisors,\n advisorCount,\n advisorMax,\n statusMix,\n };\n }, [households]);\n\n const submitting = createHousehold.isPending || updateHousehold.isPending;\n const canSubmit = form.name.trim().length > 0 && !submitting;\n\n const handleSubmit = () => {\n if (!canSubmit) return;\n if (editing) {\n const input: BraUpdateHouseholdInput = {\n name: form.name.trim(),\n status: form.status,\n riskProfile: form.riskProfile,\n currency: form.currency,\n primaryContactName: form.primaryContactName.trim() || null,\n primaryContactEmail: form.primaryContactEmail.trim() || null,\n notes: form.notes.trim() || null,\n tags: toTags(form.tags),\n };\n updateHousehold.mutate(\n { id: editing.id, input },\n {\n onSuccess: () => {\n hapticSuccess();\n void showToast(tr('brainyrich.households.saved', 'Household saved'));\n setFormOpen(false);\n },\n }\n );\n } else {\n const input: BraCreateHouseholdInput = {\n name: form.name.trim(),\n status: form.status,\n riskProfile: form.riskProfile,\n currency: form.currency,\n primaryContactName: form.primaryContactName.trim() || null,\n primaryContactEmail: form.primaryContactEmail.trim() || null,\n notes: form.notes.trim() || null,\n tags: toTags(form.tags),\n };\n if (form.advisorId.trim()) input.advisorId = form.advisorId.trim();\n createHousehold.mutate(input, {\n onSuccess: () => {\n hapticSuccess();\n void showToast(tr('brainyrich.households.created', 'Household created'));\n setFormOpen(false);\n },\n });\n }\n };\n\n const showFirm = !isLoading && !isError && households.length > 0;\n\n return (\n <div data-tour=\"brainyrich-page-households\" className=\"contents\">\n <SectionShell\n title={tr('brainyrich.households.title', 'Households')}\n description={tr(\n 'brainyrich.households.description',\n 'Client households and family units under advisement'\n )}\n badges={\n households.length\n ? [\n tr('brainyrich.households.count', '{count} households', {\n count: households.length,\n }),\n ]\n : []\n }\n actionLabel={tr('brainyrich.households.new', 'New household')}\n onAction={openCreate}\n >\n {showFirm ? (\n <FirmOverview firm={firm} tr={tr} />\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n <StatTile label={tr('brainyrich.households.kpi.aum', 'Total AUM')} value=\"—\" glow />\n <StatTile label={tr('brainyrich.households.kpi.total', 'Households')} value=\"—\" />\n <StatTile label={tr('brainyrich.households.kpi.active', 'Active')} value=\"—\" />\n <StatTile label={tr('brainyrich.households.kpi.prospects', 'Prospects')} value=\"—\" />\n </div>\n )}\n\n {isLoading ? (\n <LoadingState rows={5} />\n ) : isError ? (\n <ErrorState\n title={tr('brainyrich.households.error.title', 'Could not load households')}\n onRetry={() => refetch()}\n />\n ) : households.length === 0 ? (\n <EmptyState\n icon={<Users className=\"size-6\" aria-hidden=\"true\" />}\n title={tr('brainyrich.households.empty.title', 'No households yet')}\n description={tr(\n 'brainyrich.households.empty.description',\n 'Create your first client household to start organizing accounts, portfolios and goals.'\n )}\n action={\n <Button size=\"sm\" onClick={openCreate}>\n {tr('brainyrich.households.new', 'New household')}\n </Button>\n }\n />\n ) : (\n <FadeIn as=\"section\">\n <Card className=\"overflow-hidden\">\n <CardContent className=\"p-0\">\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n <TableRow className=\"hover:bg-transparent\">\n <TableHead>{tr('brainyrich.households.col.name', 'Household')}</TableHead>\n <TableHead>{tr('brainyrich.households.col.status', 'Status')}</TableHead>\n <TableHead className=\"hidden md:table-cell\">\n {tr('brainyrich.households.col.risk', 'Risk profile')}\n </TableHead>\n <TableHead className=\"hidden lg:table-cell\">\n {tr('brainyrich.households.col.contact', 'Primary contact')}\n </TableHead>\n <TableHead className=\"hidden sm:table-cell\">\n {tr('brainyrich.households.col.advisor', 'Advisor')}\n </TableHead>\n <TableHead className=\"text-right\">\n {tr('brainyrich.households.col.aum', 'AUM')}\n </TableHead>\n <TableHead className=\"w-12 text-right\">\n <span className=\"sr-only\">\n {tr('brainyrich.households.col.actions', 'Actions')}\n </span>\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {households.map((h) => {\n const share = firm.aum > 0 ? Math.min(100, (h.aum / firm.aum) * 100) : 0;\n return (\n <TableRow\n key={h.id}\n className=\"group cursor-pointer transition-colors\"\n onClick={() => setDetailId(h.id)}\n >\n <TableCell>\n <span className=\"font-medium text-text-primary transition-colors group-hover:text-[var(--color-action-primary-text)]\">\n {h.name}\n </span>\n {h.tags.length > 0 ? (\n <div className=\"mt-1 flex flex-wrap gap-1\">\n {h.tags.slice(0, 3).map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n {h.tags.length > 3 ? (\n <Badge variant=\"outline\" className=\"text-xs\">\n +{h.tags.length - 3}\n </Badge>\n ) : null}\n </div>\n ) : null}\n </TableCell>\n <TableCell>\n <Badge variant={statusBadgeVariant(h.status)}>{h.status}</Badge>\n </TableCell>\n <TableCell className=\"hidden md:table-cell\">\n <Badge variant=\"secondary\">{h.riskProfile}</Badge>\n </TableCell>\n <TableCell className=\"hidden lg:table-cell\">\n {h.primaryContactName ? (\n <div className=\"space-y-0.5\">\n <p className=\"text-sm text-text-primary\">\n {h.primaryContactName}\n </p>\n {h.primaryContactEmail ? (\n <p className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Mail className=\"size-3\" aria-hidden=\"true\" />\n {h.primaryContactEmail}\n </p>\n ) : null}\n </div>\n ) : (\n <span className=\"text-sm text-text-muted\">—</span>\n )}\n </TableCell>\n <TableCell className=\"hidden sm:table-cell\">\n {h.advisorId ? (\n <span className=\"flex items-center gap-1.5 text-sm text-text-secondary\">\n <UserCircle2 className=\"size-3.5\" aria-hidden=\"true\" />\n <ActorName id={h.advisorId} />\n </span>\n ) : (\n <span className=\"text-sm text-text-muted\">\n {tr('brainyrich.common.unassigned', 'Unassigned')}\n </span>\n )}\n </TableCell>\n <TableCell className=\"text-right\">\n <div className=\"flex flex-col items-end gap-1\">\n <span className=\"font-medium tabular-nums text-text-primary\">\n {formatMoney(h.aum, h.currency)}\n </span>\n <span\n className=\"h-1 w-16 overflow-hidden rounded-full bg-bg-sunken\"\n aria-hidden=\"true\"\n >\n <span\n className=\"block h-full rounded-full bg-[var(--color-action-primary-bg)]\"\n style={{ width: `${share}%` }}\n />\n </span>\n </div>\n </TableCell>\n <TableCell className=\"text-right\" onClick={(e) => e.stopPropagation()}>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={tr(\n 'brainyrich.households.rowMenu',\n 'Household actions'\n )}\n >\n <MoreHorizontal className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onClick={() => setDetailId(h.id)}>\n <Eye className=\"size-4\" aria-hidden=\"true\" />\n {tr('brainyrich.households.action.view', 'View details')}\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => openEdit(h)}>\n <Pencil className=\"size-4\" aria-hidden=\"true\" />\n {tr('brainyrich.households.action.edit', 'Edit')}\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"text-status-error-text focus:text-status-error-text\"\n onClick={() => setArchiveTarget(h)}\n >\n <Archive className=\"size-4\" aria-hidden=\"true\" />\n {tr('brainyrich.households.action.archive', 'Archive')}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n </CardContent>\n </Card>\n </FadeIn>\n )}\n\n {!isLoading && !isError && households.length > 0 ? (\n <LoadMore\n hasMore={hasMore}\n onClick={loadMore}\n loading={isFetchingNextPage}\n label={tr('brainyrich.households.loadMore', 'Load more households')}\n />\n ) : null}\n\n <HouseholdFormDialog\n open={formOpen}\n onOpenChange={setFormOpen}\n editing={editing}\n form={form}\n setField={setField}\n onSubmit={handleSubmit}\n canSubmit={canSubmit}\n submitting={submitting}\n tr={tr}\n />\n\n <HouseholdDetailSheet\n householdId={detailId}\n onClose={() => setDetailId(undefined)}\n onEdit={(h) => {\n setDetailId(undefined);\n openEdit(h);\n }}\n tr={tr}\n />\n\n <ConfirmDialog\n open={Boolean(archiveTarget)}\n onOpenChange={(o) => (!o ? setArchiveTarget(null) : undefined)}\n title={tr('brainyrich.households.archive.title', 'Archive household?')}\n description={tr(\n 'brainyrich.households.archive.description',\n 'Archiving “{name}” removes it from the active list. You can still access its records, but it will no longer appear here.',\n { name: archiveTarget?.name ?? '' }\n )}\n confirmLabel={tr('brainyrich.households.action.archive', 'Archive')}\n pendingLabel={tr('brainyrich.households.archive.pending', 'Archiving…')}\n destructive\n pending={archiveHousehold.isPending}\n onConfirm={() => {\n if (!archiveTarget) return;\n archiveHousehold.mutate(archiveTarget.id, {\n onSuccess: () => {\n hapticSuccess();\n void showToast(\n tr('brainyrich.households.archived', 'Household archived', {\n name: archiveTarget.name,\n })\n );\n setArchiveTarget(null);\n },\n });\n }}\n />\n </SectionShell>\n </div>\n );\n}\n\n// ============================================================================\n// Firm overview — practice-wide KPIs + AUM-by-advisor + book mix\n// ============================================================================\n\ninterface FirmData {\n currency: string;\n total: number;\n active: number;\n prospects: number;\n onboarding: number;\n aum: number;\n advisors: { advisorId: string; value: number }[];\n advisorCount: number;\n advisorMax: number;\n statusMix: DonutDatum[];\n}\n\nfunction FirmOverview({\n firm,\n tr,\n}: {\n firm: FirmData;\n tr: (key: string, fallback: string, params?: Record<string, string | number>) => string;\n}) {\n return (\n <FadeIn as=\"section\" className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n <StatTile\n label={tr('brainyrich.households.kpi.aum', 'Total AUM')}\n value={formatMoney(firm.aum, firm.currency)}\n glow\n icon={<Wallet className=\"size-4\" aria-hidden=\"true\" />}\n />\n <StatTile\n label={tr('brainyrich.households.kpi.total', 'Households')}\n value={String(firm.total)}\n icon={<Users className=\"size-4\" aria-hidden=\"true\" />}\n />\n <StatTile\n label={tr('brainyrich.households.kpi.active', 'Active')}\n value={String(firm.active)}\n icon={<TrendingUp className=\"size-4\" aria-hidden=\"true\" />}\n />\n <StatTile\n label={tr('brainyrich.households.kpi.prospects', 'Prospects')}\n value={String(firm.prospects)}\n icon={<Target className=\"size-4\" aria-hidden=\"true\" />}\n />\n </div>\n\n <div className=\"grid gap-4 lg:grid-cols-3\">\n <GlassCard className=\"lg:col-span-2\">\n <div className=\"mb-4 flex items-center justify-between gap-2\">\n <div>\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('brainyrich.households.firm.byAdvisor', 'AUM by advisor')}\n </h2>\n <p className=\"text-xs text-text-secondary\">\n {tr('brainyrich.households.firm.advisorCount', '{count} advisors', {\n count: firm.advisorCount,\n })}\n </p>\n </div>\n <Building2 className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n </div>\n <ul\n className=\"space-y-3.5\"\n aria-label={tr('brainyrich.households.firm.byAdvisor', 'AUM by advisor')}\n >\n {firm.advisors.map((a) => {\n const width =\n firm.advisorMax > 0 ? Math.max(2, (a.value / firm.advisorMax) * 100) : 0;\n return (\n <li key={a.advisorId || 'unassigned'} className=\"space-y-1.5\">\n <div className=\"flex items-center justify-between gap-3 text-xs\">\n <span className=\"flex items-center gap-1.5 truncate font-medium text-text-primary\">\n <UserCircle2\n className=\"size-3.5 shrink-0 text-text-secondary\"\n aria-hidden=\"true\"\n />\n {a.advisorId ? (\n <ActorName id={a.advisorId} />\n ) : (\n tr('brainyrich.common.unassigned', 'Unassigned')\n )}\n </span>\n <span className=\"shrink-0 tabular-nums text-text-secondary\">\n {formatCompact(a.value, firm.currency)}\n </span>\n </div>\n <div\n className=\"h-2 w-full overflow-hidden rounded-full bg-bg-sunken\"\n role=\"progressbar\"\n aria-valuenow={Math.round(width)}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={a.advisorId || 'Unassigned'}\n >\n <div\n className=\"h-full rounded-full bg-[var(--color-action-primary-bg)] transition-[width] duration-500 ease-out\"\n style={{ width: `${width}%` }}\n />\n </div>\n </li>\n );\n })}\n </ul>\n </GlassCard>\n\n <GlassCard>\n <div className=\"mb-2 flex items-center justify-between gap-2\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('brainyrich.households.firm.bookMix', 'Book by status')}\n </h2>\n </div>\n {firm.statusMix.length > 0 ? (\n <DonutChart\n data={firm.statusMix}\n centerValue={String(firm.total)}\n centerLabel={tr('brainyrich.households.kpi.total', 'Households')}\n legend\n height={200}\n valueFormatter={(v) => String(v)}\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {tr('brainyrich.households.firm.noMix', 'No households to chart yet.')}\n </p>\n )}\n </GlassCard>\n </div>\n </FadeIn>\n );\n}\n\n// ============================================================================\n// Create / edit dialog\n// ============================================================================\n\ninterface HouseholdFormDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n editing: BraHousehold | null;\n form: HouseholdFormState;\n setField: <K extends keyof HouseholdFormState>(key: K, value: HouseholdFormState[K]) => void;\n onSubmit: () => void;\n canSubmit: boolean;\n submitting: boolean;\n tr: (key: string, fallback: string, params?: Record<string, string | number>) => string;\n}\n\nfunction HouseholdFormDialog({\n open,\n onOpenChange,\n editing,\n form,\n setField,\n onSubmit,\n canSubmit,\n submitting,\n tr,\n}: HouseholdFormDialogProps) {\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {editing\n ? tr('brainyrich.households.form.editTitle', 'Edit household')\n : tr('brainyrich.households.form.createTitle', 'New household')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'brainyrich.households.form.subtitle',\n 'A household groups a client family’s accounts, portfolios and goals.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form\n className=\"space-y-4\"\n onSubmit={(e) => {\n e.preventDefault();\n onSubmit();\n }}\n >\n <div className=\"space-y-2\">\n <Label htmlFor=\"hh-name\">{tr('brainyrich.households.form.name', 'Name')} *</Label>\n <Input\n id=\"hh-name\"\n value={form.name}\n onChange={(e) => setField('name', e.target.value)}\n placeholder={tr('brainyrich.households.form.namePh', 'e.g. The Raghavan Family')}\n required\n />\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>{tr('brainyrich.households.form.status', 'Status')}</Label>\n <Select\n value={form.status}\n onValueChange={(v) => setField('status', v as BraHouseholdStatus)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {STATUS_OPTIONS.map((s) => (\n <SelectItem key={s} value={s}>\n {s}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-2\">\n <Label>{tr('brainyrich.households.form.risk', 'Risk profile')}</Label>\n <Select\n value={form.riskProfile}\n onValueChange={(v) => setField('riskProfile', v as BraRiskProfile)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {RISK_OPTIONS.map((r) => (\n <SelectItem key={r} value={r}>\n {r}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"hh-contact-name\">\n {tr('brainyrich.households.form.contactName', 'Primary contact')}\n </Label>\n <Input\n id=\"hh-contact-name\"\n value={form.primaryContactName}\n onChange={(e) => setField('primaryContactName', e.target.value)}\n placeholder={tr('brainyrich.households.form.contactNamePh', 'Full name')}\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"hh-contact-email\">\n {tr('brainyrich.households.form.contactEmail', 'Contact email')}\n </Label>\n <Input\n id=\"hh-contact-email\"\n type=\"email\"\n value={form.primaryContactEmail}\n onChange={(e) => setField('primaryContactEmail', e.target.value)}\n placeholder=\"name@example.com\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>{tr('brainyrich.households.form.currency', 'Currency')}</Label>\n <Select value={form.currency} onValueChange={(v) => setField('currency', v)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CURRENCY_OPTIONS.map((c) => (\n <SelectItem key={c} value={c}>\n {c}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {!editing ? (\n <div className=\"space-y-2\">\n <Label htmlFor=\"hh-advisor\">\n {tr('brainyrich.households.form.advisor', 'Advisor ID')}\n </Label>\n <Input\n id=\"hh-advisor\"\n value={form.advisorId}\n onChange={(e) => setField('advisorId', e.target.value)}\n placeholder={tr('brainyrich.households.form.advisorPh', 'Optional')}\n />\n </div>\n ) : null}\n </div>\n\n {editing ? (\n <TagField\n entityType=\"brainyrich-household\"\n entityId={editing.id}\n label={tr('brainyrich.households.form.tags', 'Tags')}\n />\n ) : (\n <div className=\"space-y-2\">\n <Label htmlFor=\"hh-tags\">{tr('brainyrich.households.form.tags', 'Tags')}</Label>\n <Input\n id=\"hh-tags\"\n value={form.tags}\n onChange={(e) => setField('tags', e.target.value)}\n placeholder={tr('brainyrich.households.form.tagsPh', 'Comma-separated')}\n />\n </div>\n )}\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"hh-notes\">{tr('brainyrich.households.form.notes', 'Notes')}</Label>\n <Textarea\n id=\"hh-notes\"\n value={form.notes}\n onChange={(e) => setField('notes', e.target.value)}\n rows={3}\n />\n </div>\n\n <ResponsiveDialogFooter>\n <ResponsiveDialogClose asChild>\n <Button type=\"button\" variant=\"outline\">\n {tr('brainyrich.common.cancel', 'Cancel')}\n </Button>\n </ResponsiveDialogClose>\n <Button type=\"submit\" disabled={!canSubmit}>\n {submitting\n ? tr('brainyrich.common.saving', 'Saving…')\n : editing\n ? tr('brainyrich.common.save', 'Save changes')\n : tr('brainyrich.households.form.create', 'Create household')}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ============================================================================\n// Detail sheet\n// ============================================================================\n\ninterface HouseholdDetailSheetProps {\n householdId: string | undefined;\n onClose: () => void;\n onEdit: (h: BraHousehold) => void;\n tr: (key: string, fallback: string, params?: Record<string, string | number>) => string;\n}\n\nfunction HouseholdDetailSheet({ householdId, onClose, onEdit, tr }: HouseholdDetailSheetProps) {\n const { data: household, isLoading, isError, refetch } = useHousehold(householdId);\n const { data: accounts } = useAccounts(householdId ? { householdId } : {});\n const { data: portfolios } = usePortfolios(householdId ? { householdId } : {});\n const { data: goals } = useGoals(householdId ? { householdId } : {});\n\n const open = Boolean(householdId);\n\n const accountRows = useMemo(\n () =>\n [...accounts]\n .map((a) => ({ account: a, value: accountValue(a) }))\n .sort((x, y) => y.value - x.value),\n [accounts]\n );\n const invested = accountRows.reduce((s, r) => s + r.value, 0);\n const allocDonut: DonutDatum[] = accountRows\n .filter((r) => r.value > 0)\n .slice(0, 6)\n .map((r) => ({ label: r.account.name, value: r.value }));\n\n return (\n <Sheet open={open} onOpenChange={(o) => (!o ? onClose() : undefined)}>\n <SheetContent className=\"flex w-full flex-col gap-0 overflow-y-auto p-0 sm:max-w-xl\">\n {isLoading ? (\n <div className=\"p-6\">\n <LoadingState rows={4} />\n </div>\n ) : isError ? (\n <div className=\"p-6\">\n <ErrorState\n title={tr('brainyrich.households.detail.error', 'Could not load household')}\n onRetry={() => refetch()}\n />\n </div>\n ) : !household ? (\n <div className=\"p-6\">\n <EmptyState\n icon={<Users className=\"size-6\" aria-hidden=\"true\" />}\n title={tr('brainyrich.households.detail.missing', 'Household not found')}\n />\n </div>\n ) : (\n <div className=\"space-y-5 p-6\">\n <SheetHeader className=\"space-y-2 p-0 text-left\">\n <SheetTitle className=\"text-xl\">{household.name}</SheetTitle>\n <SheetDescription className=\"sr-only\">\n {tr('brainyrich.households.detail.subtitle', 'Household overview')}\n </SheetDescription>\n <div className=\"flex flex-wrap items-center gap-2 pt-1\">\n <Badge variant={statusBadgeVariant(household.status)}>{household.status}</Badge>\n <Badge variant=\"secondary\">{household.riskProfile}</Badge>\n </div>\n </SheetHeader>\n\n {/* Hero — AUM + primary contact on a glass surface with emerald glow */}\n <GlassCard glow>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">\n {tr('brainyrich.households.detail.aum', 'AUM')}\n </p>\n <p className=\"mt-1 text-3xl font-semibold tracking-tight text-text-primary\">\n {formatMoney(household.aum, household.currency)}\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">{household.currency}</p>\n </div>\n <div className=\"sm:text-right\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">\n {tr('brainyrich.households.detail.contact', 'Primary contact')}\n </p>\n <p className=\"mt-1 text-base font-medium text-text-primary\">\n {household.primaryContactName ?? '—'}\n </p>\n {household.primaryContactEmail ? (\n <a\n href={`mailto:${household.primaryContactEmail}`}\n className=\"mt-0.5 inline-flex items-center gap-1 text-xs text-text-secondary hover:text-[var(--color-action-primary-text)] sm:justify-end\"\n >\n <Mail className=\"size-3\" aria-hidden=\"true\" />\n {household.primaryContactEmail}\n </a>\n ) : null}\n </div>\n </div>\n </GlassCard>\n\n {/* Portfolio counts */}\n <div className=\"grid grid-cols-3 gap-3\">\n <SummaryTile\n icon={<Wallet className=\"size-4\" aria-hidden=\"true\" />}\n label={tr('brainyrich.households.detail.accounts', 'Accounts')}\n value={String(accounts.length)}\n />\n <SummaryTile\n icon={<Briefcase className=\"size-4\" aria-hidden=\"true\" />}\n label={tr('brainyrich.households.detail.portfolios', 'Portfolios')}\n value={String(portfolios.length)}\n />\n <SummaryTile\n icon={<Target className=\"size-4\" aria-hidden=\"true\" />}\n label={tr('brainyrich.households.detail.goals', 'Goals')}\n value={String(goals.length)}\n />\n </div>\n\n {household.tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {household.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\">\n {tag}\n </Badge>\n ))}\n </div>\n ) : null}\n\n <Tabs defaultValue=\"overview\" className=\"w-full\">\n <TabsList className=\"w-full justify-start overflow-x-auto\">\n <TabsTrigger value=\"overview\">\n {tr('brainyrich.households.tab.overview', 'Overview')}\n </TabsTrigger>\n <TabsTrigger value=\"accounts\">\n {tr('brainyrich.households.detail.accounts', 'Accounts')}\n </TabsTrigger>\n <TabsTrigger value=\"goals\">\n {tr('brainyrich.households.detail.goals', 'Goals')}\n </TabsTrigger>\n <TabsTrigger value=\"documents\">\n {tr('brainyrich.households.detail.documents', 'Documents')}\n </TabsTrigger>\n <TabsTrigger value=\"discussion\">\n {tr('brainyrich.households.detail.discussion', 'Discussion')}\n </TabsTrigger>\n </TabsList>\n\n {/* Overview */}\n <TabsContent value=\"overview\" className=\"mt-4 space-y-4\">\n {allocDonut.length > 0 ? (\n <Card>\n <CardContent className=\"p-4\">\n <h3 className=\"mb-3 text-sm font-medium text-text-primary\">\n {tr('brainyrich.households.detail.allocation', 'Where assets sit')}\n </h3>\n <DonutChart\n data={allocDonut}\n centerValue={formatCompact(invested, household.currency)}\n centerLabel={tr('brainyrich.households.detail.invested', 'Invested')}\n height={200}\n valueFormatter={(v) => formatMoney(v, household.currency)}\n />\n </CardContent>\n </Card>\n ) : null}\n\n <Card>\n <CardContent className=\"space-y-3 p-4 text-sm\">\n <div className=\"flex items-center justify-between gap-4\">\n <span className=\"text-text-secondary\">\n {tr('brainyrich.households.detail.advisor', 'Advisor')}\n </span>\n <span className=\"text-right font-medium text-text-primary\">\n {household.advisorId ? (\n <ActorName id={household.advisorId} />\n ) : (\n tr('brainyrich.common.unassigned', 'Unassigned')\n )}\n </span>\n </div>\n <Separator />\n <DetailRow\n label={tr('brainyrich.households.detail.created', 'Created')}\n value={formatDate(household.createdAt)}\n />\n <DetailRow\n label={tr('brainyrich.households.detail.updated', 'Last updated')}\n value={formatDate(household.updatedAt)}\n />\n </CardContent>\n </Card>\n\n {household.notes ? (\n <Card>\n <CardContent className=\"space-y-1 p-4\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {tr('brainyrich.households.detail.summary', 'Summary')}\n </p>\n <p className=\"text-sm text-text-primary\">{household.notes}</p>\n </CardContent>\n </Card>\n ) : null}\n\n <Card>\n <CardContent className=\"p-4\">\n <TagField entityType=\"brainyrich-household\" entityId={household.id} />\n </CardContent>\n </Card>\n </TabsContent>\n\n {/* Accounts */}\n <TabsContent value=\"accounts\" className=\"mt-4 space-y-2\">\n {accountRows.length === 0 ? (\n <EmptyState\n icon={<Wallet className=\"size-6\" aria-hidden=\"true\" />}\n title={tr('brainyrich.households.detail.noAccounts', 'No accounts linked')}\n />\n ) : (\n accountRows.map(({ account, value }) => (\n <div\n key={account.id}\n className=\"flex items-center justify-between gap-3 rounded-lg border border-border-subtle bg-bg-surface p-3 transition-colors hover:border-[var(--color-action-primary-border)]\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {account.name}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {account.institution ?? account.type}\n {account.accountMask ? ` ·••${account.accountMask}` : ''}\n </p>\n </div>\n <span className=\"shrink-0 text-sm font-semibold tabular-nums text-status-success-text\">\n {formatMoney(value, account.currency)}\n </span>\n </div>\n ))\n )}\n </TabsContent>\n\n {/* Goals */}\n <TabsContent value=\"goals\" className=\"mt-4 space-y-2\">\n {goals.length === 0 ? (\n <EmptyState\n icon={<Target className=\"size-6\" aria-hidden=\"true\" />}\n title={tr('brainyrich.households.detail.noGoals', 'No goals set')}\n />\n ) : (\n goals.map((g: BraGoal) => (\n <div\n key={g.id}\n className=\"flex items-center justify-between gap-3 rounded-lg border border-border-subtle bg-bg-surface p-3\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{g.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {tr('brainyrich.households.detail.goalTarget', 'Target')}{' '}\n {formatMoney(g.targetAmount, g.currency)} · {formatDate(g.targetDate)}\n </p>\n </div>\n <Badge variant={statusBadgeVariant(g.projection)} className=\"shrink-0\">\n {g.projection.replace(/_/g, ' ')}\n </Badge>\n </div>\n ))\n )}\n </TabsContent>\n\n {/* Documents */}\n <TabsContent value=\"documents\" className=\"mt-4\">\n <DocumentsPanel\n entityType=\"brainyrich-household\"\n entityId={household.id}\n heading={tr('brainyrich.households.detail.documents', 'Documents')}\n />\n </TabsContent>\n\n {/* Discussion */}\n <TabsContent value=\"discussion\" className=\"mt-4\">\n <CommentThread\n entityType=\"brainyrich-household\"\n entityId={household.id}\n heading={tr('brainyrich.households.detail.discussion', 'Discussion')}\n />\n </TabsContent>\n </Tabs>\n\n <Button variant=\"outline\" className=\"w-full\" onClick={() => onEdit(household)}>\n <Pencil className=\"size-4\" aria-hidden=\"true\" />\n {tr('brainyrich.households.action.edit', 'Edit')}\n </Button>\n </div>\n )}\n </SheetContent>\n </Sheet>\n );\n}\n\nfunction SummaryTile({ icon, label, value }: { icon: ReactNode; label: string; value: string }) {\n return (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3 text-center transition-shadow motion-safe:hover:shadow-[var(--shadow-pop-hover,var(--shadow-elevation-2))]\">\n <div className=\"mx-auto mb-1 flex size-8 items-center justify-center rounded-full bg-bg-sunken text-text-secondary\">\n {icon}\n </div>\n <p className=\"text-lg font-semibold text-text-primary\">{value}</p>\n <p className=\"text-xs text-text-secondary\">{label}</p>\n </div>\n );\n}\n\nfunction DetailRow({ label, value }: { label: string; value: string }) {\n return (\n <div className=\"flex items-center justify-between gap-4\">\n <span className=\"text-text-secondary\">{label}</span>\n <span className=\"text-right font-medium text-text-primary\">{value}</span>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FamilyPage.js","names":[],"sources":["../../../src/brainyrich/pages/FamilyPage.tsx"],"sourcesContent":["import { useMemo, useState, type ReactNode } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n ResponsiveDialog,\n ResponsiveDialogClose,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Separator,\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Users,\n MoreHorizontal,\n Pencil,\n Archive,\n Eye,\n Mail,\n Wallet,\n Target,\n Briefcase,\n Building2,\n TrendingUp,\n UserCircle2,\n} from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport {\n usePagedHouseholds,\n useHousehold,\n useCreateHousehold,\n useUpdateHousehold,\n useArchiveHousehold,\n useAccounts,\n usePortfolios,\n useGoals,\n} from '../hooks/useBrainyRichApi';\nimport {\n ConfirmDialog,\n LoadMore,\n ActorName,\n StatTile,\n GlassCard,\n FadeIn,\n DonutChart,\n type DonutDatum,\n} from '../components';\nimport { TagField } from '../components/reuse/TagField';\nimport { HOUSEHOLD_ENTITY_TYPE, HouseholdDetailRail } from '../components/HouseholdDetailRail';\nimport { DocumentsPanel } from '../components/reuse/DocumentsPanel';\nimport { hapticSuccess, showToast } from '../utils/nativeControls';\nimport { useTrackBrainyRichPageView } from '../hooks/useBrainyRichAnalytics';\nimport type {\n BraHousehold,\n BraHouseholdStatus,\n BraRiskProfile,\n BraCreateHouseholdInput,\n BraUpdateHouseholdInput,\n BraAccount,\n BraGoal,\n BraPortfolio,\n} from '../types/bra';\nimport {\n SectionShell,\n EmptyState,\n LoadingState,\n ErrorState,\n formatMoney,\n formatCompact,\n formatDate,\n formatPct,\n statusBadgeVariant,\n} from './_shared';\n\nconst STATUS_OPTIONS: BraHouseholdStatus[] = ['PROSPECT', 'ONBOARDING', 'ACTIVE', 'INACTIVE'];\nconst RISK_OPTIONS: BraRiskProfile[] = [\n 'CONSERVATIVE',\n 'MODERATE',\n 'BALANCED',\n 'GROWTH',\n 'AGGRESSIVE',\n];\nconst CURRENCY_OPTIONS = ['USD', 'EUR', 'GBP', 'INR', 'AED', 'SGD'];\n\n/** Per-status donut tone (semantic — colour is never the only signal). */\nconst STATUS_TONE: Record<BraHouseholdStatus, DonutDatum['tone']> = {\n ACTIVE: 'brand',\n ONBOARDING: 'info',\n PROSPECT: 'warning',\n INACTIVE: 'neutral',\n};\n\ninterface HouseholdFormState {\n name: string;\n status: BraHouseholdStatus;\n riskProfile: BraRiskProfile;\n currency: string;\n advisorId: string;\n primaryContactName: string;\n primaryContactEmail: string;\n notes: string;\n tags: string;\n}\n\nconst emptyForm: HouseholdFormState = {\n name: '',\n status: 'PROSPECT',\n riskProfile: 'BALANCED',\n currency: 'USD',\n advisorId: '',\n primaryContactName: '',\n primaryContactEmail: '',\n notes: '',\n tags: '',\n};\n\nfunction toTags(raw: string): string[] {\n return raw\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n}\n\n/** Real market value of an account: settled cash + marked-to-market holdings. */\nfunction accountValue(a: BraAccount): number {\n const holdings = (a.holdings ?? []).reduce(\n (sum, h) => sum + (Number.isFinite(h.marketValue) ? h.marketValue : 0),\n 0\n );\n const cash = Number.isFinite(a.cashBalance) ? a.cashBalance : 0;\n return cash + holdings;\n}\n\nexport function FamilyPage() {\n const { t } = useI18n();\n const tr = (key: string, fallback: string, params?: Record<string, string | number>) =>\n tWithFallback(t, key, fallback, params);\n\n useTrackBrainyRichPageView('brainyrich.family.viewed');\n\n const {\n items: households,\n hasMore,\n loadMore,\n isLoading,\n isFetchingNextPage,\n isError,\n refetch,\n } = usePagedHouseholds();\n\n const createHousehold = useCreateHousehold();\n const updateHousehold = useUpdateHousehold();\n const archiveHousehold = useArchiveHousehold();\n\n const [formOpen, setFormOpen] = useState(false);\n const [editing, setEditing] = useState<BraHousehold | null>(null);\n const [form, setForm] = useState<HouseholdFormState>(emptyForm);\n const [detailId, setDetailId] = useState<string | undefined>(undefined);\n const [archiveTarget, setArchiveTarget] = useState<BraHousehold | null>(null);\n\n const setField = <K extends keyof HouseholdFormState>(key: K, value: HouseholdFormState[K]) =>\n setForm((prev) => ({ ...prev, [key]: value }));\n\n const openCreate = () => {\n setEditing(null);\n setForm(emptyForm);\n setFormOpen(true);\n };\n\n const openEdit = (h: BraHousehold) => {\n setEditing(h);\n setForm({\n name: h.name,\n status: h.status,\n riskProfile: h.riskProfile,\n currency: h.currency,\n advisorId: h.advisorId ?? '',\n primaryContactName: h.primaryContactName ?? '',\n primaryContactEmail: h.primaryContactEmail ?? '',\n notes: h.notes ?? '',\n tags: h.tags.join(', '),\n });\n setFormOpen(true);\n };\n\n const firm = useMemo(() => {\n const currency = households[0]?.currency ?? 'USD';\n const total = households.length;\n const active = households.filter((h) => h.status === 'ACTIVE').length;\n const prospects = households.filter((h) => h.status === 'PROSPECT').length;\n const onboarding = households.filter((h) => h.status === 'ONBOARDING').length;\n const aum = households.reduce((sum, h) => sum + (Number.isFinite(h.aum) ? h.aum : 0), 0);\n\n // AUM grouped by advisor (empty advisorId → unassigned bucket).\n const byAdvisor = new Map<string, number>();\n for (const h of households) {\n const key = h.advisorId?.trim() || '';\n byAdvisor.set(key, (byAdvisor.get(key) ?? 0) + (Number.isFinite(h.aum) ? h.aum : 0));\n }\n const advisors = Array.from(byAdvisor.entries())\n .map(([advisorId, value]) => ({ advisorId, value }))\n .sort((a, b) => b.value - a.value);\n const advisorCount = advisors.filter((a) => a.advisorId).length;\n const advisorMax = advisors.reduce((m, a) => Math.max(m, a.value), 0);\n\n // Book mix by status → donut.\n const statusMix: DonutDatum[] = (STATUS_OPTIONS as BraHouseholdStatus[])\n .map((s) => ({\n label: s.charAt(0) + s.slice(1).toLowerCase(),\n value: households.filter((h) => h.status === s).length,\n tone: STATUS_TONE[s],\n }))\n .filter((d) => d.value > 0);\n\n return {\n currency,\n total,\n active,\n prospects,\n onboarding,\n aum,\n advisors,\n advisorCount,\n advisorMax,\n statusMix,\n };\n }, [households]);\n\n const submitting = createHousehold.isPending || updateHousehold.isPending;\n const canSubmit = form.name.trim().length > 0 && !submitting;\n\n const handleSubmit = () => {\n if (!canSubmit) return;\n if (editing) {\n const input: BraUpdateHouseholdInput = {\n name: form.name.trim(),\n status: form.status,\n riskProfile: form.riskProfile,\n currency: form.currency,\n primaryContactName: form.primaryContactName.trim() || null,\n primaryContactEmail: form.primaryContactEmail.trim() || null,\n notes: form.notes.trim() || null,\n tags: toTags(form.tags),\n };\n updateHousehold.mutate(\n { id: editing.id, input },\n {\n onSuccess: () => {\n hapticSuccess();\n void showToast(tr('brainyrich.households.saved', 'Household saved'));\n setFormOpen(false);\n },\n }\n );\n } else {\n const input: BraCreateHouseholdInput = {\n name: form.name.trim(),\n status: form.status,\n riskProfile: form.riskProfile,\n currency: form.currency,\n primaryContactName: form.primaryContactName.trim() || null,\n primaryContactEmail: form.primaryContactEmail.trim() || null,\n notes: form.notes.trim() || null,\n tags: toTags(form.tags),\n };\n if (form.advisorId.trim()) input.advisorId = form.advisorId.trim();\n createHousehold.mutate(input, {\n onSuccess: () => {\n hapticSuccess();\n void showToast(tr('brainyrich.households.created', 'Household created'));\n setFormOpen(false);\n },\n });\n }\n };\n\n const showFirm = !isLoading && !isError && households.length > 0;\n\n return (\n <div data-tour=\"brainyrich-page-households\" className=\"contents\">\n <SectionShell\n title={tr('brainyrich.households.title', 'Households')}\n description={tr(\n 'brainyrich.households.description',\n 'Client households and family units under advisement'\n )}\n badges={\n households.length\n ? [\n tr('brainyrich.households.count', '{count} households', {\n count: households.length,\n }),\n ]\n : []\n }\n actionLabel={tr('brainyrich.households.new', 'New household')}\n onAction={openCreate}\n >\n {showFirm ? (\n <FirmOverview firm={firm} tr={tr} />\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n <StatTile label={tr('brainyrich.households.kpi.aum', 'Total AUM')} value=\"—\" glow />\n <StatTile label={tr('brainyrich.households.kpi.total', 'Households')} value=\"—\" />\n <StatTile label={tr('brainyrich.households.kpi.active', 'Active')} value=\"—\" />\n <StatTile label={tr('brainyrich.households.kpi.prospects', 'Prospects')} value=\"—\" />\n </div>\n )}\n\n {isLoading ? (\n <LoadingState rows={5} />\n ) : isError ? (\n <ErrorState\n title={tr('brainyrich.households.error.title', 'Could not load households')}\n onRetry={() => refetch()}\n />\n ) : households.length === 0 ? (\n <EmptyState\n icon={<Users className=\"size-6\" aria-hidden=\"true\" />}\n title={tr('brainyrich.households.empty.title', 'No households yet')}\n description={tr(\n 'brainyrich.households.empty.description',\n 'Create your first client household to start organizing accounts, portfolios and goals.'\n )}\n action={\n <Button size=\"sm\" onClick={openCreate}>\n {tr('brainyrich.households.new', 'New household')}\n </Button>\n }\n />\n ) : (\n <FadeIn as=\"section\">\n <Card className=\"overflow-hidden\">\n <CardContent className=\"p-0\">\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n <TableRow className=\"hover:bg-transparent\">\n <TableHead>{tr('brainyrich.households.col.name', 'Household')}</TableHead>\n <TableHead>{tr('brainyrich.households.col.status', 'Status')}</TableHead>\n <TableHead className=\"hidden md:table-cell\">\n {tr('brainyrich.households.col.risk', 'Risk profile')}\n </TableHead>\n <TableHead className=\"hidden lg:table-cell\">\n {tr('brainyrich.households.col.contact', 'Primary contact')}\n </TableHead>\n <TableHead className=\"hidden sm:table-cell\">\n {tr('brainyrich.households.col.advisor', 'Advisor')}\n </TableHead>\n <TableHead className=\"text-right\">\n {tr('brainyrich.households.col.aum', 'AUM')}\n </TableHead>\n <TableHead className=\"w-12 text-right\">\n <span className=\"sr-only\">\n {tr('brainyrich.households.col.actions', 'Actions')}\n </span>\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {households.map((h) => {\n const share = firm.aum > 0 ? Math.min(100, (h.aum / firm.aum) * 100) : 0;\n return (\n <TableRow\n key={h.id}\n className=\"group cursor-pointer transition-colors\"\n onClick={() => setDetailId(h.id)}\n >\n <TableCell>\n <span className=\"font-medium text-text-primary transition-colors group-hover:text-[var(--color-action-primary-text)]\">\n {h.name}\n </span>\n {h.tags.length > 0 ? (\n <div className=\"mt-1 flex flex-wrap gap-1\">\n {h.tags.slice(0, 3).map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n {h.tags.length > 3 ? (\n <Badge variant=\"outline\" className=\"text-xs\">\n +{h.tags.length - 3}\n </Badge>\n ) : null}\n </div>\n ) : null}\n </TableCell>\n <TableCell>\n <Badge variant={statusBadgeVariant(h.status)}>{h.status}</Badge>\n </TableCell>\n <TableCell className=\"hidden md:table-cell\">\n <Badge variant=\"secondary\">{h.riskProfile}</Badge>\n </TableCell>\n <TableCell className=\"hidden lg:table-cell\">\n {h.primaryContactName ? (\n <div className=\"space-y-0.5\">\n <p className=\"text-sm text-text-primary\">\n {h.primaryContactName}\n </p>\n {h.primaryContactEmail ? (\n <p className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Mail className=\"size-3\" aria-hidden=\"true\" />\n {h.primaryContactEmail}\n </p>\n ) : null}\n </div>\n ) : (\n <span className=\"text-sm text-text-muted\">—</span>\n )}\n </TableCell>\n <TableCell className=\"hidden sm:table-cell\">\n {h.advisorId ? (\n <span className=\"flex items-center gap-1.5 text-sm text-text-secondary\">\n <UserCircle2 className=\"size-3.5\" aria-hidden=\"true\" />\n <ActorName id={h.advisorId} />\n </span>\n ) : (\n <span className=\"text-sm text-text-muted\">\n {tr('brainyrich.common.unassigned', 'Unassigned')}\n </span>\n )}\n </TableCell>\n <TableCell className=\"text-right\">\n <div className=\"flex flex-col items-end gap-1\">\n <span className=\"font-medium tabular-nums text-text-primary\">\n {formatMoney(h.aum, h.currency)}\n </span>\n <span\n className=\"h-1 w-16 overflow-hidden rounded-full bg-bg-sunken\"\n aria-hidden=\"true\"\n >\n <span\n className=\"block h-full rounded-full bg-[var(--color-action-primary-bg)]\"\n style={{ width: `${share}%` }}\n />\n </span>\n </div>\n </TableCell>\n <TableCell className=\"text-right\" onClick={(e) => e.stopPropagation()}>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={tr(\n 'brainyrich.households.rowMenu',\n 'Household actions'\n )}\n >\n <MoreHorizontal className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onClick={() => setDetailId(h.id)}>\n <Eye className=\"size-4\" aria-hidden=\"true\" />\n {tr('brainyrich.households.action.view', 'View details')}\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => openEdit(h)}>\n <Pencil className=\"size-4\" aria-hidden=\"true\" />\n {tr('brainyrich.households.action.edit', 'Edit')}\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"text-status-error-text focus:text-status-error-text\"\n onClick={() => setArchiveTarget(h)}\n >\n <Archive className=\"size-4\" aria-hidden=\"true\" />\n {tr('brainyrich.households.action.archive', 'Archive')}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n </CardContent>\n </Card>\n </FadeIn>\n )}\n\n {!isLoading && !isError && households.length > 0 ? (\n <LoadMore\n hasMore={hasMore}\n onClick={loadMore}\n loading={isFetchingNextPage}\n label={tr('brainyrich.households.loadMore', 'Load more households')}\n />\n ) : null}\n\n <HouseholdFormDialog\n open={formOpen}\n onOpenChange={setFormOpen}\n editing={editing}\n form={form}\n setField={setField}\n onSubmit={handleSubmit}\n canSubmit={canSubmit}\n submitting={submitting}\n tr={tr}\n />\n\n <HouseholdDetailSheet\n householdId={detailId}\n onClose={() => setDetailId(undefined)}\n onEdit={(h) => {\n setDetailId(undefined);\n openEdit(h);\n }}\n tr={tr}\n />\n\n <ConfirmDialog\n open={Boolean(archiveTarget)}\n onOpenChange={(o) => (!o ? setArchiveTarget(null) : undefined)}\n title={tr('brainyrich.households.archive.title', 'Archive household?')}\n description={tr(\n 'brainyrich.households.archive.description',\n 'Archiving “{name}” removes it from the active list. You can still access its records, but it will no longer appear here.',\n { name: archiveTarget?.name ?? '' }\n )}\n confirmLabel={tr('brainyrich.households.action.archive', 'Archive')}\n pendingLabel={tr('brainyrich.households.archive.pending', 'Archiving…')}\n destructive\n pending={archiveHousehold.isPending}\n onConfirm={() => {\n if (!archiveTarget) return;\n archiveHousehold.mutate(archiveTarget.id, {\n onSuccess: () => {\n hapticSuccess();\n void showToast(\n tr('brainyrich.households.archived', 'Household archived', {\n name: archiveTarget.name,\n })\n );\n setArchiveTarget(null);\n },\n });\n }}\n />\n </SectionShell>\n </div>\n );\n}\n\n// ============================================================================\n// Firm overview — practice-wide KPIs + AUM-by-advisor + book mix\n// ============================================================================\n\ninterface FirmData {\n currency: string;\n total: number;\n active: number;\n prospects: number;\n onboarding: number;\n aum: number;\n advisors: { advisorId: string; value: number }[];\n advisorCount: number;\n advisorMax: number;\n statusMix: DonutDatum[];\n}\n\nfunction FirmOverview({\n firm,\n tr,\n}: {\n firm: FirmData;\n tr: (key: string, fallback: string, params?: Record<string, string | number>) => string;\n}) {\n return (\n <FadeIn as=\"section\" className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n <StatTile\n label={tr('brainyrich.households.kpi.aum', 'Total AUM')}\n value={formatMoney(firm.aum, firm.currency)}\n glow\n icon={<Wallet className=\"size-4\" aria-hidden=\"true\" />}\n />\n <StatTile\n label={tr('brainyrich.households.kpi.total', 'Households')}\n value={String(firm.total)}\n icon={<Users className=\"size-4\" aria-hidden=\"true\" />}\n />\n <StatTile\n label={tr('brainyrich.households.kpi.active', 'Active')}\n value={String(firm.active)}\n icon={<TrendingUp className=\"size-4\" aria-hidden=\"true\" />}\n />\n <StatTile\n label={tr('brainyrich.households.kpi.prospects', 'Prospects')}\n value={String(firm.prospects)}\n icon={<Target className=\"size-4\" aria-hidden=\"true\" />}\n />\n </div>\n\n <div className=\"grid gap-4 lg:grid-cols-3\">\n <GlassCard className=\"lg:col-span-2\">\n <div className=\"mb-4 flex items-center justify-between gap-2\">\n <div>\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('brainyrich.households.firm.byAdvisor', 'AUM by advisor')}\n </h2>\n <p className=\"text-xs text-text-secondary\">\n {tr('brainyrich.households.firm.advisorCount', '{count} advisors', {\n count: firm.advisorCount,\n })}\n </p>\n </div>\n <Building2 className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n </div>\n <ul\n className=\"space-y-3.5\"\n aria-label={tr('brainyrich.households.firm.byAdvisor', 'AUM by advisor')}\n >\n {firm.advisors.map((a) => {\n const width =\n firm.advisorMax > 0 ? Math.max(2, (a.value / firm.advisorMax) * 100) : 0;\n return (\n <li key={a.advisorId || 'unassigned'} className=\"space-y-1.5\">\n <div className=\"flex items-center justify-between gap-3 text-xs\">\n <span className=\"flex items-center gap-1.5 truncate font-medium text-text-primary\">\n <UserCircle2\n className=\"size-3.5 shrink-0 text-text-secondary\"\n aria-hidden=\"true\"\n />\n {a.advisorId ? (\n <ActorName id={a.advisorId} />\n ) : (\n tr('brainyrich.common.unassigned', 'Unassigned')\n )}\n </span>\n <span className=\"shrink-0 tabular-nums text-text-secondary\">\n {formatCompact(a.value, firm.currency)}\n </span>\n </div>\n <div\n className=\"h-2 w-full overflow-hidden rounded-full bg-bg-sunken\"\n role=\"progressbar\"\n aria-valuenow={Math.round(width)}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={a.advisorId || 'Unassigned'}\n >\n <div\n className=\"h-full rounded-full bg-[var(--color-action-primary-bg)] transition-[width] duration-500 ease-out\"\n style={{ width: `${width}%` }}\n />\n </div>\n </li>\n );\n })}\n </ul>\n </GlassCard>\n\n <GlassCard>\n <div className=\"mb-2 flex items-center justify-between gap-2\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('brainyrich.households.firm.bookMix', 'Book by status')}\n </h2>\n </div>\n {firm.statusMix.length > 0 ? (\n <DonutChart\n data={firm.statusMix}\n centerValue={String(firm.total)}\n centerLabel={tr('brainyrich.households.kpi.total', 'Households')}\n legend\n height={200}\n valueFormatter={(v) => String(v)}\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {tr('brainyrich.households.firm.noMix', 'No households to chart yet.')}\n </p>\n )}\n </GlassCard>\n </div>\n </FadeIn>\n );\n}\n\n// ============================================================================\n// Create / edit dialog\n// ============================================================================\n\ninterface HouseholdFormDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n editing: BraHousehold | null;\n form: HouseholdFormState;\n setField: <K extends keyof HouseholdFormState>(key: K, value: HouseholdFormState[K]) => void;\n onSubmit: () => void;\n canSubmit: boolean;\n submitting: boolean;\n tr: (key: string, fallback: string, params?: Record<string, string | number>) => string;\n}\n\nfunction HouseholdFormDialog({\n open,\n onOpenChange,\n editing,\n form,\n setField,\n onSubmit,\n canSubmit,\n submitting,\n tr,\n}: HouseholdFormDialogProps) {\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {editing\n ? tr('brainyrich.households.form.editTitle', 'Edit household')\n : tr('brainyrich.households.form.createTitle', 'New household')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'brainyrich.households.form.subtitle',\n 'A household groups a client family’s accounts, portfolios and goals.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form\n className=\"space-y-4\"\n onSubmit={(e) => {\n e.preventDefault();\n onSubmit();\n }}\n >\n <div className=\"space-y-2\">\n <Label htmlFor=\"hh-name\">{tr('brainyrich.households.form.name', 'Name')} *</Label>\n <Input\n id=\"hh-name\"\n value={form.name}\n onChange={(e) => setField('name', e.target.value)}\n placeholder={tr('brainyrich.households.form.namePh', 'e.g. The Raghavan Family')}\n required\n />\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>{tr('brainyrich.households.form.status', 'Status')}</Label>\n <Select\n value={form.status}\n onValueChange={(v) => setField('status', v as BraHouseholdStatus)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {STATUS_OPTIONS.map((s) => (\n <SelectItem key={s} value={s}>\n {s}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-2\">\n <Label>{tr('brainyrich.households.form.risk', 'Risk profile')}</Label>\n <Select\n value={form.riskProfile}\n onValueChange={(v) => setField('riskProfile', v as BraRiskProfile)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {RISK_OPTIONS.map((r) => (\n <SelectItem key={r} value={r}>\n {r}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"hh-contact-name\">\n {tr('brainyrich.households.form.contactName', 'Primary contact')}\n </Label>\n <Input\n id=\"hh-contact-name\"\n value={form.primaryContactName}\n onChange={(e) => setField('primaryContactName', e.target.value)}\n placeholder={tr('brainyrich.households.form.contactNamePh', 'Full name')}\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"hh-contact-email\">\n {tr('brainyrich.households.form.contactEmail', 'Contact email')}\n </Label>\n <Input\n id=\"hh-contact-email\"\n type=\"email\"\n value={form.primaryContactEmail}\n onChange={(e) => setField('primaryContactEmail', e.target.value)}\n placeholder=\"name@example.com\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>{tr('brainyrich.households.form.currency', 'Currency')}</Label>\n <Select value={form.currency} onValueChange={(v) => setField('currency', v)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CURRENCY_OPTIONS.map((c) => (\n <SelectItem key={c} value={c}>\n {c}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {!editing ? (\n <div className=\"space-y-2\">\n <Label htmlFor=\"hh-advisor\">\n {tr('brainyrich.households.form.advisor', 'Advisor ID')}\n </Label>\n <Input\n id=\"hh-advisor\"\n value={form.advisorId}\n onChange={(e) => setField('advisorId', e.target.value)}\n placeholder={tr('brainyrich.households.form.advisorPh', 'Optional')}\n />\n </div>\n ) : null}\n </div>\n\n {editing ? (\n <TagField\n entityType=\"brainyrich-household\"\n entityId={editing.id}\n label={tr('brainyrich.households.form.tags', 'Tags')}\n />\n ) : (\n <div className=\"space-y-2\">\n <Label htmlFor=\"hh-tags\">{tr('brainyrich.households.form.tags', 'Tags')}</Label>\n <Input\n id=\"hh-tags\"\n value={form.tags}\n onChange={(e) => setField('tags', e.target.value)}\n placeholder={tr('brainyrich.households.form.tagsPh', 'Comma-separated')}\n />\n </div>\n )}\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"hh-notes\">{tr('brainyrich.households.form.notes', 'Notes')}</Label>\n <Textarea\n id=\"hh-notes\"\n value={form.notes}\n onChange={(e) => setField('notes', e.target.value)}\n rows={3}\n />\n </div>\n\n <ResponsiveDialogFooter>\n <ResponsiveDialogClose asChild>\n <Button type=\"button\" variant=\"outline\">\n {tr('brainyrich.common.cancel', 'Cancel')}\n </Button>\n </ResponsiveDialogClose>\n <Button type=\"submit\" disabled={!canSubmit}>\n {submitting\n ? tr('brainyrich.common.saving', 'Saving…')\n : editing\n ? tr('brainyrich.common.save', 'Save changes')\n : tr('brainyrich.households.form.create', 'Create household')}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ============================================================================\n// Detail sheet\n// ============================================================================\n\ninterface HouseholdDetailSheetProps {\n householdId: string | undefined;\n onClose: () => void;\n onEdit: (h: BraHousehold) => void;\n tr: (key: string, fallback: string, params?: Record<string, string | number>) => string;\n}\n\nfunction HouseholdDetailSheet({ householdId, onClose, onEdit, tr }: HouseholdDetailSheetProps) {\n const { data: household, isLoading, isError, refetch } = useHousehold(householdId);\n const { data: accounts } = useAccounts(householdId ? { householdId } : {});\n const {\n data: portfolios,\n isLoading: portfoliosLoading,\n isError: portfoliosError,\n refetch: refetchPortfolios,\n } = usePortfolios(householdId ? { householdId } : {});\n const { data: goals } = useGoals(householdId ? { householdId } : {});\n\n const open = Boolean(householdId);\n\n const accountRows = useMemo(\n () =>\n [...accounts]\n .map((a) => ({ account: a, value: accountValue(a) }))\n .sort((x, y) => y.value - x.value),\n [accounts]\n );\n const invested = accountRows.reduce((s, r) => s + r.value, 0);\n const allocDonut: DonutDatum[] = accountRows\n .filter((r) => r.value > 0)\n .slice(0, 6)\n .map((r) => ({ label: r.account.name, value: r.value }));\n\n return (\n <Sheet open={open} onOpenChange={(o) => (!o ? onClose() : undefined)}>\n <SheetContent className=\"flex w-full flex-col gap-0 overflow-y-auto p-0 sm:max-w-xl lg:max-w-5xl\">\n {isLoading ? (\n <div className=\"p-6\">\n <LoadingState rows={4} />\n </div>\n ) : isError ? (\n <div className=\"p-6\">\n <ErrorState\n title={tr('brainyrich.households.detail.error', 'Could not load household')}\n onRetry={() => refetch()}\n />\n </div>\n ) : !household ? (\n <div className=\"p-6\">\n <EmptyState\n icon={<Users className=\"size-6\" aria-hidden=\"true\" />}\n title={tr('brainyrich.households.detail.missing', 'Household not found')}\n />\n </div>\n ) : (\n <div className=\"space-y-5 p-6\">\n <SheetHeader className=\"space-y-2 p-0 text-left\">\n <SheetTitle className=\"text-xl\">{household.name}</SheetTitle>\n <SheetDescription className=\"sr-only\">\n {tr('brainyrich.households.detail.subtitle', 'Household overview')}\n </SheetDescription>\n <div className=\"flex flex-wrap items-center gap-2 pt-1\">\n <Badge variant={statusBadgeVariant(household.status)}>{household.status}</Badge>\n <Badge variant=\"secondary\">{household.riskProfile}</Badge>\n </div>\n </SheetHeader>\n\n {/* Hero — AUM + primary contact on a glass surface with emerald glow */}\n <GlassCard glow>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">\n {tr('brainyrich.households.detail.aum', 'AUM')}\n </p>\n <p className=\"mt-1 text-3xl font-semibold tracking-tight text-text-primary\">\n {formatMoney(household.aum, household.currency)}\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">{household.currency}</p>\n </div>\n <div className=\"sm:text-right\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">\n {tr('brainyrich.households.detail.contact', 'Primary contact')}\n </p>\n <p className=\"mt-1 text-base font-medium text-text-primary\">\n {household.primaryContactName ?? '—'}\n </p>\n {household.primaryContactEmail ? (\n <a\n href={`mailto:${household.primaryContactEmail}`}\n className=\"mt-0.5 inline-flex items-center gap-1 text-xs text-text-secondary hover:text-[var(--color-action-primary-text)] sm:justify-end\"\n >\n <Mail className=\"size-3\" aria-hidden=\"true\" />\n {household.primaryContactEmail}\n </a>\n ) : null}\n </div>\n </div>\n </GlassCard>\n\n {/* Portfolio counts */}\n <div className=\"grid grid-cols-3 gap-3\">\n <SummaryTile\n icon={<Wallet className=\"size-4\" aria-hidden=\"true\" />}\n label={tr('brainyrich.households.detail.accounts', 'Accounts')}\n value={String(accounts.length)}\n />\n <SummaryTile\n icon={<Briefcase className=\"size-4\" aria-hidden=\"true\" />}\n label={tr('brainyrich.households.detail.portfolios', 'Portfolios')}\n value={String(portfolios.length)}\n />\n <SummaryTile\n icon={<Target className=\"size-4\" aria-hidden=\"true\" />}\n label={tr('brainyrich.households.detail.goals', 'Goals')}\n value={String(goals.length)}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 lg:grid-cols-[minmax(0,1fr)_20rem]\">\n <Tabs defaultValue=\"accounts\" className=\"w-full min-w-0\">\n <TabsList className=\"w-full justify-start overflow-x-auto\">\n <TabsTrigger value=\"accounts\">\n {tr('brainyrich.households.detail.accounts', 'Accounts')}\n </TabsTrigger>\n <TabsTrigger value=\"portfolios\">\n {tr('brainyrich.households.detail.portfolios', 'Portfolios')}\n </TabsTrigger>\n <TabsTrigger value=\"goals\">\n {tr('brainyrich.households.detail.goals', 'Goals')}\n </TabsTrigger>\n <TabsTrigger value=\"documents\">\n {tr('brainyrich.households.detail.documents', 'Documents')}\n </TabsTrigger>\n <TabsTrigger value=\"notes\">\n {tr('brainyrich.households.tab.notes', 'Notes')}\n </TabsTrigger>\n </TabsList>\n\n {/* Accounts */}\n <TabsContent value=\"accounts\" className=\"mt-4 space-y-2\">\n {allocDonut.length > 0 ? (\n <Card>\n <CardContent className=\"p-4\">\n <h3 className=\"mb-3 text-sm font-medium text-text-primary\">\n {tr('brainyrich.households.detail.allocation', 'Where assets sit')}\n </h3>\n <DonutChart\n data={allocDonut}\n centerValue={formatCompact(invested, household.currency)}\n centerLabel={tr('brainyrich.households.detail.invested', 'Invested')}\n height={200}\n valueFormatter={(v) => formatMoney(v, household.currency)}\n />\n </CardContent>\n </Card>\n ) : null}\n\n {accountRows.length === 0 ? (\n <EmptyState\n icon={<Wallet className=\"size-6\" aria-hidden=\"true\" />}\n title={tr('brainyrich.households.detail.noAccounts', 'No accounts linked')}\n />\n ) : (\n accountRows.map(({ account, value }) => (\n <div\n key={account.id}\n className=\"flex items-center justify-between gap-3 rounded-lg border border-border-subtle bg-bg-surface p-3 transition-colors hover:border-[var(--color-action-primary-border)]\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {account.name}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {account.institution ?? account.type}\n {account.accountMask ? ` ·••${account.accountMask}` : ''}\n </p>\n </div>\n <span className=\"shrink-0 text-sm font-semibold tabular-nums text-status-success-text\">\n {formatMoney(value, account.currency)}\n </span>\n </div>\n ))\n )}\n </TabsContent>\n\n {/* Portfolios */}\n <TabsContent value=\"portfolios\" className=\"mt-4 space-y-2\">\n {portfoliosLoading ? (\n <LoadingState rows={2} />\n ) : portfoliosError ? (\n <ErrorState\n title={tr(\n 'brainyrich.households.detail.portfoliosError',\n 'Could not load portfolios'\n )}\n onRetry={() => refetchPortfolios()}\n />\n ) : portfolios.length === 0 ? (\n <EmptyState\n icon={<Briefcase className=\"size-6\" aria-hidden=\"true\" />}\n title={tr(\n 'brainyrich.households.detail.noPortfolios',\n 'No portfolios linked'\n )}\n />\n ) : (\n portfolios.map((p: BraPortfolio) => (\n <div\n key={p.id}\n data-testid=\"household-portfolio-row\"\n className=\"flex items-center justify-between gap-3 rounded-lg border border-border-subtle bg-bg-surface p-3 transition-colors hover:border-[var(--color-action-primary-border)]\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{p.name}</p>\n <p className=\"truncate text-xs text-text-secondary\">\n {p.baseCurrency} ·{' '}\n {tr(\n 'brainyrich.households.detail.driftThreshold',\n 'Drift threshold {pct}',\n {\n pct: formatPct(p.driftThresholdPct),\n }\n )}\n {p.description ? ` · ${p.description}` : ''}\n </p>\n </div>\n <Badge variant={statusBadgeVariant(p.status)} className=\"shrink-0\">\n {p.status.replace(/_/g, ' ')}\n </Badge>\n </div>\n ))\n )}\n </TabsContent>\n\n {/* Goals */}\n <TabsContent value=\"goals\" className=\"mt-4 space-y-2\">\n {goals.length === 0 ? (\n <EmptyState\n icon={<Target className=\"size-6\" aria-hidden=\"true\" />}\n title={tr('brainyrich.households.detail.noGoals', 'No goals set')}\n />\n ) : (\n goals.map((g: BraGoal) => (\n <div\n key={g.id}\n className=\"flex items-center justify-between gap-3 rounded-lg border border-border-subtle bg-bg-surface p-3\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{g.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {tr('brainyrich.households.detail.goalTarget', 'Target')}{' '}\n {formatMoney(g.targetAmount, g.currency)} · {formatDate(g.targetDate)}\n </p>\n </div>\n <Badge variant={statusBadgeVariant(g.projection)} className=\"shrink-0\">\n {g.projection.replace(/_/g, ' ')}\n </Badge>\n </div>\n ))\n )}\n </TabsContent>\n\n {/* Documents */}\n <TabsContent value=\"documents\" className=\"mt-4\">\n <DocumentsPanel\n entityType={HOUSEHOLD_ENTITY_TYPE}\n entityId={household.id}\n heading={tr('brainyrich.households.detail.documents', 'Documents')}\n />\n </TabsContent>\n\n {/* Notes — household summary + profile details. Threaded advisor\n comments live in the persistent right rail. */}\n <TabsContent value=\"notes\" className=\"mt-4 space-y-4\">\n <Card>\n <CardContent className=\"space-y-3 p-4 text-sm\">\n <div className=\"flex items-center justify-between gap-4\">\n <span className=\"text-text-secondary\">\n {tr('brainyrich.households.detail.advisor', 'Advisor')}\n </span>\n <span className=\"text-right font-medium text-text-primary\">\n {household.advisorId ? (\n <ActorName id={household.advisorId} />\n ) : (\n tr('brainyrich.common.unassigned', 'Unassigned')\n )}\n </span>\n </div>\n <Separator />\n <DetailRow\n label={tr('brainyrich.households.detail.created', 'Created')}\n value={formatDate(household.createdAt)}\n />\n <DetailRow\n label={tr('brainyrich.households.detail.updated', 'Last updated')}\n value={formatDate(household.updatedAt)}\n />\n </CardContent>\n </Card>\n\n {household.notes ? (\n <Card>\n <CardContent className=\"space-y-1 p-4\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {tr('brainyrich.households.detail.summary', 'Summary')}\n </p>\n <p className=\"text-sm text-text-primary\">{household.notes}</p>\n </CardContent>\n </Card>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'brainyrich.households.detail.noSummary',\n 'No summary yet. Add one with Edit, or leave a note in Comments/Notes.'\n )}\n </p>\n )}\n </TabsContent>\n </Tabs>\n\n <HouseholdDetailRail householdId={household.id} legacyTags={household.tags} tr={tr} />\n </div>\n\n <Button variant=\"outline\" className=\"w-full\" onClick={() => onEdit(household)}>\n <Pencil className=\"size-4\" aria-hidden=\"true\" />\n {tr('brainyrich.households.action.edit', 'Edit')}\n </Button>\n </div>\n )}\n </SheetContent>\n </Sheet>\n );\n}\n\nfunction SummaryTile({ icon, label, value }: { icon: ReactNode; label: string; value: string }) {\n return (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3 text-center transition-shadow motion-safe:hover:shadow-[var(--shadow-pop-hover,var(--shadow-elevation-2))]\">\n <div className=\"mx-auto mb-1 flex size-8 items-center justify-center rounded-full bg-bg-sunken text-text-secondary\">\n {icon}\n </div>\n <p className=\"text-lg font-semibold text-text-primary\">{value}</p>\n <p className=\"text-xs text-text-secondary\">{label}</p>\n </div>\n );\n}\n\nfunction DetailRow({ label, value }: { label: string; value: string }) {\n return (\n <div className=\"flex items-center justify-between gap-4\">\n <span className=\"text-text-secondary\">{label}</span>\n <span className=\"text-right font-medium text-text-primary\">{value}</span>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -182,7 +182,14 @@ function Y({ translate: e }) {
|
|
|
182
182
|
}) })] }),
|
|
183
183
|
/* @__PURE__ */ T(O, { children: [/* @__PURE__ */ w(j, {
|
|
184
184
|
className: "flex flex-row items-center justify-between gap-4",
|
|
185
|
-
children: /* @__PURE__ */ T("div", { children: [
|
|
185
|
+
children: /* @__PURE__ */ T("div", { children: [
|
|
186
|
+
/* @__PURE__ */ w(M, { children: e("brainyrich.firm.roster.title", "Firm Roster") }),
|
|
187
|
+
/* @__PURE__ */ w(A, { children: e("brainyrich.firm.roster.description", "Every advisor on the firm roster, with the right to change roles.") }),
|
|
188
|
+
/* @__PURE__ */ w("p", {
|
|
189
|
+
className: "mt-1 text-xs text-text-secondary",
|
|
190
|
+
children: e("brainyrich.firm.roster.seatNote", "Each active advisor uses one advisor seat on your plan. Deactivate an advisor to free their seat.")
|
|
191
|
+
})
|
|
192
|
+
] })
|
|
186
193
|
}), /* @__PURE__ */ T(k, {
|
|
187
194
|
className: "space-y-4",
|
|
188
195
|
children: [/* @__PURE__ */ T("div", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirmPage.js","names":[],"sources":["../../../src/brainyrich/pages/FirmPage.tsx"],"sourcesContent":["/**\n * FirmPage — firm roster + firm-wide reporting (BOFF-4247).\n *\n * `braFirmReport` / `braListAdvisors` are FIRM_ADMIN-only server-side (see\n * firm.gql) — the domain data (BraAdvisor.role) is the only authorization\n * fact, there is no separate platform-RBAC role for it. This page therefore\n * always renders the calling actor's own roster status first, and only\n * attempts the firm-wide views when that status is FIRM_ADMIN (or when the\n * actor hasn't registered a roster row yet, in which case it offers\n * self-registration — the very first advisor in a workspace becomes\n * FIRM_ADMIN automatically).\n */\nimport { useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@burdenoff/fe-libs/ui';\nimport { Building2, PlusCircle, ShieldCheck, UserPlus, Users } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport {\n useMyAdvisorProfile,\n useEnsureAdvisorProfile,\n useAdvisors,\n useSetAdvisorRole,\n useSetAdvisorStatus,\n useFirmReport,\n} from '../hooks/useBrainyRichApi';\nimport type { BraAdvisor, BraAdvisorRole } from '../types/bra';\nimport {\n AllocationBars,\n SectionShell,\n EmptyState,\n LoadingState,\n ErrorState,\n formatMoney,\n formatDate,\n formatPct,\n type AllocationDatum,\n} from './_shared';\nimport { StatTile, FadeIn } from '../components/viz';\n\n/** Largest books charted as a share of firm AUM; the table below lists them all. */\nconst MAX_CHARTED_ADVISORS = 8;\n\ninterface AdvisorBookRow {\n advisorId: string;\n aum: number;\n displayName?: string | null;\n email?: string | null;\n}\n\nfunction advisorShareBars(\n byAdvisor: readonly AdvisorBookRow[],\n totalAum: number,\n currency: string\n): AllocationDatum[] {\n if (totalAum <= 0) return [];\n return byAdvisor.slice(0, MAX_CHARTED_ADVISORS).map((row) => {\n const share = (row.aum / totalAum) * 100;\n return {\n label: row.displayName ?? row.email ?? row.advisorId,\n value: share,\n tone: 'accent' as const,\n caption: `${formatMoney(row.aum, currency)} · ${formatPct(share)}`,\n };\n });\n}\n\ntype Translate = (\n key: string,\n fallback: string,\n params?: Record<string, string | number>\n) => string;\n\nexport function FirmPage() {\n const { t } = useI18n();\n const translate: Translate = (key, fallback, params) => tWithFallback(t, key, fallback, params);\n\n const myProfileQuery = useMyAdvisorProfile();\n const ensureProfile = useEnsureAdvisorProfile();\n const myProfile = myProfileQuery.data;\n const isFirmAdmin = myProfile?.role === 'FIRM_ADMIN';\n\n const [displayName, setDisplayName] = useState('');\n const [email, setEmail] = useState('');\n\n if (myProfileQuery.isLoading) {\n return (\n <SectionShell\n title={translate('brainyrich.firm.title', 'Firm')}\n description={translate(\n 'brainyrich.firm.description',\n 'Firm roster and firm-wide AUM reporting.'\n )}\n >\n <LoadingState />\n </SectionShell>\n );\n }\n\n if (!myProfile) {\n return (\n <SectionShell\n title={translate('brainyrich.firm.title', 'Firm')}\n description={translate(\n 'brainyrich.firm.description',\n 'Firm roster and firm-wide AUM reporting.'\n )}\n >\n <FadeIn>\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Building2 className=\"size-4\" aria-hidden=\"true\" />\n {translate('brainyrich.firm.join.title', 'Join the firm roster')}\n </CardTitle>\n <CardDescription>\n {translate(\n 'brainyrich.firm.join.description',\n \"You're not on this workspace's BrainyRich firm roster yet. The first advisor to register becomes the firm admin automatically; anyone after that is invited by an existing admin.\"\n )}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"firm-display-name\">\n {translate('brainyrich.firm.fields.displayName', 'Display name')}\n </Label>\n <Input\n id=\"firm-display-name\"\n value={displayName}\n onChange={(e) => setDisplayName(e.target.value)}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"firm-email\">\n {translate('brainyrich.firm.fields.email', 'Email')}\n </Label>\n <Input\n id=\"firm-email\"\n type=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n />\n </div>\n </div>\n <Button\n disabled={ensureProfile.isPending}\n onClick={() =>\n ensureProfile.mutate({\n displayName: displayName || undefined,\n email: email || undefined,\n })\n }\n >\n <UserPlus className=\"size-4\" aria-hidden=\"true\" />\n {ensureProfile.isPending ? 'Registering…' : 'Register on firm roster'}\n </Button>\n </CardContent>\n </Card>\n </FadeIn>\n </SectionShell>\n );\n }\n\n return (\n <SectionShell\n title={translate('brainyrich.firm.title', 'Firm')}\n description={translate(\n 'brainyrich.firm.description',\n 'Firm roster and firm-wide AUM reporting.'\n )}\n >\n <FadeIn>\n <Card>\n <CardContent className=\"flex flex-wrap items-center justify-between gap-3 py-4\">\n <div className=\"flex items-center gap-2\">\n <ShieldCheck className=\"size-4 text-status-success-text\" aria-hidden=\"true\" />\n <span className=\"text-sm text-text-secondary\">\n {translate('brainyrich.firm.you', 'You are on the firm roster as')}\n </span>\n <Badge variant={isFirmAdmin ? 'default' : 'secondary'}>{myProfile.role}</Badge>\n </div>\n </CardContent>\n </Card>\n </FadeIn>\n\n {isFirmAdmin ? (\n <FirmAdminView translate={translate} />\n ) : (\n <FadeIn>\n <EmptyState\n icon={<Users className=\"size-8\" aria-hidden=\"true\" />}\n title={translate(\n 'brainyrich.firm.notAdmin',\n 'Firm-wide roster and AUM reporting are visible to firm admins only.'\n )}\n />\n </FadeIn>\n )}\n </SectionShell>\n );\n}\n\nfunction FirmAdminView({ translate }: { translate: Translate }) {\n const firmReportQuery = useFirmReport();\n const advisorsQuery = useAdvisors();\n const setRole = useSetAdvisorRole();\n const setStatus = useSetAdvisorStatus();\n const [inviteUserId, setInviteUserId] = useState('');\n const [inviteRole, setInviteRole] = useState<BraAdvisorRole>('ADVISOR');\n const ensureProfile = useEnsureAdvisorProfile();\n\n const report = firmReportQuery.data;\n\n return (\n <div className=\"space-y-6\">\n <FadeIn>\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n <StatTile\n label={translate('brainyrich.firm.kpis.totalAum', 'Firm AUM')}\n value={report ? formatMoney(report.totalAum, report.currency) : '—'}\n glow\n />\n <StatTile\n label={translate('brainyrich.firm.kpis.advisors', 'Advisors')}\n value={report ? String(report.totalAdvisors) : '—'}\n />\n <StatTile\n label={translate('brainyrich.firm.kpis.households', 'Households')}\n value={report ? String(report.totalHouseholds) : '—'}\n />\n <StatTile\n label={translate('brainyrich.firm.kpis.asOf', 'As of')}\n value={report ? formatDate(report.asOf) : '—'}\n />\n </div>\n </FadeIn>\n\n <Card>\n <CardHeader>\n <CardTitle>{translate('brainyrich.firm.byAdvisor.title', 'AUM by advisor')}</CardTitle>\n <CardDescription>\n {translate(\n 'brainyrich.firm.byAdvisor.description',\n 'Sorted by AUM, largest book first — a real rollup of BraHousehold.aum per advisor.'\n )}\n </CardDescription>\n </CardHeader>\n <CardContent>\n {firmReportQuery.isLoading ? (\n <LoadingState />\n ) : firmReportQuery.isError ? (\n <ErrorState onRetry={() => firmReportQuery.refetch()} />\n ) : !report || report.byAdvisor.length === 0 ? (\n <EmptyState\n icon={<Building2 className=\"size-8\" aria-hidden=\"true\" />}\n title={translate('brainyrich.firm.byAdvisor.empty', 'No advisor books yet')}\n />\n ) : (\n <div className=\"space-y-6\">\n {advisorShareBars(report.byAdvisor, report.totalAum, report.currency).length > 0 ? (\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {translate('brainyrich.firm.byAdvisor.shareLabel', 'Share of firm AUM')}\n </p>\n <AllocationBars\n data={advisorShareBars(report.byAdvisor, report.totalAum, report.currency)}\n />\n {report.byAdvisor.length > MAX_CHARTED_ADVISORS ? (\n <p className=\"text-xs text-text-secondary\">\n Showing the {MAX_CHARTED_ADVISORS} largest books — every advisor is listed\n below.\n </p>\n ) : null}\n </div>\n ) : null}\n\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Advisor</TableHead>\n <TableHead>Role</TableHead>\n <TableHead>Households</TableHead>\n <TableHead className=\"text-right\">AUM</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {report.byAdvisor.map((row) => (\n <TableRow key={row.advisorId}>\n <TableCell className=\"font-medium\">\n {row.displayName ?? row.email ?? row.advisorId}\n </TableCell>\n <TableCell>\n <Badge variant={row.role === 'FIRM_ADMIN' ? 'default' : 'secondary'}>\n {row.role ?? '—'}\n </Badge>\n </TableCell>\n <TableCell>{row.householdCount}</TableCell>\n <TableCell className=\"text-right font-medium\">\n {formatMoney(row.aum, report.currency)}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-4\">\n <div>\n <CardTitle>{translate('brainyrich.firm.roster.title', 'Firm Roster')}</CardTitle>\n <CardDescription>\n {translate(\n 'brainyrich.firm.roster.description',\n 'Every advisor on the firm roster. Each active advisor uses one advisor seat on your plan; deactivate an advisor to free their seat.'\n )}\n </CardDescription>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex flex-wrap items-end gap-3 rounded-lg border border-border-subtle bg-bg-sunken p-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"invite-user-id\">\n {translate('brainyrich.firm.roster.inviteUserId', 'User ID to invite')}\n </Label>\n <Input\n id=\"invite-user-id\"\n value={inviteUserId}\n onChange={(e) => setInviteUserId(e.target.value)}\n className=\"w-64\"\n placeholder=\"actor id\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label>{translate('brainyrich.firm.roster.inviteRole', 'Role')}</Label>\n <Select value={inviteRole} onValueChange={(v) => setInviteRole(v as BraAdvisorRole)}>\n <SelectTrigger className=\"w-40\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ADVISOR\">Advisor</SelectItem>\n <SelectItem value=\"FIRM_ADMIN\">Firm Admin</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <Button\n disabled={!inviteUserId.trim() || ensureProfile.isPending}\n onClick={() =>\n ensureProfile.mutate(\n { userId: inviteUserId.trim(), role: inviteRole },\n { onSuccess: () => setInviteUserId('') }\n )\n }\n >\n <PlusCircle className=\"size-4\" aria-hidden=\"true\" />\n {translate('brainyrich.firm.roster.invite', 'Invite')}\n </Button>\n </div>\n\n {advisorsQuery.isLoading ? (\n <LoadingState />\n ) : advisorsQuery.isError ? (\n <ErrorState onRetry={() => advisorsQuery.refetch()} />\n ) : advisorsQuery.data.length === 0 ? (\n <EmptyState\n icon={<Users className=\"size-8\" aria-hidden=\"true\" />}\n title={translate('brainyrich.firm.roster.empty', 'No advisors yet')}\n />\n ) : (\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Name</TableHead>\n <TableHead>Email</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Role</TableHead>\n <TableHead className=\"text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {advisorsQuery.data.map((advisor: BraAdvisor) => (\n <TableRow key={advisor.id}>\n <TableCell className=\"font-medium\">\n {advisor.displayName ?? advisor.userId}\n </TableCell>\n <TableCell>{advisor.email ?? '—'}</TableCell>\n <TableCell>\n <Badge variant={advisor.status === 'ACTIVE' ? 'default' : 'secondary'}>\n {advisor.status}\n </Badge>\n </TableCell>\n <TableCell>\n <Select\n value={advisor.role}\n onValueChange={(v) =>\n setRole.mutate({ userId: advisor.userId, role: v as BraAdvisorRole })\n }\n >\n <SelectTrigger className=\"w-40\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ADVISOR\">Advisor</SelectItem>\n <SelectItem value=\"FIRM_ADMIN\">Firm Admin</SelectItem>\n </SelectContent>\n </Select>\n </TableCell>\n <TableCell className=\"text-right\">\n <div className=\"flex items-center justify-end gap-3\">\n <span className=\"text-xs text-text-secondary\">\n {advisor.invitedById\n ? `Invited by ${advisor.invitedById}`\n : 'Self-registered'}\n </span>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={setStatus.isPending}\n onClick={() =>\n setStatus.mutate({\n userId: advisor.userId,\n status: advisor.status === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE',\n })\n }\n >\n {advisor.status === 'ACTIVE'\n ? translate('brainyrich.firm.roster.deactivate', 'Deactivate')\n : translate('brainyrich.firm.roster.reactivate', 'Reactivate')}\n </Button>\n </div>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n )}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FirmPage.js","names":[],"sources":["../../../src/brainyrich/pages/FirmPage.tsx"],"sourcesContent":["/**\n * FirmPage — firm roster + firm-wide reporting (BOFF-4247).\n *\n * `braFirmReport` / `braListAdvisors` are FIRM_ADMIN-only server-side (see\n * firm.gql) — the domain data (BraAdvisor.role) is the only authorization\n * fact, there is no separate platform-RBAC role for it. This page therefore\n * always renders the calling actor's own roster status first, and only\n * attempts the firm-wide views when that status is FIRM_ADMIN (or when the\n * actor hasn't registered a roster row yet, in which case it offers\n * self-registration — the very first advisor in a workspace becomes\n * FIRM_ADMIN automatically).\n */\nimport { useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@burdenoff/fe-libs/ui';\nimport { Building2, PlusCircle, ShieldCheck, UserPlus, Users } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport {\n useMyAdvisorProfile,\n useEnsureAdvisorProfile,\n useAdvisors,\n useSetAdvisorRole,\n useSetAdvisorStatus,\n useFirmReport,\n} from '../hooks/useBrainyRichApi';\nimport type { BraAdvisor, BraAdvisorRole } from '../types/bra';\nimport {\n AllocationBars,\n SectionShell,\n EmptyState,\n LoadingState,\n ErrorState,\n formatMoney,\n formatDate,\n formatPct,\n type AllocationDatum,\n} from './_shared';\nimport { StatTile, FadeIn } from '../components/viz';\n\n/** Largest books charted as a share of firm AUM; the table below lists them all. */\nconst MAX_CHARTED_ADVISORS = 8;\n\ninterface AdvisorBookRow {\n advisorId: string;\n aum: number;\n displayName?: string | null;\n email?: string | null;\n}\n\nfunction advisorShareBars(\n byAdvisor: readonly AdvisorBookRow[],\n totalAum: number,\n currency: string\n): AllocationDatum[] {\n if (totalAum <= 0) return [];\n return byAdvisor.slice(0, MAX_CHARTED_ADVISORS).map((row) => {\n const share = (row.aum / totalAum) * 100;\n return {\n label: row.displayName ?? row.email ?? row.advisorId,\n value: share,\n tone: 'accent' as const,\n caption: `${formatMoney(row.aum, currency)} · ${formatPct(share)}`,\n };\n });\n}\n\ntype Translate = (\n key: string,\n fallback: string,\n params?: Record<string, string | number>\n) => string;\n\nexport function FirmPage() {\n const { t } = useI18n();\n const translate: Translate = (key, fallback, params) => tWithFallback(t, key, fallback, params);\n\n const myProfileQuery = useMyAdvisorProfile();\n const ensureProfile = useEnsureAdvisorProfile();\n const myProfile = myProfileQuery.data;\n const isFirmAdmin = myProfile?.role === 'FIRM_ADMIN';\n\n const [displayName, setDisplayName] = useState('');\n const [email, setEmail] = useState('');\n\n if (myProfileQuery.isLoading) {\n return (\n <SectionShell\n title={translate('brainyrich.firm.title', 'Firm')}\n description={translate(\n 'brainyrich.firm.description',\n 'Firm roster and firm-wide AUM reporting.'\n )}\n >\n <LoadingState />\n </SectionShell>\n );\n }\n\n if (!myProfile) {\n return (\n <SectionShell\n title={translate('brainyrich.firm.title', 'Firm')}\n description={translate(\n 'brainyrich.firm.description',\n 'Firm roster and firm-wide AUM reporting.'\n )}\n >\n <FadeIn>\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Building2 className=\"size-4\" aria-hidden=\"true\" />\n {translate('brainyrich.firm.join.title', 'Join the firm roster')}\n </CardTitle>\n <CardDescription>\n {translate(\n 'brainyrich.firm.join.description',\n \"You're not on this workspace's BrainyRich firm roster yet. The first advisor to register becomes the firm admin automatically; anyone after that is invited by an existing admin.\"\n )}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"firm-display-name\">\n {translate('brainyrich.firm.fields.displayName', 'Display name')}\n </Label>\n <Input\n id=\"firm-display-name\"\n value={displayName}\n onChange={(e) => setDisplayName(e.target.value)}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"firm-email\">\n {translate('brainyrich.firm.fields.email', 'Email')}\n </Label>\n <Input\n id=\"firm-email\"\n type=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n />\n </div>\n </div>\n <Button\n disabled={ensureProfile.isPending}\n onClick={() =>\n ensureProfile.mutate({\n displayName: displayName || undefined,\n email: email || undefined,\n })\n }\n >\n <UserPlus className=\"size-4\" aria-hidden=\"true\" />\n {ensureProfile.isPending ? 'Registering…' : 'Register on firm roster'}\n </Button>\n </CardContent>\n </Card>\n </FadeIn>\n </SectionShell>\n );\n }\n\n return (\n <SectionShell\n title={translate('brainyrich.firm.title', 'Firm')}\n description={translate(\n 'brainyrich.firm.description',\n 'Firm roster and firm-wide AUM reporting.'\n )}\n >\n <FadeIn>\n <Card>\n <CardContent className=\"flex flex-wrap items-center justify-between gap-3 py-4\">\n <div className=\"flex items-center gap-2\">\n <ShieldCheck className=\"size-4 text-status-success-text\" aria-hidden=\"true\" />\n <span className=\"text-sm text-text-secondary\">\n {translate('brainyrich.firm.you', 'You are on the firm roster as')}\n </span>\n <Badge variant={isFirmAdmin ? 'default' : 'secondary'}>{myProfile.role}</Badge>\n </div>\n </CardContent>\n </Card>\n </FadeIn>\n\n {isFirmAdmin ? (\n <FirmAdminView translate={translate} />\n ) : (\n <FadeIn>\n <EmptyState\n icon={<Users className=\"size-8\" aria-hidden=\"true\" />}\n title={translate(\n 'brainyrich.firm.notAdmin',\n 'Firm-wide roster and AUM reporting are visible to firm admins only.'\n )}\n />\n </FadeIn>\n )}\n </SectionShell>\n );\n}\n\nfunction FirmAdminView({ translate }: { translate: Translate }) {\n const firmReportQuery = useFirmReport();\n const advisorsQuery = useAdvisors();\n const setRole = useSetAdvisorRole();\n const setStatus = useSetAdvisorStatus();\n const [inviteUserId, setInviteUserId] = useState('');\n const [inviteRole, setInviteRole] = useState<BraAdvisorRole>('ADVISOR');\n const ensureProfile = useEnsureAdvisorProfile();\n\n const report = firmReportQuery.data;\n\n return (\n <div className=\"space-y-6\">\n <FadeIn>\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n <StatTile\n label={translate('brainyrich.firm.kpis.totalAum', 'Firm AUM')}\n value={report ? formatMoney(report.totalAum, report.currency) : '—'}\n glow\n />\n <StatTile\n label={translate('brainyrich.firm.kpis.advisors', 'Advisors')}\n value={report ? String(report.totalAdvisors) : '—'}\n />\n <StatTile\n label={translate('brainyrich.firm.kpis.households', 'Households')}\n value={report ? String(report.totalHouseholds) : '—'}\n />\n <StatTile\n label={translate('brainyrich.firm.kpis.asOf', 'As of')}\n value={report ? formatDate(report.asOf) : '—'}\n />\n </div>\n </FadeIn>\n\n <Card>\n <CardHeader>\n <CardTitle>{translate('brainyrich.firm.byAdvisor.title', 'AUM by advisor')}</CardTitle>\n <CardDescription>\n {translate(\n 'brainyrich.firm.byAdvisor.description',\n 'Sorted by AUM, largest book first — a real rollup of BraHousehold.aum per advisor.'\n )}\n </CardDescription>\n </CardHeader>\n <CardContent>\n {firmReportQuery.isLoading ? (\n <LoadingState />\n ) : firmReportQuery.isError ? (\n <ErrorState onRetry={() => firmReportQuery.refetch()} />\n ) : !report || report.byAdvisor.length === 0 ? (\n <EmptyState\n icon={<Building2 className=\"size-8\" aria-hidden=\"true\" />}\n title={translate('brainyrich.firm.byAdvisor.empty', 'No advisor books yet')}\n />\n ) : (\n <div className=\"space-y-6\">\n {advisorShareBars(report.byAdvisor, report.totalAum, report.currency).length > 0 ? (\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {translate('brainyrich.firm.byAdvisor.shareLabel', 'Share of firm AUM')}\n </p>\n <AllocationBars\n data={advisorShareBars(report.byAdvisor, report.totalAum, report.currency)}\n />\n {report.byAdvisor.length > MAX_CHARTED_ADVISORS ? (\n <p className=\"text-xs text-text-secondary\">\n Showing the {MAX_CHARTED_ADVISORS} largest books — every advisor is listed\n below.\n </p>\n ) : null}\n </div>\n ) : null}\n\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Advisor</TableHead>\n <TableHead>Role</TableHead>\n <TableHead>Households</TableHead>\n <TableHead className=\"text-right\">AUM</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {report.byAdvisor.map((row) => (\n <TableRow key={row.advisorId}>\n <TableCell className=\"font-medium\">\n {row.displayName ?? row.email ?? row.advisorId}\n </TableCell>\n <TableCell>\n <Badge variant={row.role === 'FIRM_ADMIN' ? 'default' : 'secondary'}>\n {row.role ?? '—'}\n </Badge>\n </TableCell>\n <TableCell>{row.householdCount}</TableCell>\n <TableCell className=\"text-right font-medium\">\n {formatMoney(row.aum, report.currency)}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-4\">\n <div>\n <CardTitle>{translate('brainyrich.firm.roster.title', 'Firm Roster')}</CardTitle>\n <CardDescription>\n {translate(\n 'brainyrich.firm.roster.description',\n 'Every advisor on the firm roster, with the right to change roles.'\n )}\n </CardDescription>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {translate(\n 'brainyrich.firm.roster.seatNote',\n 'Each active advisor uses one advisor seat on your plan. Deactivate an advisor to free their seat.'\n )}\n </p>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex flex-wrap items-end gap-3 rounded-lg border border-border-subtle bg-bg-sunken p-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"invite-user-id\">\n {translate('brainyrich.firm.roster.inviteUserId', 'User ID to invite')}\n </Label>\n <Input\n id=\"invite-user-id\"\n value={inviteUserId}\n onChange={(e) => setInviteUserId(e.target.value)}\n className=\"w-64\"\n placeholder=\"actor id\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label>{translate('brainyrich.firm.roster.inviteRole', 'Role')}</Label>\n <Select value={inviteRole} onValueChange={(v) => setInviteRole(v as BraAdvisorRole)}>\n <SelectTrigger className=\"w-40\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ADVISOR\">Advisor</SelectItem>\n <SelectItem value=\"FIRM_ADMIN\">Firm Admin</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <Button\n disabled={!inviteUserId.trim() || ensureProfile.isPending}\n onClick={() =>\n ensureProfile.mutate(\n { userId: inviteUserId.trim(), role: inviteRole },\n { onSuccess: () => setInviteUserId('') }\n )\n }\n >\n <PlusCircle className=\"size-4\" aria-hidden=\"true\" />\n {translate('brainyrich.firm.roster.invite', 'Invite')}\n </Button>\n </div>\n\n {advisorsQuery.isLoading ? (\n <LoadingState />\n ) : advisorsQuery.isError ? (\n <ErrorState onRetry={() => advisorsQuery.refetch()} />\n ) : advisorsQuery.data.length === 0 ? (\n <EmptyState\n icon={<Users className=\"size-8\" aria-hidden=\"true\" />}\n title={translate('brainyrich.firm.roster.empty', 'No advisors yet')}\n />\n ) : (\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Name</TableHead>\n <TableHead>Email</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Role</TableHead>\n <TableHead className=\"text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {advisorsQuery.data.map((advisor: BraAdvisor) => (\n <TableRow key={advisor.id}>\n <TableCell className=\"font-medium\">\n {advisor.displayName ?? advisor.userId}\n </TableCell>\n <TableCell>{advisor.email ?? '—'}</TableCell>\n <TableCell>\n <Badge variant={advisor.status === 'ACTIVE' ? 'default' : 'secondary'}>\n {advisor.status}\n </Badge>\n </TableCell>\n <TableCell>\n <Select\n value={advisor.role}\n onValueChange={(v) =>\n setRole.mutate({ userId: advisor.userId, role: v as BraAdvisorRole })\n }\n >\n <SelectTrigger className=\"w-40\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ADVISOR\">Advisor</SelectItem>\n <SelectItem value=\"FIRM_ADMIN\">Firm Admin</SelectItem>\n </SelectContent>\n </Select>\n </TableCell>\n <TableCell className=\"text-right\">\n <div className=\"flex items-center justify-end gap-3\">\n <span className=\"text-xs text-text-secondary\">\n {advisor.invitedById\n ? `Invited by ${advisor.invitedById}`\n : 'Self-registered'}\n </span>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={setStatus.isPending}\n onClick={() =>\n setStatus.mutate({\n userId: advisor.userId,\n status: advisor.status === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE',\n })\n }\n >\n {advisor.status === 'ACTIVE'\n ? translate('brainyrich.firm.roster.deactivate', 'Deactivate')\n : translate('brainyrich.firm.roster.reactivate', 'Reactivate')}\n </Button>\n </div>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n )}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|