@burdenoff/microfe-brainyrich 2026.706.2 → 2026.706.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/brainyrich/components/viz.js.map +1 -1
- package/dist/brainyrich/pages/AccountsPage.js.map +1 -1
- package/dist/brainyrich/pages/AiAssistantPage.js.map +1 -1
- package/dist/brainyrich/pages/AnalyticsPage.js.map +1 -1
- package/dist/brainyrich/pages/DashboardPage.js.map +1 -1
- package/dist/brainyrich/pages/FamilyPage.js.map +1 -1
- package/dist/brainyrich/pages/GoalsPage.js.map +1 -1
- package/dist/brainyrich/pages/InvestmentsPage.js.map +1 -1
- package/dist/brainyrich/pages/PortfoliosPage.js.map +1 -1
- package/dist/brainyrich/pages/ReportsPage.js +2 -1
- package/dist/brainyrich/pages/ReportsPage.js.map +1 -1
- package/dist/brainyrich/pages/RiskPage.js.map +1 -1
- package/dist/node_modules/@emotion/is-prop-valid/dist/emotion-is-prop-valid.esm.js +13 -0
- package/dist/node_modules/@emotion/is-prop-valid/dist/emotion-is-prop-valid.esm.js.map +1 -0
- package/dist/node_modules/@emotion/memoize/dist/emotion-memoize.esm.js +14 -0
- package/dist/node_modules/@emotion/memoize/dist/emotion-memoize.esm.js.map +1 -0
- package/dist/node_modules/framer-motion/dist/es/render/dom/utils/filter-props.js +2 -2
- package/package.json +5 -4
- package/dist/_virtual/__vite-optional-peer-dep_@emotion/is-prop-valid_framer-motion.js +0 -10
- package/dist/_virtual/__vite-optional-peer-dep_@emotion/is-prop-valid_framer-motion.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viz.js","names":[],"sources":["../../../src/brainyrich/components/viz.tsx"],"sourcesContent":["/**\n * BrainyRich data-viz + polish kit.\n * @module brainyrich/components/viz\n *\n * A tokenized, responsive, dark-mode-first visualization + motion kit built on\n * recharts (^2.15) and framer-motion (^12). Every colour is read from the\n * platform's SEMANTIC CSS variables at runtime (see {@link useVizColors}) so\n * charts always track the active theme and the emerald BrainyRich brand\n * (`--color-action-primary-bg`). No raw hex/rgb literals live in this file.\n *\n * All chart components are wrapped in a recharts `<ResponsiveContainer>` so they\n * resize with their parent. Motion is tasteful (<=250ms) and disabled under\n * `prefers-reduced-motion`.\n *\n * Page agents: import from `#brainyrich/components/viz` (or the components\n * barrel). Pass values straight from the real `bra*` hooks — this kit only\n * renders; it never fabricates data.\n */\nimport { useEffect, useId, useMemo, useState, type CSSProperties, type ReactNode } from 'react';\nimport {\n Area,\n AreaChart,\n Bar,\n BarChart,\n Cell,\n Line,\n LineChart,\n Pie,\n PieChart,\n ReferenceLine,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from 'recharts';\nimport { AnimatePresence, motion, useReducedMotion } from 'framer-motion';\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nfunction cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n\n// ============================================================================\n// Token → colour resolution\n// ============================================================================\n//\n// recharts needs concrete colour strings (fills, stroke, gradient stops), so we\n// resolve each semantic CSS variable to its computed value off\n// `document.documentElement` and re-read whenever the theme attribute flips.\n// Consumers NEVER hard-code colours: they name a semantic token here.\n\nconst CORE_TOKENS = {\n brand: '--color-action-primary-bg',\n brandBorder: '--color-action-primary-border',\n grid: '--color-chart-grid',\n axis: '--color-chart-axis',\n positive: '--color-chart-positive',\n negative: '--color-chart-negative',\n success: '--color-status-success',\n error: '--color-status-error',\n warning: '--color-status-warning',\n info: '--color-status-info',\n teal: '--color-accent-teal',\n blue: '--color-accent-blue',\n purple: '--color-accent-purple',\n indigo: '--color-accent-indigo',\n textPrimary: '--color-text-primary',\n textSecondary: '--color-text-secondary',\n surface: '--color-bg-elevated',\n border: '--color-border-subtle',\n} as const;\n\ntype CoreToken = keyof typeof CORE_TOKENS;\n\n/** Ordered categorical palette — emerald brand first, then distinct chart hues. */\nconst PALETTE_VARS = [\n '--color-action-primary-bg',\n '--color-chart-2',\n '--color-chart-3',\n '--color-chart-4',\n '--color-chart-5',\n '--color-chart-6',\n '--color-chart-7',\n '--color-chart-8',\n '--color-chart-9',\n '--color-chart-10',\n '--color-chart-11',\n '--color-chart-12',\n];\n\nexport type VizColors = Record<CoreToken, string> & {\n /** Categorical palette (index 0 = emerald brand). */\n palette: string[];\n /** Resolve any semantic tone name to a concrete colour string. */\n tone: (tone?: VizTone) => string;\n};\n\n/** Named tones a datum may request; falls back to the categorical palette. */\nexport type VizTone =\n | 'brand'\n | 'primary'\n | 'success'\n | 'positive'\n | 'error'\n | 'negative'\n | 'warning'\n | 'info'\n | 'teal'\n | 'blue'\n | 'purple'\n | 'indigo'\n | 'neutral';\n\nfunction readVar(name: string): string {\n if (typeof window === 'undefined') return `var(${name})`;\n const v = getComputedStyle(document.documentElement).getPropertyValue(name).trim();\n return v || `var(${name})`;\n}\n\n/**\n * Resolve the semantic viz palette from CSS variables and keep it in sync with\n * the active theme. Returns concrete colour strings for use in recharts props.\n */\nexport function useVizColors(): VizColors {\n const [tick, setTick] = useState(0);\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const rerender = () => setTick((t) => t + 1);\n const obs = new MutationObserver(rerender);\n obs.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['data-theme', 'class', 'style'],\n });\n return () => obs.disconnect();\n }, []);\n\n return useMemo(() => {\n void tick; // re-resolve whenever the theme attribute changes\n const core = Object.fromEntries(\n (Object.keys(CORE_TOKENS) as CoreToken[]).map((k) => [k, readVar(CORE_TOKENS[k])])\n ) as Record<CoreToken, string>;\n const palette = PALETTE_VARS.map(readVar);\n\n const tone = (t?: VizTone): string => {\n switch (t) {\n case 'brand':\n case 'primary':\n return core.brand;\n case 'success':\n case 'positive':\n return core.success;\n case 'error':\n case 'negative':\n return core.error;\n case 'warning':\n return core.warning;\n case 'info':\n return core.info;\n case 'teal':\n return core.teal;\n case 'blue':\n return core.blue;\n case 'purple':\n return core.purple;\n case 'indigo':\n return core.indigo;\n case 'neutral':\n return core.textSecondary;\n default:\n return core.brand;\n }\n };\n\n return { ...core, palette, tone };\n }, [tick]);\n}\n\n// ============================================================================\n// Shared chart tooltip (themed — recharts' default is light-only)\n// ============================================================================\n\ninterface TooltipEntry {\n name?: string | number;\n value?: string | number;\n color?: string;\n payload?: Record<string, unknown>;\n}\n\nfunction useTooltipStyles(): { wrapper: CSSProperties; label: CSSProperties } {\n const c = useVizColors();\n return {\n wrapper: {\n background: c.surface,\n border: `1px solid ${c.border}`,\n borderRadius: 10,\n padding: '8px 12px',\n color: c.textPrimary,\n fontSize: 12,\n boxShadow: 'var(--shadow-pop, 0 8px 24px rgba(0,0,0,0.35))',\n },\n label: { color: c.textSecondary, marginBottom: 4, fontSize: 11 },\n };\n}\n\nfunction ChartTooltip({\n active,\n payload,\n label,\n valueFormatter,\n}: {\n active?: boolean;\n payload?: TooltipEntry[];\n label?: string | number;\n valueFormatter?: (v: number) => string;\n}) {\n const styles = useTooltipStyles();\n if (!active || !payload || payload.length === 0) return null;\n return (\n <div style={styles.wrapper} role=\"tooltip\">\n {label !== undefined && label !== '' ? <div style={styles.label}>{label}</div> : null}\n {payload.map((entry, i) => {\n const raw = typeof entry.value === 'number' ? entry.value : Number(entry.value);\n const shown =\n valueFormatter && Number.isFinite(raw) ? valueFormatter(raw) : String(entry.value ?? '');\n return (\n <div key={i} className=\"flex items-center gap-2\">\n {entry.color ? (\n <span\n aria-hidden=\"true\"\n style={{\n width: 8,\n height: 8,\n borderRadius: 9999,\n background: entry.color,\n display: 'inline-block',\n }}\n />\n ) : null}\n {entry.name ? <span style={{ color: styles.label.color }}>{entry.name}:</span> : null}\n <span style={{ fontWeight: 600 }}>{shown}</span>\n </div>\n );\n })}\n </div>\n );\n}\n\n// ============================================================================\n// Motion helpers\n// ============================================================================\n\n/**\n * Staggered enter animation (translateY + opacity). Fully disabled under\n * `prefers-reduced-motion` (renders a plain element). Use `index` to stagger a\n * list/grid of cards.\n */\nexport function FadeIn({\n children,\n index = 0,\n delay = 0,\n className,\n as = 'div',\n}: {\n children: ReactNode;\n index?: number;\n delay?: number;\n className?: string;\n as?: 'div' | 'li' | 'section';\n}) {\n const reduce = useReducedMotion();\n if (reduce) {\n const Tag = as;\n return <Tag className={className}>{children}</Tag>;\n }\n const MotionTag = motion[as];\n return (\n <MotionTag\n className={className}\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.25, ease: 'easeOut', delay: delay + index * 0.04 }}\n >\n {children}\n </MotionTag>\n );\n}\n\n// ============================================================================\n// GlassCard — sanctioned glass/elevation surface with graceful fallback\n// ============================================================================\n\n/**\n * Floating/hero surface using the sanctioned glass tokens. Falls back to the\n * standard elevated surface when a glass token is absent (older brand builds).\n */\nexport function GlassCard({\n children,\n className,\n glow = false,\n}: {\n children: ReactNode;\n className?: string;\n /** Add a subtle emerald glow ring. */\n glow?: boolean;\n}) {\n return (\n <div\n className={cn(\n 'rounded-2xl border p-5',\n // Glass tokens with hard fallbacks to always-present semantic tokens.\n 'border-[var(--color-border-glass,var(--color-border-subtle))]',\n 'bg-[var(--color-surface-glass,var(--color-bg-elevated))]',\n 'shadow-[var(--shadow-float,var(--shadow-elevation-3))]',\n 'backdrop-blur-[var(--blur-glass,12px)]',\n glow && 'ring-1 ring-[var(--color-action-primary-border)]/40',\n className\n )}\n >\n {children}\n </div>\n );\n}\n\n// ============================================================================\n// DonutChart\n// ============================================================================\n\nexport interface DonutDatum {\n label: string;\n value: number;\n tone?: VizTone;\n}\n\nexport function DonutChart({\n data,\n centerLabel,\n centerValue,\n legend = true,\n height = 220,\n valueFormatter,\n}: {\n data: DonutDatum[];\n centerLabel?: string;\n centerValue?: string;\n legend?: boolean;\n height?: number;\n valueFormatter?: (v: number) => string;\n}) {\n const c = useVizColors();\n const reduce = useReducedMotion();\n const rows = data.filter((d) => Number.isFinite(d.value) && d.value > 0);\n\n if (rows.length === 0) {\n return <p className=\"text-sm text-text-secondary\">No data.</p>;\n }\n\n const colorFor = (d: DonutDatum, i: number) =>\n d.tone ? c.tone(d.tone) : c.palette[i % c.palette.length];\n\n return (\n <div className=\"flex flex-col items-center gap-4 sm:flex-row sm:items-center\">\n <div className=\"relative\" style={{ width: '100%', maxWidth: 260, height }}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <PieChart>\n <Pie\n data={rows}\n dataKey=\"value\"\n nameKey=\"label\"\n innerRadius=\"62%\"\n outerRadius=\"90%\"\n paddingAngle={2}\n stroke=\"transparent\"\n isAnimationActive={!reduce}\n animationDuration={250}\n >\n {rows.map((d, i) => (\n <Cell key={d.label} fill={colorFor(d, i)} />\n ))}\n </Pie>\n <Tooltip content={<ChartTooltip valueFormatter={valueFormatter} />} />\n </PieChart>\n </ResponsiveContainer>\n {centerValue || centerLabel ? (\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center\">\n {centerValue ? (\n <span className=\"text-xl font-semibold text-text-primary\">{centerValue}</span>\n ) : null}\n {centerLabel ? (\n <span className=\"text-xs text-text-secondary\">{centerLabel}</span>\n ) : null}\n </div>\n ) : null}\n </div>\n {legend ? (\n <ul className=\"grid flex-1 grid-cols-1 gap-2 sm:grid-cols-2\" aria-label=\"Legend\">\n {rows.map((d, i) => (\n <li key={d.label} className=\"flex items-center gap-2 text-xs\">\n <span\n aria-hidden=\"true\"\n className=\"size-2.5 shrink-0 rounded-full\"\n style={{ background: colorFor(d, i) }}\n />\n <span className=\"truncate text-text-secondary\">{d.label}</span>\n <span className=\"ml-auto font-medium text-text-primary\">\n {valueFormatter ? valueFormatter(d.value) : d.value}\n </span>\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n );\n}\n\n// ============================================================================\n// Sparkline\n// ============================================================================\n\nexport function Sparkline({\n data,\n tone = 'brand',\n height = 40,\n className,\n}: {\n data: number[];\n tone?: VizTone;\n height?: number;\n className?: string;\n}) {\n const c = useVizColors();\n const stroke = c.tone(tone);\n const points = data.filter((n) => Number.isFinite(n));\n const gradientId = `spark-${useId().replace(/:/g, '')}`;\n if (points.length < 2) {\n return <div className={className} style={{ height }} aria-hidden=\"true\" />;\n }\n const chartData = points.map((y, x) => ({ x, y }));\n return (\n <div className={className} style={{ width: '100%', height }} aria-hidden=\"true\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={chartData} margin={{ top: 2, right: 0, bottom: 0, left: 0 }}>\n <defs>\n <linearGradient id={gradientId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={stroke} stopOpacity={0.28} />\n <stop offset=\"100%\" stopColor={stroke} stopOpacity={0} />\n </linearGradient>\n </defs>\n <Area\n type=\"monotone\"\n dataKey=\"y\"\n stroke={stroke}\n strokeWidth={2}\n fill={`url(#${gradientId})`}\n isAnimationActive={false}\n dot={false}\n />\n </AreaChart>\n </ResponsiveContainer>\n </div>\n );\n}\n\n// ============================================================================\n// ProgressRing — clean SVG radial progress\n// ============================================================================\n\nexport function ProgressRing({\n value,\n label,\n tone = 'brand',\n size = 120,\n strokeWidth = 10,\n}: {\n value: number;\n label?: string;\n tone?: VizTone;\n size?: number;\n strokeWidth?: number;\n}) {\n const c = useVizColors();\n const reduce = useReducedMotion();\n const pct = Math.max(0, Math.min(100, Number.isFinite(value) ? value : 0));\n const r = (size - strokeWidth) / 2;\n const circ = 2 * Math.PI * r;\n const dash = (pct / 100) * circ;\n const stroke = c.tone(tone);\n\n return (\n <div\n className=\"relative inline-flex items-center justify-center\"\n style={{ width: size, height: size }}\n role=\"progressbar\"\n aria-valuenow={Math.round(pct)}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={label ?? 'Progress'}\n >\n <svg width={size} height={size} className=\"-rotate-90\">\n <circle\n cx={size / 2}\n cy={size / 2}\n r={r}\n fill=\"none\"\n stroke={c.border}\n strokeWidth={strokeWidth}\n />\n <motion.circle\n cx={size / 2}\n cy={size / 2}\n r={r}\n fill=\"none\"\n stroke={stroke}\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeDasharray={circ}\n initial={{ strokeDashoffset: reduce ? circ - dash : circ }}\n animate={{ strokeDashoffset: circ - dash }}\n transition={{ duration: reduce ? 0 : 0.25, ease: 'easeOut' }}\n />\n </svg>\n <div className=\"absolute inset-0 flex flex-col items-center justify-center\">\n <span className=\"text-lg font-semibold text-text-primary\">{Math.round(pct)}%</span>\n {label ? <span className=\"text-[10px] text-text-secondary\">{label}</span> : null}\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// TrendBars — diverging inflow/outflow cashflow bars\n// ============================================================================\n\nexport interface TrendDatum {\n label: string;\n inflow: number;\n outflow: number;\n}\n\nexport function TrendBars({\n data,\n height = 240,\n valueFormatter,\n}: {\n data: TrendDatum[];\n height?: number;\n valueFormatter?: (v: number) => string;\n}) {\n const c = useVizColors();\n const reduce = useReducedMotion();\n const rows = data.filter((d) => Number.isFinite(d.inflow) && Number.isFinite(d.outflow));\n if (rows.length === 0) {\n return <p className=\"text-sm text-text-secondary\">No cashflow data.</p>;\n }\n // Diverging: inflow up (positive), outflow down (negative).\n const chartData = rows.map((d) => ({\n label: d.label,\n inflow: Math.abs(d.inflow),\n outflow: -Math.abs(d.outflow),\n }));\n\n return (\n <div style={{ width: '100%', height }}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart data={chartData} margin={{ top: 8, right: 8, bottom: 0, left: 8 }} stackOffset=\"sign\">\n <XAxis\n dataKey=\"label\"\n tick={{ fill: c.textSecondary, fontSize: 11 }}\n axisLine={{ stroke: c.grid }}\n tickLine={false}\n />\n <YAxis\n tick={{ fill: c.textSecondary, fontSize: 11 }}\n axisLine={false}\n tickLine={false}\n width={44}\n tickFormatter={(v: number) =>\n valueFormatter ? valueFormatter(Math.abs(v)) : String(Math.abs(v))\n }\n />\n <ReferenceLine y={0} stroke={c.grid} />\n <Tooltip\n cursor={{ fill: c.border, opacity: 0.25 }}\n content={<ChartTooltip valueFormatter={(v) => (valueFormatter ? valueFormatter(Math.abs(v)) : String(Math.abs(v)))} />}\n />\n <Bar\n dataKey=\"inflow\"\n name=\"Inflow\"\n fill={c.positive}\n radius={[4, 4, 0, 0]}\n isAnimationActive={!reduce}\n animationDuration={250}\n />\n <Bar\n dataKey=\"outflow\"\n name=\"Outflow\"\n fill={c.negative}\n radius={[0, 0, 4, 4]}\n isAnimationActive={!reduce}\n animationDuration={250}\n />\n </BarChart>\n </ResponsiveContainer>\n </div>\n );\n}\n\n// ============================================================================\n// MiniAreaChart — projection with optional dashed target line\n// ============================================================================\n\nexport interface AreaDatum {\n x: string | number;\n y: number;\n}\n\nexport function MiniAreaChart({\n data,\n target,\n tone = 'brand',\n height = 200,\n valueFormatter,\n}: {\n data: AreaDatum[];\n target?: number;\n tone?: VizTone;\n height?: number;\n valueFormatter?: (v: number) => string;\n}) {\n const c = useVizColors();\n const reduce = useReducedMotion();\n const stroke = c.tone(tone);\n const rows = data.filter((d) => Number.isFinite(d.y));\n const gradientId = `area-${useId().replace(/:/g, '')}`;\n if (rows.length < 2) {\n return <p className=\"text-sm text-text-secondary\">Not enough data to chart.</p>;\n }\n return (\n <div style={{ width: '100%', height }}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={rows} margin={{ top: 8, right: 8, bottom: 0, left: 8 }}>\n <defs>\n <linearGradient id={gradientId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={stroke} stopOpacity={0.3} />\n <stop offset=\"100%\" stopColor={stroke} stopOpacity={0.02} />\n </linearGradient>\n </defs>\n <XAxis\n dataKey=\"x\"\n tick={{ fill: c.textSecondary, fontSize: 11 }}\n axisLine={{ stroke: c.grid }}\n tickLine={false}\n />\n <YAxis\n tick={{ fill: c.textSecondary, fontSize: 11 }}\n axisLine={false}\n tickLine={false}\n width={44}\n tickFormatter={(v: number) => (valueFormatter ? valueFormatter(v) : String(v))}\n />\n <Tooltip content={<ChartTooltip valueFormatter={valueFormatter} />} />\n {target !== undefined && Number.isFinite(target) ? (\n <ReferenceLine\n y={target}\n stroke={c.textSecondary}\n strokeDasharray=\"5 4\"\n label={{\n value: valueFormatter ? `Target ${valueFormatter(target)}` : `Target ${target}`,\n position: 'insideTopRight',\n fill: c.textSecondary,\n fontSize: 10,\n }}\n />\n ) : null}\n <Area\n type=\"monotone\"\n dataKey=\"y\"\n stroke={stroke}\n strokeWidth={2}\n fill={`url(#${gradientId})`}\n isAnimationActive={!reduce}\n animationDuration={250}\n dot={false}\n />\n </AreaChart>\n </ResponsiveContainer>\n </div>\n );\n}\n\n// ============================================================================\n// LifecycleStepper — horizontal proposal/rebalance lifecycle\n// ============================================================================\n\nconst NEGATIVE_TERMINAL = /reject|cancel|abandon|decline|fail/i;\n\nexport function LifecycleStepper({\n steps,\n current,\n className,\n}: {\n steps: string[];\n current: string;\n className?: string;\n}) {\n const isNegativeTerminal = NEGATIVE_TERMINAL.test(current);\n const currentIndex = steps.findIndex((s) => s.toLowerCase() === current.toLowerCase());\n\n return (\n <ol\n className={cn('flex w-full items-center overflow-x-auto', className)}\n aria-label=\"Lifecycle progress\"\n >\n {steps.map((step, i) => {\n const isLast = i === steps.length - 1;\n const done = currentIndex >= 0 && i < currentIndex;\n const active = currentIndex >= 0 && i === currentIndex && !isNegativeTerminal;\n const state = done ? 'done' : active ? 'active' : 'upcoming';\n return (\n <li key={step} className=\"flex min-w-fit flex-1 items-center\" aria-current={active ? 'step' : undefined}>\n <div className=\"flex flex-col items-center gap-1.5 px-1\">\n <span\n className={cn(\n 'flex size-7 items-center justify-center rounded-full border text-xs font-semibold transition-colors',\n state === 'done' &&\n 'border-transparent bg-[var(--color-action-primary-bg)] text-[var(--color-action-primary-text)]',\n state === 'active' &&\n 'border-[var(--color-action-primary-border)] bg-[var(--color-action-primary-bg)]/15 text-[var(--color-action-primary-text)]',\n state === 'upcoming' && 'border-border-subtle bg-bg-sunken text-text-secondary'\n )}\n aria-hidden=\"true\"\n >\n {done ? '✓' : i + 1}\n </span>\n <span\n className={cn(\n 'whitespace-nowrap text-[11px]',\n active ? 'font-medium text-text-primary' : 'text-text-secondary'\n )}\n >\n {step}\n </span>\n </div>\n {!isLast ? (\n <span\n aria-hidden=\"true\"\n className={cn(\n 'mx-1 h-0.5 flex-1 rounded-full',\n done ? 'bg-[var(--color-action-primary-bg)]' : 'bg-border-subtle'\n )}\n />\n ) : null}\n </li>\n );\n })}\n {isNegativeTerminal ? (\n <li className=\"ml-2 flex min-w-fit items-center gap-1.5\" aria-current=\"step\">\n <span className=\"mx-1 h-0.5 w-6 rounded-full bg-status-error-border\" aria-hidden=\"true\" />\n <div className=\"flex flex-col items-center gap-1.5 px-1\">\n <span\n className=\"flex size-7 items-center justify-center rounded-full border border-transparent bg-status-error text-xs font-semibold text-status-error-text\"\n aria-hidden=\"true\"\n >\n ✕\n </span>\n <span className=\"whitespace-nowrap text-[11px] font-medium text-status-error-text\">\n {current}\n </span>\n </div>\n </li>\n ) : null}\n </ol>\n );\n}\n\n// ============================================================================\n// StatTile — premium KPI tile\n// ============================================================================\n\nexport function StatTile({\n label,\n value,\n delta,\n deltaTone,\n deltaSuffix = '%',\n spark,\n sparkTone = 'brand',\n glow = false,\n icon,\n className,\n}: {\n label: string;\n value: string;\n /** Optional signed delta rendered as a chip (icon + sign + status colour). */\n delta?: number;\n /** Override the auto tone derived from the delta sign. */\n deltaTone?: 'positive' | 'negative' | 'neutral';\n deltaSuffix?: string;\n /** Optional inline sparkline series. */\n spark?: number[];\n sparkTone?: VizTone;\n /** Subtle emerald glow ring for hero tiles. */\n glow?: boolean;\n icon?: ReactNode;\n className?: string;\n}) {\n const positive = deltaTone ? deltaTone === 'positive' : (delta ?? 0) > 0;\n const negative = deltaTone ? deltaTone === 'negative' : (delta ?? 0) < 0;\n const deltaClass = positive\n ? 'text-status-success-text bg-status-success-bg'\n : negative\n ? 'text-status-error-text bg-status-error-bg'\n : 'text-text-secondary bg-bg-sunken';\n const sign = delta !== undefined && delta > 0 ? '+' : '';\n\n return (\n <div\n className={cn(\n 'group relative overflow-hidden rounded-xl border border-border-subtle bg-bg-surface p-4 transition-shadow',\n 'motion-safe:hover:shadow-[var(--shadow-pop-hover,var(--shadow-elevation-2))]',\n glow && 'ring-1 ring-[var(--color-action-primary-border)]/40',\n className\n )}\n >\n {glow ? (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute -right-8 -top-8 size-24 rounded-full bg-[var(--color-action-primary-bg)]/15 blur-2xl\"\n />\n ) : null}\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"text-xs text-text-secondary\">{label}</p>\n {icon ? <span className=\"text-text-secondary\">{icon}</span> : null}\n </div>\n <p className=\"mt-1 text-2xl font-semibold text-text-primary\">{value}</p>\n <div className=\"mt-2 flex items-center justify-between gap-2\">\n {delta !== undefined && Number.isFinite(delta) ? (\n <span\n className={cn(\n 'inline-flex items-center gap-0.5 rounded-full px-1.5 py-0.5 text-xs font-medium',\n deltaClass\n )}\n >\n <span aria-hidden=\"true\">{positive ? '▲' : negative ? '▼' : '—'}</span>\n {sign}\n {delta.toFixed(1)}\n {deltaSuffix}\n </span>\n ) : (\n <span />\n )}\n {spark && spark.length > 1 ? (\n <div className=\"w-20 shrink-0\">\n <Sparkline data={spark} tone={sparkTone} height={28} />\n </div>\n ) : null}\n </div>\n </div>\n );\n}\n\nexport { AnimatePresence };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"viz.js","names":[],"sources":["../../../src/brainyrich/components/viz.tsx"],"sourcesContent":["/**\n * BrainyRich data-viz + polish kit.\n * @module brainyrich/components/viz\n *\n * A tokenized, responsive, dark-mode-first visualization + motion kit built on\n * recharts (^2.15) and framer-motion (^12). Every colour is read from the\n * platform's SEMANTIC CSS variables at runtime (see {@link useVizColors}) so\n * charts always track the active theme and the emerald BrainyRich brand\n * (`--color-action-primary-bg`). No raw hex/rgb literals live in this file.\n *\n * All chart components are wrapped in a recharts `<ResponsiveContainer>` so they\n * resize with their parent. Motion is tasteful (<=250ms) and disabled under\n * `prefers-reduced-motion`.\n *\n * Page agents: import from `#brainyrich/components/viz` (or the components\n * barrel). Pass values straight from the real `bra*` hooks — this kit only\n * renders; it never fabricates data.\n */\nimport { useEffect, useId, useMemo, useState, type CSSProperties, type ReactNode } from 'react';\nimport {\n Area,\n AreaChart,\n Bar,\n BarChart,\n Cell,\n Line,\n LineChart,\n Pie,\n PieChart,\n ReferenceLine,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from 'recharts';\nimport { AnimatePresence, motion, useReducedMotion } from 'framer-motion';\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nfunction cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n\n// ============================================================================\n// Token → colour resolution\n// ============================================================================\n//\n// recharts needs concrete colour strings (fills, stroke, gradient stops), so we\n// resolve each semantic CSS variable to its computed value off\n// `document.documentElement` and re-read whenever the theme attribute flips.\n// Consumers NEVER hard-code colours: they name a semantic token here.\n\nconst CORE_TOKENS = {\n brand: '--color-action-primary-bg',\n brandBorder: '--color-action-primary-border',\n grid: '--color-chart-grid',\n axis: '--color-chart-axis',\n positive: '--color-chart-positive',\n negative: '--color-chart-negative',\n success: '--color-status-success',\n error: '--color-status-error',\n warning: '--color-status-warning',\n info: '--color-status-info',\n teal: '--color-accent-teal',\n blue: '--color-accent-blue',\n purple: '--color-accent-purple',\n indigo: '--color-accent-indigo',\n textPrimary: '--color-text-primary',\n textSecondary: '--color-text-secondary',\n surface: '--color-bg-elevated',\n border: '--color-border-subtle',\n} as const;\n\ntype CoreToken = keyof typeof CORE_TOKENS;\n\n/** Ordered categorical palette — emerald brand first, then distinct chart hues. */\nconst PALETTE_VARS = [\n '--color-action-primary-bg',\n '--color-chart-2',\n '--color-chart-3',\n '--color-chart-4',\n '--color-chart-5',\n '--color-chart-6',\n '--color-chart-7',\n '--color-chart-8',\n '--color-chart-9',\n '--color-chart-10',\n '--color-chart-11',\n '--color-chart-12',\n];\n\nexport type VizColors = Record<CoreToken, string> & {\n /** Categorical palette (index 0 = emerald brand). */\n palette: string[];\n /** Resolve any semantic tone name to a concrete colour string. */\n tone: (tone?: VizTone) => string;\n};\n\n/** Named tones a datum may request; falls back to the categorical palette. */\nexport type VizTone =\n | 'brand'\n | 'primary'\n | 'success'\n | 'positive'\n | 'error'\n | 'negative'\n | 'warning'\n | 'info'\n | 'teal'\n | 'blue'\n | 'purple'\n | 'indigo'\n | 'neutral';\n\nfunction readVar(name: string): string {\n if (typeof window === 'undefined') return `var(${name})`;\n const v = getComputedStyle(document.documentElement).getPropertyValue(name).trim();\n return v || `var(${name})`;\n}\n\n/**\n * Resolve the semantic viz palette from CSS variables and keep it in sync with\n * the active theme. Returns concrete colour strings for use in recharts props.\n */\nexport function useVizColors(): VizColors {\n const [tick, setTick] = useState(0);\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const rerender = () => setTick((t) => t + 1);\n const obs = new MutationObserver(rerender);\n obs.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['data-theme', 'class', 'style'],\n });\n return () => obs.disconnect();\n }, []);\n\n return useMemo(() => {\n void tick; // re-resolve whenever the theme attribute changes\n const core = Object.fromEntries(\n (Object.keys(CORE_TOKENS) as CoreToken[]).map((k) => [k, readVar(CORE_TOKENS[k])])\n ) as Record<CoreToken, string>;\n const palette = PALETTE_VARS.map(readVar);\n\n const tone = (t?: VizTone): string => {\n switch (t) {\n case 'brand':\n case 'primary':\n return core.brand;\n case 'success':\n case 'positive':\n return core.success;\n case 'error':\n case 'negative':\n return core.error;\n case 'warning':\n return core.warning;\n case 'info':\n return core.info;\n case 'teal':\n return core.teal;\n case 'blue':\n return core.blue;\n case 'purple':\n return core.purple;\n case 'indigo':\n return core.indigo;\n case 'neutral':\n return core.textSecondary;\n default:\n return core.brand;\n }\n };\n\n return { ...core, palette, tone };\n }, [tick]);\n}\n\n// ============================================================================\n// Shared chart tooltip (themed — recharts' default is light-only)\n// ============================================================================\n\ninterface TooltipEntry {\n name?: string | number;\n value?: string | number;\n color?: string;\n payload?: Record<string, unknown>;\n}\n\nfunction useTooltipStyles(): { wrapper: CSSProperties; label: CSSProperties } {\n const c = useVizColors();\n return {\n wrapper: {\n background: c.surface,\n border: `1px solid ${c.border}`,\n borderRadius: 10,\n padding: '8px 12px',\n color: c.textPrimary,\n fontSize: 12,\n boxShadow: 'var(--shadow-pop, 0 8px 24px rgba(0,0,0,0.35))',\n },\n label: { color: c.textSecondary, marginBottom: 4, fontSize: 11 },\n };\n}\n\nfunction ChartTooltip({\n active,\n payload,\n label,\n valueFormatter,\n}: {\n active?: boolean;\n payload?: TooltipEntry[];\n label?: string | number;\n valueFormatter?: (v: number) => string;\n}) {\n const styles = useTooltipStyles();\n if (!active || !payload || payload.length === 0) return null;\n return (\n <div style={styles.wrapper} role=\"tooltip\">\n {label !== undefined && label !== '' ? <div style={styles.label}>{label}</div> : null}\n {payload.map((entry, i) => {\n const raw = typeof entry.value === 'number' ? entry.value : Number(entry.value);\n const shown =\n valueFormatter && Number.isFinite(raw) ? valueFormatter(raw) : String(entry.value ?? '');\n return (\n <div key={i} className=\"flex items-center gap-2\">\n {entry.color ? (\n <span\n aria-hidden=\"true\"\n style={{\n width: 8,\n height: 8,\n borderRadius: 9999,\n background: entry.color,\n display: 'inline-block',\n }}\n />\n ) : null}\n {entry.name ? <span style={{ color: styles.label.color }}>{entry.name}:</span> : null}\n <span style={{ fontWeight: 600 }}>{shown}</span>\n </div>\n );\n })}\n </div>\n );\n}\n\n// ============================================================================\n// Motion helpers\n// ============================================================================\n\n/**\n * Staggered enter animation (translateY + opacity). Fully disabled under\n * `prefers-reduced-motion` (renders a plain element). Use `index` to stagger a\n * list/grid of cards.\n */\nexport function FadeIn({\n children,\n index = 0,\n delay = 0,\n className,\n as = 'div',\n}: {\n children: ReactNode;\n index?: number;\n delay?: number;\n className?: string;\n as?: 'div' | 'li' | 'section';\n}) {\n const reduce = useReducedMotion();\n if (reduce) {\n const Tag = as;\n return <Tag className={className}>{children}</Tag>;\n }\n const MotionTag = motion[as];\n return (\n <MotionTag\n className={className}\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.25, ease: 'easeOut', delay: delay + index * 0.04 }}\n >\n {children}\n </MotionTag>\n );\n}\n\n// ============================================================================\n// GlassCard — sanctioned glass/elevation surface with graceful fallback\n// ============================================================================\n\n/**\n * Floating/hero surface using the sanctioned glass tokens. Falls back to the\n * standard elevated surface when a glass token is absent (older brand builds).\n */\nexport function GlassCard({\n children,\n className,\n glow = false,\n}: {\n children: ReactNode;\n className?: string;\n /** Add a subtle emerald glow ring. */\n glow?: boolean;\n}) {\n return (\n <div\n className={cn(\n 'rounded-2xl border p-5',\n // Glass tokens with hard fallbacks to always-present semantic tokens.\n 'border-[var(--color-border-glass,var(--color-border-subtle))]',\n 'bg-[var(--color-surface-glass,var(--color-bg-elevated))]',\n 'shadow-[var(--shadow-float,var(--shadow-elevation-3))]',\n 'backdrop-blur-[var(--blur-glass,12px)]',\n glow && 'ring-1 ring-[var(--color-action-primary-border)]/40',\n className\n )}\n >\n {children}\n </div>\n );\n}\n\n// ============================================================================\n// DonutChart\n// ============================================================================\n\nexport interface DonutDatum {\n label: string;\n value: number;\n tone?: VizTone;\n}\n\nexport function DonutChart({\n data,\n centerLabel,\n centerValue,\n legend = true,\n height = 220,\n valueFormatter,\n}: {\n data: DonutDatum[];\n centerLabel?: string;\n centerValue?: string;\n legend?: boolean;\n height?: number;\n valueFormatter?: (v: number) => string;\n}) {\n const c = useVizColors();\n const reduce = useReducedMotion();\n const rows = data.filter((d) => Number.isFinite(d.value) && d.value > 0);\n\n if (rows.length === 0) {\n return <p className=\"text-sm text-text-secondary\">No data.</p>;\n }\n\n const colorFor = (d: DonutDatum, i: number) =>\n d.tone ? c.tone(d.tone) : c.palette[i % c.palette.length];\n\n return (\n <div className=\"flex flex-col items-center gap-4 sm:flex-row sm:items-center\">\n <div className=\"relative\" style={{ width: '100%', maxWidth: 260, height }}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <PieChart>\n <Pie\n data={rows}\n dataKey=\"value\"\n nameKey=\"label\"\n innerRadius=\"62%\"\n outerRadius=\"90%\"\n paddingAngle={2}\n stroke=\"transparent\"\n isAnimationActive={!reduce}\n animationDuration={250}\n >\n {rows.map((d, i) => (\n <Cell key={d.label} fill={colorFor(d, i)} />\n ))}\n </Pie>\n <Tooltip content={<ChartTooltip valueFormatter={valueFormatter} />} />\n </PieChart>\n </ResponsiveContainer>\n {centerValue || centerLabel ? (\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center\">\n {centerValue ? (\n <span className=\"text-xl font-semibold text-text-primary\">{centerValue}</span>\n ) : null}\n {centerLabel ? (\n <span className=\"text-xs text-text-secondary\">{centerLabel}</span>\n ) : null}\n </div>\n ) : null}\n </div>\n {legend ? (\n <ul className=\"grid flex-1 grid-cols-1 gap-2 sm:grid-cols-2\" aria-label=\"Legend\">\n {rows.map((d, i) => (\n <li key={d.label} className=\"flex items-center gap-2 text-xs\">\n <span\n aria-hidden=\"true\"\n className=\"size-2.5 shrink-0 rounded-full\"\n style={{ background: colorFor(d, i) }}\n />\n <span className=\"truncate text-text-secondary\">{d.label}</span>\n <span className=\"ml-auto font-medium text-text-primary\">\n {valueFormatter ? valueFormatter(d.value) : d.value}\n </span>\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n );\n}\n\n// ============================================================================\n// Sparkline\n// ============================================================================\n\nexport function Sparkline({\n data,\n tone = 'brand',\n height = 40,\n className,\n}: {\n data: number[];\n tone?: VizTone;\n height?: number;\n className?: string;\n}) {\n const c = useVizColors();\n const stroke = c.tone(tone);\n const points = data.filter((n) => Number.isFinite(n));\n const gradientId = `spark-${useId().replace(/:/g, '')}`;\n if (points.length < 2) {\n return <div className={className} style={{ height }} aria-hidden=\"true\" />;\n }\n const chartData = points.map((y, x) => ({ x, y }));\n return (\n <div className={className} style={{ width: '100%', height }} aria-hidden=\"true\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={chartData} margin={{ top: 2, right: 0, bottom: 0, left: 0 }}>\n <defs>\n <linearGradient id={gradientId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={stroke} stopOpacity={0.28} />\n <stop offset=\"100%\" stopColor={stroke} stopOpacity={0} />\n </linearGradient>\n </defs>\n <Area\n type=\"monotone\"\n dataKey=\"y\"\n stroke={stroke}\n strokeWidth={2}\n fill={`url(#${gradientId})`}\n isAnimationActive={false}\n dot={false}\n />\n </AreaChart>\n </ResponsiveContainer>\n </div>\n );\n}\n\n// ============================================================================\n// ProgressRing — clean SVG radial progress\n// ============================================================================\n\nexport function ProgressRing({\n value,\n label,\n tone = 'brand',\n size = 120,\n strokeWidth = 10,\n}: {\n value: number;\n label?: string;\n tone?: VizTone;\n size?: number;\n strokeWidth?: number;\n}) {\n const c = useVizColors();\n const reduce = useReducedMotion();\n const pct = Math.max(0, Math.min(100, Number.isFinite(value) ? value : 0));\n const r = (size - strokeWidth) / 2;\n const circ = 2 * Math.PI * r;\n const dash = (pct / 100) * circ;\n const stroke = c.tone(tone);\n\n return (\n <div\n className=\"relative inline-flex items-center justify-center\"\n style={{ width: size, height: size }}\n role=\"progressbar\"\n aria-valuenow={Math.round(pct)}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={label ?? 'Progress'}\n >\n <svg width={size} height={size} className=\"-rotate-90\">\n <circle\n cx={size / 2}\n cy={size / 2}\n r={r}\n fill=\"none\"\n stroke={c.border}\n strokeWidth={strokeWidth}\n />\n <motion.circle\n cx={size / 2}\n cy={size / 2}\n r={r}\n fill=\"none\"\n stroke={stroke}\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeDasharray={circ}\n initial={{ strokeDashoffset: reduce ? circ - dash : circ }}\n animate={{ strokeDashoffset: circ - dash }}\n transition={{ duration: reduce ? 0 : 0.25, ease: 'easeOut' }}\n />\n </svg>\n <div className=\"absolute inset-0 flex flex-col items-center justify-center\">\n <span className=\"text-lg font-semibold text-text-primary\">{Math.round(pct)}%</span>\n {label ? <span className=\"text-[10px] text-text-secondary\">{label}</span> : null}\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// TrendBars — diverging inflow/outflow cashflow bars\n// ============================================================================\n\nexport interface TrendDatum {\n label: string;\n inflow: number;\n outflow: number;\n}\n\nexport function TrendBars({\n data,\n height = 240,\n valueFormatter,\n}: {\n data: TrendDatum[];\n height?: number;\n valueFormatter?: (v: number) => string;\n}) {\n const c = useVizColors();\n const reduce = useReducedMotion();\n const rows = data.filter((d) => Number.isFinite(d.inflow) && Number.isFinite(d.outflow));\n if (rows.length === 0) {\n return <p className=\"text-sm text-text-secondary\">No cashflow data.</p>;\n }\n // Diverging: inflow up (positive), outflow down (negative).\n const chartData = rows.map((d) => ({\n label: d.label,\n inflow: Math.abs(d.inflow),\n outflow: -Math.abs(d.outflow),\n }));\n\n return (\n <div style={{ width: '100%', height }}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart\n data={chartData}\n margin={{ top: 8, right: 8, bottom: 0, left: 8 }}\n stackOffset=\"sign\"\n >\n <XAxis\n dataKey=\"label\"\n tick={{ fill: c.textSecondary, fontSize: 11 }}\n axisLine={{ stroke: c.grid }}\n tickLine={false}\n />\n <YAxis\n tick={{ fill: c.textSecondary, fontSize: 11 }}\n axisLine={false}\n tickLine={false}\n width={44}\n tickFormatter={(v: number) =>\n valueFormatter ? valueFormatter(Math.abs(v)) : String(Math.abs(v))\n }\n />\n <ReferenceLine y={0} stroke={c.grid} />\n <Tooltip\n cursor={{ fill: c.border, opacity: 0.25 }}\n content={\n <ChartTooltip\n valueFormatter={(v) =>\n valueFormatter ? valueFormatter(Math.abs(v)) : String(Math.abs(v))\n }\n />\n }\n />\n <Bar\n dataKey=\"inflow\"\n name=\"Inflow\"\n fill={c.positive}\n radius={[4, 4, 0, 0]}\n isAnimationActive={!reduce}\n animationDuration={250}\n />\n <Bar\n dataKey=\"outflow\"\n name=\"Outflow\"\n fill={c.negative}\n radius={[0, 0, 4, 4]}\n isAnimationActive={!reduce}\n animationDuration={250}\n />\n </BarChart>\n </ResponsiveContainer>\n </div>\n );\n}\n\n// ============================================================================\n// MiniAreaChart — projection with optional dashed target line\n// ============================================================================\n\nexport interface AreaDatum {\n x: string | number;\n y: number;\n}\n\nexport function MiniAreaChart({\n data,\n target,\n tone = 'brand',\n height = 200,\n valueFormatter,\n}: {\n data: AreaDatum[];\n target?: number;\n tone?: VizTone;\n height?: number;\n valueFormatter?: (v: number) => string;\n}) {\n const c = useVizColors();\n const reduce = useReducedMotion();\n const stroke = c.tone(tone);\n const rows = data.filter((d) => Number.isFinite(d.y));\n const gradientId = `area-${useId().replace(/:/g, '')}`;\n if (rows.length < 2) {\n return <p className=\"text-sm text-text-secondary\">Not enough data to chart.</p>;\n }\n return (\n <div style={{ width: '100%', height }}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={rows} margin={{ top: 8, right: 8, bottom: 0, left: 8 }}>\n <defs>\n <linearGradient id={gradientId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={stroke} stopOpacity={0.3} />\n <stop offset=\"100%\" stopColor={stroke} stopOpacity={0.02} />\n </linearGradient>\n </defs>\n <XAxis\n dataKey=\"x\"\n tick={{ fill: c.textSecondary, fontSize: 11 }}\n axisLine={{ stroke: c.grid }}\n tickLine={false}\n />\n <YAxis\n tick={{ fill: c.textSecondary, fontSize: 11 }}\n axisLine={false}\n tickLine={false}\n width={44}\n tickFormatter={(v: number) => (valueFormatter ? valueFormatter(v) : String(v))}\n />\n <Tooltip content={<ChartTooltip valueFormatter={valueFormatter} />} />\n {target !== undefined && Number.isFinite(target) ? (\n <ReferenceLine\n y={target}\n stroke={c.textSecondary}\n strokeDasharray=\"5 4\"\n label={{\n value: valueFormatter ? `Target ${valueFormatter(target)}` : `Target ${target}`,\n position: 'insideTopRight',\n fill: c.textSecondary,\n fontSize: 10,\n }}\n />\n ) : null}\n <Area\n type=\"monotone\"\n dataKey=\"y\"\n stroke={stroke}\n strokeWidth={2}\n fill={`url(#${gradientId})`}\n isAnimationActive={!reduce}\n animationDuration={250}\n dot={false}\n />\n </AreaChart>\n </ResponsiveContainer>\n </div>\n );\n}\n\n// ============================================================================\n// LifecycleStepper — horizontal proposal/rebalance lifecycle\n// ============================================================================\n\nconst NEGATIVE_TERMINAL = /reject|cancel|abandon|decline|fail/i;\n\nexport function LifecycleStepper({\n steps,\n current,\n className,\n}: {\n steps: string[];\n current: string;\n className?: string;\n}) {\n const isNegativeTerminal = NEGATIVE_TERMINAL.test(current);\n const currentIndex = steps.findIndex((s) => s.toLowerCase() === current.toLowerCase());\n\n return (\n <ol\n className={cn('flex w-full items-center overflow-x-auto', className)}\n aria-label=\"Lifecycle progress\"\n >\n {steps.map((step, i) => {\n const isLast = i === steps.length - 1;\n const done = currentIndex >= 0 && i < currentIndex;\n const active = currentIndex >= 0 && i === currentIndex && !isNegativeTerminal;\n const state = done ? 'done' : active ? 'active' : 'upcoming';\n return (\n <li\n key={step}\n className=\"flex min-w-fit flex-1 items-center\"\n aria-current={active ? 'step' : undefined}\n >\n <div className=\"flex flex-col items-center gap-1.5 px-1\">\n <span\n className={cn(\n 'flex size-7 items-center justify-center rounded-full border text-xs font-semibold transition-colors',\n state === 'done' &&\n 'border-transparent bg-[var(--color-action-primary-bg)] text-[var(--color-action-primary-text)]',\n state === 'active' &&\n 'border-[var(--color-action-primary-border)] bg-[var(--color-action-primary-bg)]/15 text-[var(--color-action-primary-text)]',\n state === 'upcoming' && 'border-border-subtle bg-bg-sunken text-text-secondary'\n )}\n aria-hidden=\"true\"\n >\n {done ? '✓' : i + 1}\n </span>\n <span\n className={cn(\n 'whitespace-nowrap text-[11px]',\n active ? 'font-medium text-text-primary' : 'text-text-secondary'\n )}\n >\n {step}\n </span>\n </div>\n {!isLast ? (\n <span\n aria-hidden=\"true\"\n className={cn(\n 'mx-1 h-0.5 flex-1 rounded-full',\n done ? 'bg-[var(--color-action-primary-bg)]' : 'bg-border-subtle'\n )}\n />\n ) : null}\n </li>\n );\n })}\n {isNegativeTerminal ? (\n <li className=\"ml-2 flex min-w-fit items-center gap-1.5\" aria-current=\"step\">\n <span className=\"mx-1 h-0.5 w-6 rounded-full bg-status-error-border\" aria-hidden=\"true\" />\n <div className=\"flex flex-col items-center gap-1.5 px-1\">\n <span\n className=\"flex size-7 items-center justify-center rounded-full border border-transparent bg-status-error text-xs font-semibold text-status-error-text\"\n aria-hidden=\"true\"\n >\n ✕\n </span>\n <span className=\"whitespace-nowrap text-[11px] font-medium text-status-error-text\">\n {current}\n </span>\n </div>\n </li>\n ) : null}\n </ol>\n );\n}\n\n// ============================================================================\n// StatTile — premium KPI tile\n// ============================================================================\n\nexport function StatTile({\n label,\n value,\n delta,\n deltaTone,\n deltaSuffix = '%',\n spark,\n sparkTone = 'brand',\n glow = false,\n icon,\n className,\n}: {\n label: string;\n value: string;\n /** Optional signed delta rendered as a chip (icon + sign + status colour). */\n delta?: number;\n /** Override the auto tone derived from the delta sign. */\n deltaTone?: 'positive' | 'negative' | 'neutral';\n deltaSuffix?: string;\n /** Optional inline sparkline series. */\n spark?: number[];\n sparkTone?: VizTone;\n /** Subtle emerald glow ring for hero tiles. */\n glow?: boolean;\n icon?: ReactNode;\n className?: string;\n}) {\n const positive = deltaTone ? deltaTone === 'positive' : (delta ?? 0) > 0;\n const negative = deltaTone ? deltaTone === 'negative' : (delta ?? 0) < 0;\n const deltaClass = positive\n ? 'text-status-success-text bg-status-success-bg'\n : negative\n ? 'text-status-error-text bg-status-error-bg'\n : 'text-text-secondary bg-bg-sunken';\n const sign = delta !== undefined && delta > 0 ? '+' : '';\n\n return (\n <div\n className={cn(\n 'group relative overflow-hidden rounded-xl border border-border-subtle bg-bg-surface p-4 transition-shadow',\n 'motion-safe:hover:shadow-[var(--shadow-pop-hover,var(--shadow-elevation-2))]',\n glow && 'ring-1 ring-[var(--color-action-primary-border)]/40',\n className\n )}\n >\n {glow ? (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute -right-8 -top-8 size-24 rounded-full bg-[var(--color-action-primary-bg)]/15 blur-2xl\"\n />\n ) : null}\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"text-xs text-text-secondary\">{label}</p>\n {icon ? <span className=\"text-text-secondary\">{icon}</span> : null}\n </div>\n <p className=\"mt-1 text-2xl font-semibold text-text-primary\">{value}</p>\n <div className=\"mt-2 flex items-center justify-between gap-2\">\n {delta !== undefined && Number.isFinite(delta) ? (\n <span\n className={cn(\n 'inline-flex items-center gap-0.5 rounded-full px-1.5 py-0.5 text-xs font-medium',\n deltaClass\n )}\n >\n <span aria-hidden=\"true\">{positive ? '▲' : negative ? '▼' : '—'}</span>\n {sign}\n {delta.toFixed(1)}\n {deltaSuffix}\n </span>\n ) : (\n <span />\n )}\n {spark && spark.length > 1 ? (\n <div className=\"w-20 shrink-0\">\n <Sparkline data={spark} tone={sparkTone} height={28} />\n </div>\n ) : null}\n </div>\n </div>\n );\n}\n\nexport { AnimatePresence };\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountsPage.js","names":[],"sources":["../../../src/brainyrich/pages/AccountsPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport {\n ArrowDownRight,\n ArrowUpRight,\n Building2,\n CheckCircle2,\n ChevronRight,\n Landmark,\n Layers,\n MoreHorizontal,\n Pencil,\n Plus,\n Receipt,\n Wallet,\n Archive as ArchiveIcon,\n} from 'lucide-react';\nimport {\n useAccount,\n useAllAccounts,\n usePagedAccounts,\n usePagedTransactions,\n useCreateAccount,\n useUpdateAccount,\n useArchiveAccount,\n useRecordTransaction,\n} from '../hooks/useBrainyRichApi';\nimport type { BraAccount, BraAccountType, BraTransactionType } from '../types/bra';\nimport { ActorName, ConfirmDialog, HouseholdPicker, LoadMore } from '../components';\nimport {\n DonutChart,\n FadeIn,\n GlassCard,\n StatTile,\n type DonutDatum,\n type VizTone,\n} from '../components/viz';\nimport { DocumentsPanel } from '../components/reuse/DocumentsPanel';\nimport {\n SectionShell,\n EmptyState,\n LoadingState,\n ErrorState,\n formatMoney,\n formatCompact,\n formatDate,\n statusBadgeVariant,\n} from './_shared';\n\n// Schema enum option lists (SDL enum members — not fabricated data).\nconst ACCOUNT_TYPES: BraAccountType[] = [\n 'BANK',\n 'BROKERAGE',\n 'RETIREMENT',\n 'CASH',\n 'CRYPTO',\n 'REAL_ESTATE',\n 'ALTERNATIVE',\n 'LIABILITY',\n];\n\nconst TRANSACTION_TYPES: BraTransactionType[] = [\n 'BUY',\n 'SELL',\n 'DIVIDEND',\n 'INTEREST',\n 'DEPOSIT',\n 'WITHDRAWAL',\n 'FEE',\n 'TRANSFER',\n];\n\n/** Stable, semantic tone per account type so the allocation donut reads consistently. */\nconst TYPE_TONE: Record<BraAccountType, VizTone> = {\n BANK: 'teal',\n BROKERAGE: 'brand',\n RETIREMENT: 'blue',\n CASH: 'success',\n CRYPTO: 'warning',\n REAL_ESTATE: 'purple',\n ALTERNATIVE: 'indigo',\n LIABILITY: 'error',\n};\n\n/** Positive = inflow (deposit/dividend/interest/sell/transfer in). */\nfunction isInflow(type: BraTransactionType): boolean {\n return (\n type === 'DEPOSIT' ||\n type === 'DIVIDEND' ||\n type === 'INTEREST' ||\n type === 'SELL' ||\n type === 'TRANSFER'\n );\n}\n\nfunction parseNum(value: string): number | undefined {\n if (value.trim() === '') return undefined;\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n}\n\ninterface AccountFormState {\n name: string;\n type: BraAccountType;\n institution: string;\n currency: string;\n cashBalance: string;\n accountMask: string;\n householdId: string | null;\n}\n\nconst EMPTY_ACCOUNT_FORM: AccountFormState = {\n name: '',\n type: 'BANK',\n institution: '',\n currency: 'USD',\n cashBalance: '',\n accountMask: '',\n householdId: null,\n};\n\ninterface TransactionFormState {\n type: BraTransactionType;\n tradeDate: string;\n symbol: string;\n quantity: string;\n price: string;\n amount: string;\n fee: string;\n notes: string;\n}\n\nconst EMPTY_TX_FORM: TransactionFormState = {\n type: 'DEPOSIT',\n tradeDate: '',\n symbol: '',\n quantity: '',\n price: '',\n amount: '',\n fee: '',\n notes: '',\n};\n\nexport function AccountsPage() {\n // Full set drives the KPI aggregates (totals must cover every account).\n const allAccounts = useAllAccounts();\n const accounts = allAccounts.data;\n // Paged connection drives the interactive table + LoadMore.\n const paged = usePagedAccounts();\n\n const [selectedId, setSelectedId] = useState<string | undefined>(undefined);\n\n const [createOpen, setCreateOpen] = useState(false);\n const [createForm, setCreateForm] = useState<AccountFormState>(EMPTY_ACCOUNT_FORM);\n\n const [editAccount, setEditAccount] = useState<BraAccount | null>(null);\n const [editForm, setEditForm] = useState<AccountFormState>(EMPTY_ACCOUNT_FORM);\n\n const [archiveTarget, setArchiveTarget] = useState<BraAccount | null>(null);\n\n const [txOpen, setTxOpen] = useState(false);\n const [txForm, setTxForm] = useState<TransactionFormState>(EMPTY_TX_FORM);\n\n const createAccount = useCreateAccount();\n const updateAccount = useUpdateAccount();\n const archiveAccount = useArchiveAccount();\n const recordTransaction = useRecordTransaction();\n\n const totalCash = accounts.reduce((sum, a) => sum + (a.cashBalance ?? 0), 0);\n const activeCount = accounts.filter((a) => a.status === 'ACTIVE').length;\n const institutionCount = new Set(\n accounts.map((a) => a.institution).filter((i): i is string => Boolean(i))\n ).size;\n const displayCurrency = accounts[0]?.currency ?? 'USD';\n\n // Cash-by-account-type allocation — aggregated straight from the real accounts.\n const cashByType = useMemo<DonutDatum[]>(() => {\n const totals = new Map<BraAccountType, number>();\n for (const a of accounts) {\n const cash = a.cashBalance ?? 0;\n if (cash <= 0) continue;\n totals.set(a.type, (totals.get(a.type) ?? 0) + cash);\n }\n return [...totals.entries()]\n .map(([type, value]) => ({\n label: type.replace(/_/g, ' '),\n value,\n tone: TYPE_TONE[type],\n }))\n .sort((a, b) => b.value - a.value);\n }, [accounts]);\n\n function openCreate() {\n setCreateForm(EMPTY_ACCOUNT_FORM);\n setCreateOpen(true);\n }\n\n function submitCreate() {\n if (!createForm.name.trim()) return;\n createAccount.mutate(\n {\n name: createForm.name.trim(),\n type: createForm.type,\n institution: createForm.institution.trim() || null,\n currency: createForm.currency.trim() || 'USD',\n cashBalance: parseNum(createForm.cashBalance) ?? 0,\n accountMask: createForm.accountMask.trim() || null,\n householdId: createForm.householdId,\n },\n { onSuccess: () => setCreateOpen(false) }\n );\n }\n\n function openEdit(account: BraAccount) {\n setEditAccount(account);\n setEditForm({\n name: account.name,\n type: account.type,\n institution: account.institution ?? '',\n currency: account.currency,\n cashBalance: String(account.cashBalance ?? ''),\n accountMask: account.accountMask ?? '',\n householdId: account.householdId,\n });\n }\n\n function submitEdit() {\n if (!editAccount || !editForm.name.trim()) return;\n updateAccount.mutate(\n {\n id: editAccount.id,\n input: {\n name: editForm.name.trim(),\n type: editForm.type,\n institution: editForm.institution.trim() || null,\n currency: editForm.currency.trim() || 'USD',\n cashBalance: parseNum(editForm.cashBalance) ?? 0,\n accountMask: editForm.accountMask.trim() || null,\n householdId: editForm.householdId,\n },\n },\n { onSuccess: () => setEditAccount(null) }\n );\n }\n\n function submitArchive() {\n if (!archiveTarget) return;\n const id = archiveTarget.id;\n archiveAccount.mutate(id, {\n onSuccess: () => {\n setArchiveTarget(null);\n if (selectedId === id) setSelectedId(undefined);\n },\n });\n }\n\n function openRecordTx() {\n setTxForm(EMPTY_TX_FORM);\n setTxOpen(true);\n }\n\n const txAmount = parseNum(txForm.amount);\n const txAmountValid = txAmount !== undefined && txAmount > 0;\n const txValid = Boolean(selectedId && txForm.tradeDate) && txAmountValid;\n\n function submitTx() {\n if (!selectedId || !txForm.tradeDate || !txAmountValid) return;\n const tradeIso = new Date(txForm.tradeDate);\n if (Number.isNaN(tradeIso.getTime())) return;\n recordTransaction.mutate(\n {\n accountId: selectedId,\n type: txForm.type,\n tradeDate: tradeIso.toISOString(),\n symbol: txForm.symbol.trim() || null,\n quantity: parseNum(txForm.quantity),\n price: parseNum(txForm.price),\n amount: txAmount,\n fee: parseNum(txForm.fee),\n notes: txForm.notes.trim() || null,\n },\n { onSuccess: () => setTxOpen(false) }\n );\n }\n\n const moneyFmt = (v: number) => formatMoney(v, displayCurrency);\n\n return (\n <SectionShell\n title=\"Accounts\"\n description=\"Linked bank, brokerage and retirement accounts with recent activity.\"\n badges={[`${accounts.length} account${accounts.length === 1 ? '' : 's'}`]}\n actionLabel=\"New account\"\n onAction={openCreate}\n >\n {/* Hero — premium KPI tiles + live cash allocation donut */}\n <div className=\"grid gap-4 lg:grid-cols-3\">\n <div className=\"grid gap-4 sm:grid-cols-2 lg:col-span-2\">\n <FadeIn index={0}>\n <StatTile\n label=\"Total cash\"\n value={formatMoney(totalCash, displayCurrency)}\n glow\n icon={<Wallet className=\"size-4\" aria-hidden=\"true\" />}\n />\n </FadeIn>\n <FadeIn index={1}>\n <StatTile\n label=\"Total accounts\"\n value={String(accounts.length)}\n icon={<Landmark className=\"size-4\" aria-hidden=\"true\" />}\n />\n </FadeIn>\n <FadeIn index={2}>\n <StatTile\n label=\"Active\"\n value={`${activeCount}/${accounts.length}`}\n icon={<CheckCircle2 className=\"size-4\" aria-hidden=\"true\" />}\n />\n </FadeIn>\n <FadeIn index={3}>\n <StatTile\n label=\"Institutions\"\n value={String(institutionCount)}\n icon={<Building2 className=\"size-4\" aria-hidden=\"true\" />}\n />\n </FadeIn>\n </div>\n\n <FadeIn index={4}>\n <GlassCard className=\"h-full\" glow>\n <div className=\"mb-2 flex items-center gap-2\">\n <Layers className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n <h2 className=\"text-sm font-semibold text-text-primary\">Cash by account type</h2>\n </div>\n {cashByType.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n No cash balances recorded yet.\n </p>\n ) : (\n <DonutChart\n data={cashByType}\n centerValue={formatCompact(totalCash, displayCurrency)}\n centerLabel=\"Total cash\"\n height={200}\n valueFormatter={moneyFmt}\n />\n )}\n </GlassCard>\n </FadeIn>\n </div>\n\n <FadeIn index={5} as=\"section\">\n <Card className=\"transition-shadow motion-safe:hover:shadow-[var(--shadow-pop-hover,var(--shadow-elevation-2))]\">\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Landmark className=\"h-5 w-5\" aria-hidden=\"true\" />\n Linked accounts\n </CardTitle>\n <CardDescription>\n Select an account to view holdings and transactions.\n </CardDescription>\n </CardHeader>\n <CardContent>\n {paged.isLoading ? (\n <LoadingState />\n ) : paged.isError ? (\n <ErrorState onRetry={() => paged.refetch()} />\n ) : paged.items.length === 0 ? (\n <EmptyState\n icon={<Landmark className=\"size-6\" aria-hidden=\"true\" />}\n title=\"No accounts yet\"\n description=\"Add your first bank, brokerage or retirement account to start tracking net worth.\"\n action={\n <Button size=\"sm\" onClick={openCreate}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n New account\n </Button>\n }\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Account</TableHead>\n <TableHead>Type</TableHead>\n <TableHead>Institution</TableHead>\n <TableHead className=\"text-right\">Cash balance</TableHead>\n <TableHead>Status</TableHead>\n <TableHead className=\"w-12 text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {paged.items.map((account) => (\n <TableRow\n key={account.id}\n className=\"group cursor-pointer transition-colors hover:bg-bg-elevated\"\n onClick={() => setSelectedId(account.id)}\n >\n <TableCell>\n <div className=\"flex items-center gap-3\">\n <div\n className=\"flex h-9 w-9 items-center justify-center rounded-lg border border-border-subtle bg-bg-sunken text-xs font-semibold text-text-secondary transition-colors group-hover:border-[var(--color-action-primary-border)] group-hover:text-text-primary\"\n aria-hidden=\"true\"\n >\n {(account.institution ?? account.name).slice(0, 2).toUpperCase()}\n </div>\n <div className=\"min-w-0\">\n <p className=\"flex items-center gap-1 truncate font-medium text-text-primary\">\n {account.name}\n <ChevronRight\n className=\"size-3.5 shrink-0 text-text-secondary opacity-0 transition-opacity group-hover:opacity-100\"\n aria-hidden=\"true\"\n />\n </p>\n <p className=\"truncate text-xs text-text-secondary\">\n {account.institution ?? 'Manual account'}\n {account.accountMask ? ` · ••${account.accountMask}` : ''}\n </p>\n </div>\n </div>\n </TableCell>\n <TableCell>\n <Badge variant=\"outline\">{account.type.replace(/_/g, ' ')}</Badge>\n </TableCell>\n <TableCell className=\"text-sm text-text-secondary\">\n {account.institution ?? '—'}\n </TableCell>\n <TableCell className=\"text-right font-semibold tabular-nums text-text-primary\">\n {formatMoney(account.cashBalance, account.currency)}\n </TableCell>\n <TableCell>\n <Badge variant={statusBadgeVariant(account.status)}>\n {account.status}\n </Badge>\n </TableCell>\n <TableCell\n className=\"text-right\"\n onClick={(e) => e.stopPropagation()}\n >\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label={`Actions for ${account.name}`}\n >\n <MoreHorizontal className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onClick={() => openEdit(account)}>\n <Pencil className=\"size-4\" aria-hidden=\"true\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuItem\n className=\"text-status-error-text\"\n onClick={() => setArchiveTarget(account)}\n >\n <ArchiveIcon className=\"size-4\" aria-hidden=\"true\" />\n Archive\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n <LoadMore\n hasMore={paged.hasMore}\n onClick={paged.loadMore}\n loading={paged.isFetchingNextPage}\n className=\"mt-4\"\n />\n </div>\n )}\n </CardContent>\n </Card>\n </FadeIn>\n\n {/* Create account dialog */}\n <Dialog open={createOpen} onOpenChange={setCreateOpen}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>New account</DialogTitle>\n <DialogDescription>Add a bank, brokerage or retirement account.</DialogDescription>\n </DialogHeader>\n <AccountForm form={createForm} onChange={setCreateForm} />\n <DialogFooter>\n <DialogClose asChild>\n <Button variant=\"outline\">Cancel</Button>\n </DialogClose>\n <Button\n onClick={submitCreate}\n disabled={!createForm.name.trim() || createAccount.isPending}\n >\n {createAccount.isPending ? 'Creating…' : 'Create account'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* Edit account dialog */}\n <Dialog open={Boolean(editAccount)} onOpenChange={(open) => !open && setEditAccount(null)}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>Edit account</DialogTitle>\n <DialogDescription>Update account details.</DialogDescription>\n </DialogHeader>\n <AccountForm form={editForm} onChange={setEditForm} />\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setEditAccount(null)}>\n Cancel\n </Button>\n <Button\n onClick={submitEdit}\n disabled={!editForm.name.trim() || updateAccount.isPending}\n >\n {updateAccount.isPending ? 'Saving…' : 'Save changes'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* Archive confirm dialog */}\n <ConfirmDialog\n open={Boolean(archiveTarget)}\n onOpenChange={(open) => !open && setArchiveTarget(null)}\n title=\"Archive account?\"\n description={\n archiveTarget\n ? `“${archiveTarget.name}” will be archived and hidden from active lists. This does not delete its history.`\n : undefined\n }\n confirmLabel=\"Archive account\"\n pendingLabel=\"Archiving…\"\n destructive\n pending={archiveAccount.isPending}\n onConfirm={submitArchive}\n />\n\n {/* Account detail sheet */}\n <AccountDetailSheet\n accountId={selectedId}\n onClose={() => setSelectedId(undefined)}\n onRecordTransaction={openRecordTx}\n />\n\n {/* Record transaction dialog */}\n <Dialog open={txOpen} onOpenChange={setTxOpen}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>Record transaction</DialogTitle>\n <DialogDescription>Log activity against the selected account.</DialogDescription>\n </DialogHeader>\n <div className=\"grid gap-4\">\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-type\">Type</Label>\n <Select\n value={txForm.type}\n onValueChange={(v) => setTxForm((f) => ({ ...f, type: v as BraTransactionType }))}\n >\n <SelectTrigger id=\"tx-type\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {TRANSACTION_TYPES.map((t) => (\n <SelectItem key={t} value={t}>\n {t.replace(/_/g, ' ')}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-date\">Trade date</Label>\n <Input\n id=\"tx-date\"\n type=\"date\"\n value={txForm.tradeDate}\n onChange={(e) => setTxForm((f) => ({ ...f, tradeDate: e.target.value }))}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-symbol\">Symbol</Label>\n <Input\n id=\"tx-symbol\"\n value={txForm.symbol}\n onChange={(e) => setTxForm((f) => ({ ...f, symbol: e.target.value }))}\n placeholder=\"e.g. VOO\"\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-amount\">Amount</Label>\n <Input\n id=\"tx-amount\"\n type=\"number\"\n min=\"0\"\n step=\"any\"\n value={txForm.amount}\n onChange={(e) => setTxForm((f) => ({ ...f, amount: e.target.value }))}\n aria-invalid={txForm.amount.trim() !== '' && !txAmountValid}\n aria-describedby=\"tx-amount-hint\"\n />\n <p id=\"tx-amount-hint\" className=\"text-xs text-text-secondary\">\n {txForm.amount.trim() !== '' && !txAmountValid\n ? 'Enter an amount greater than 0.'\n : 'Positive amount; direction is set by the type.'}\n </p>\n </div>\n </div>\n <div className=\"grid grid-cols-3 gap-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-qty\">Quantity</Label>\n <Input\n id=\"tx-qty\"\n type=\"number\"\n value={txForm.quantity}\n onChange={(e) => setTxForm((f) => ({ ...f, quantity: e.target.value }))}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-price\">Price</Label>\n <Input\n id=\"tx-price\"\n type=\"number\"\n value={txForm.price}\n onChange={(e) => setTxForm((f) => ({ ...f, price: e.target.value }))}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-fee\">Fee</Label>\n <Input\n id=\"tx-fee\"\n type=\"number\"\n value={txForm.fee}\n onChange={(e) => setTxForm((f) => ({ ...f, fee: e.target.value }))}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-notes\">Notes</Label>\n <Textarea\n id=\"tx-notes\"\n value={txForm.notes}\n onChange={(e) => setTxForm((f) => ({ ...f, notes: e.target.value }))}\n placeholder=\"Optional note\"\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setTxOpen(false)}>\n Cancel\n </Button>\n <Button onClick={submitTx} disabled={!txValid || recordTransaction.isPending}>\n {recordTransaction.isPending ? 'Saving…' : 'Record transaction'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </SectionShell>\n );\n}\n\n// ============================================================================\n// Account form (shared by create + edit dialogs)\n// ============================================================================\n\nfunction AccountForm({\n form,\n onChange,\n}: {\n form: AccountFormState;\n onChange: (updater: (prev: AccountFormState) => AccountFormState) => void;\n}) {\n return (\n <div className=\"grid gap-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"acct-name\">Name</Label>\n <Input\n id=\"acct-name\"\n value={form.name}\n onChange={(e) => onChange((f) => ({ ...f, name: e.target.value }))}\n placeholder=\"e.g. Everyday Checking\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"acct-type\">Type</Label>\n <Select\n value={form.type}\n onValueChange={(v) => onChange((f) => ({ ...f, type: v as BraAccountType }))}\n >\n <SelectTrigger id=\"acct-type\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ACCOUNT_TYPES.map((t) => (\n <SelectItem key={t} value={t}>\n {t.replace(/_/g, ' ')}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"acct-currency\">Currency</Label>\n <Input\n id=\"acct-currency\"\n value={form.currency}\n onChange={(e) => onChange((f) => ({ ...f, currency: e.target.value.toUpperCase() }))}\n placeholder=\"USD\"\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"acct-institution\">Institution</Label>\n <Input\n id=\"acct-institution\"\n value={form.institution}\n onChange={(e) => onChange((f) => ({ ...f, institution: e.target.value }))}\n placeholder=\"e.g. Chase\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"acct-balance\">Cash balance</Label>\n <Input\n id=\"acct-balance\"\n type=\"number\"\n value={form.cashBalance}\n onChange={(e) => onChange((f) => ({ ...f, cashBalance: e.target.value }))}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"acct-mask\">Account mask</Label>\n <Input\n id=\"acct-mask\"\n value={form.accountMask}\n onChange={(e) => onChange((f) => ({ ...f, accountMask: e.target.value }))}\n placeholder=\"Last 4 digits\"\n />\n </div>\n </div>\n <HouseholdPicker\n value={form.householdId}\n onChange={(householdId) => onChange((f) => ({ ...f, householdId }))}\n />\n </div>\n );\n}\n\n// ============================================================================\n// Account detail sheet — holdings + recent transactions\n// ============================================================================\n\nfunction AccountDetailSheet({\n accountId,\n onClose,\n onRecordTransaction,\n}: {\n accountId: string | undefined;\n onClose: () => void;\n onRecordTransaction: () => void;\n}) {\n const { data: account, isLoading, isError, refetch } = useAccount(accountId);\n const txPaged = usePagedTransactions(accountId ? { accountId } : {});\n const transactions = accountId ? txPaged.items : [];\n\n // Holdings allocation by market value — real per-holding figures.\n const holdingsAlloc = useMemo<DonutDatum[]>(() => {\n if (!account) return [];\n return account.holdings\n .filter((h) => Number.isFinite(h.marketValue) && h.marketValue > 0)\n .map((h) => ({ label: h.symbol, value: h.marketValue }))\n .sort((a, b) => b.value - a.value);\n }, [account]);\n\n const holdingsTotal = holdingsAlloc.reduce((s, h) => s + h.value, 0);\n\n return (\n <Sheet open={Boolean(accountId)} onOpenChange={(open) => !open && onClose()}>\n <SheetContent className=\"flex w-full flex-col overflow-y-auto p-0 sm:max-w-xl\">\n <SheetHeader className=\"border-b border-border-subtle px-6 pb-4 pt-6\">\n <SheetTitle className=\"text-lg\">{account?.name ?? 'Account'}</SheetTitle>\n <SheetDescription>\n {account\n ? `${account.institution ?? 'Manual account'} · ${account.type.replace(/_/g, ' ')}`\n : 'Loading account details…'}\n </SheetDescription>\n </SheetHeader>\n\n {isLoading ? (\n <div className=\"p-6\">\n <LoadingState rows={3} />\n </div>\n ) : isError ? (\n <div className=\"p-6\">\n <ErrorState onRetry={() => refetch()} />\n </div>\n ) : !account ? (\n <div className=\"p-6\">\n <EmptyState\n title=\"Account not found\"\n description=\"This account is no longer available.\"\n />\n </div>\n ) : (\n <div className=\"space-y-6 p-6\">\n {/* Hero — cash balance with emerald glow + status + owner */}\n <GlassCard glow className=\"space-y-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">\n Cash balance\n </p>\n <p className=\"mt-1 text-3xl font-semibold tracking-tight text-text-primary\">\n {formatMoney(account.cashBalance, account.currency)}\n </p>\n </div>\n <Badge variant={statusBadgeVariant(account.status)}>{account.status}</Badge>\n </div>\n <div className=\"flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border-subtle pt-3 text-xs\">\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-text-secondary\">Owner</span>\n <ActorName\n id={account.ownerId}\n className=\"font-medium text-text-primary\"\n />\n </div>\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-text-secondary\">Holdings value</span>\n <span className=\"font-medium text-text-primary\">\n {holdingsTotal > 0 ? formatMoney(holdingsTotal, account.currency) : '—'}\n </span>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-text-secondary\">Positions</span>\n <span className=\"font-medium text-text-primary\">{account.holdings.length}</span>\n </div>\n </div>\n </GlassCard>\n\n {/* Allocation donut — market value split across positions */}\n {holdingsAlloc.length > 0 ? (\n <div className=\"rounded-xl border border-border-subtle bg-bg-surface p-4\">\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">\n Holdings allocation\n </h3>\n <DonutChart\n data={holdingsAlloc}\n centerValue={formatCompact(holdingsTotal, account.currency)}\n centerLabel=\"Market value\"\n height={180}\n valueFormatter={(v) => formatMoney(v, account.currency)}\n />\n </div>\n ) : null}\n\n {/* Tabbed detail: Holdings / Transactions / Documents */}\n <Tabs defaultValue=\"holdings\" className=\"w-full\">\n <TabsList className=\"w-full\">\n <TabsTrigger value=\"holdings\" className=\"flex-1\">\n Holdings\n </TabsTrigger>\n <TabsTrigger value=\"transactions\" className=\"flex-1\">\n Transactions\n </TabsTrigger>\n <TabsTrigger value=\"documents\" className=\"flex-1\">\n Documents\n </TabsTrigger>\n </TabsList>\n\n {/* Holdings */}\n <TabsContent value=\"holdings\" className=\"mt-4\">\n {account.holdings.length === 0 ? (\n <EmptyState\n icon={<Layers className=\"size-6\" aria-hidden=\"true\" />}\n title=\"No holdings\"\n description=\"This account has no positions recorded yet.\"\n />\n ) : (\n <div className=\"overflow-x-auto rounded-lg border border-border-subtle\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Holding</TableHead>\n <TableHead className=\"text-right\">Qty</TableHead>\n <TableHead className=\"text-right\">Price</TableHead>\n <TableHead className=\"text-right\">Market value</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {account.holdings.map((h) => (\n <TableRow\n key={h.id}\n className=\"transition-colors hover:bg-bg-elevated\"\n >\n <TableCell>\n <p className=\"font-medium text-text-primary\">{h.symbol}</p>\n {h.name ? (\n <p className=\"truncate text-xs text-text-secondary\">{h.name}</p>\n ) : null}\n </TableCell>\n <TableCell className=\"text-right tabular-nums text-text-secondary\">\n {h.quantity}\n </TableCell>\n <TableCell className=\"text-right tabular-nums text-text-secondary\">\n {formatMoney(h.lastPrice, h.currency)}\n </TableCell>\n <TableCell className=\"text-right font-semibold tabular-nums text-text-primary\">\n {formatMoney(h.marketValue, h.currency)}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n )}\n </TabsContent>\n\n {/* Transactions */}\n <TabsContent value=\"transactions\" className=\"mt-4 space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Recent activity</h3>\n <Button size=\"sm\" onClick={onRecordTransaction}>\n <Receipt className=\"size-4\" aria-hidden=\"true\" />\n Record\n </Button>\n </div>\n {txPaged.isLoading ? (\n <LoadingState rows={2} />\n ) : txPaged.isError ? (\n <ErrorState onRetry={() => txPaged.refetch()} />\n ) : transactions.length === 0 ? (\n <EmptyState\n icon={<Receipt className=\"size-6\" aria-hidden=\"true\" />}\n title=\"No transactions\"\n description=\"Record a transaction to see activity here.\"\n action={\n <Button size=\"sm\" onClick={onRecordTransaction}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n Record transaction\n </Button>\n }\n />\n ) : (\n <div className=\"space-y-2\">\n {transactions.map((tx) => {\n const inflow = isInflow(tx.type);\n const Icon = inflow ? ArrowUpRight : ArrowDownRight;\n const tone = inflow ? 'text-status-success-text' : 'text-status-error-text';\n const iconBg = inflow ? 'bg-status-success-bg' : 'bg-status-error-bg';\n return (\n <div\n key={tx.id}\n className=\"flex items-center justify-between gap-3 rounded-lg border border-border-subtle bg-bg-surface p-3 transition-colors hover:bg-bg-elevated\"\n >\n <div className=\"flex min-w-0 items-center gap-3\">\n <span\n className={`flex size-8 shrink-0 items-center justify-center rounded-full ${iconBg} ${tone}`}\n aria-hidden=\"true\"\n >\n <Icon className=\"size-4\" />\n </span>\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"truncate text-sm font-medium text-text-primary\">\n {tx.type.replace(/_/g, ' ')}\n </span>\n {tx.symbol ? (\n <Badge variant=\"outline\" className=\"shrink-0\">\n {tx.symbol}\n </Badge>\n ) : null}\n </div>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {formatDate(tx.tradeDate)}\n </p>\n </div>\n </div>\n <span\n className={`shrink-0 text-sm font-semibold tabular-nums ${tone}`}\n >\n {inflow ? '+' : '−'}\n {formatMoney(Math.abs(tx.amount), tx.currency)}\n </span>\n </div>\n );\n })}\n <LoadMore\n hasMore={txPaged.hasMore}\n onClick={txPaged.loadMore}\n loading={txPaged.isFetchingNextPage}\n />\n </div>\n )}\n </TabsContent>\n\n {/* Documents — statements, KYC, tax packs (wspace-files reuse) */}\n <TabsContent value=\"documents\" className=\"mt-4\">\n <DocumentsPanel entityType=\"brainyrich-account\" entityId={account.id} />\n </TabsContent>\n </Tabs>\n </div>\n )}\n </SheetContent>\n </Sheet>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AccountsPage.js","names":[],"sources":["../../../src/brainyrich/pages/AccountsPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport {\n ArrowDownRight,\n ArrowUpRight,\n Building2,\n CheckCircle2,\n ChevronRight,\n Landmark,\n Layers,\n MoreHorizontal,\n Pencil,\n Plus,\n Receipt,\n Wallet,\n Archive as ArchiveIcon,\n} from 'lucide-react';\nimport {\n useAccount,\n useAllAccounts,\n usePagedAccounts,\n usePagedTransactions,\n useCreateAccount,\n useUpdateAccount,\n useArchiveAccount,\n useRecordTransaction,\n} from '../hooks/useBrainyRichApi';\nimport type { BraAccount, BraAccountType, BraTransactionType } from '../types/bra';\nimport { ActorName, ConfirmDialog, HouseholdPicker, LoadMore } from '../components';\nimport {\n DonutChart,\n FadeIn,\n GlassCard,\n StatTile,\n type DonutDatum,\n type VizTone,\n} from '../components/viz';\nimport { DocumentsPanel } from '../components/reuse/DocumentsPanel';\nimport {\n SectionShell,\n EmptyState,\n LoadingState,\n ErrorState,\n formatMoney,\n formatCompact,\n formatDate,\n statusBadgeVariant,\n} from './_shared';\n\n// Schema enum option lists (SDL enum members — not fabricated data).\nconst ACCOUNT_TYPES: BraAccountType[] = [\n 'BANK',\n 'BROKERAGE',\n 'RETIREMENT',\n 'CASH',\n 'CRYPTO',\n 'REAL_ESTATE',\n 'ALTERNATIVE',\n 'LIABILITY',\n];\n\nconst TRANSACTION_TYPES: BraTransactionType[] = [\n 'BUY',\n 'SELL',\n 'DIVIDEND',\n 'INTEREST',\n 'DEPOSIT',\n 'WITHDRAWAL',\n 'FEE',\n 'TRANSFER',\n];\n\n/** Stable, semantic tone per account type so the allocation donut reads consistently. */\nconst TYPE_TONE: Record<BraAccountType, VizTone> = {\n BANK: 'teal',\n BROKERAGE: 'brand',\n RETIREMENT: 'blue',\n CASH: 'success',\n CRYPTO: 'warning',\n REAL_ESTATE: 'purple',\n ALTERNATIVE: 'indigo',\n LIABILITY: 'error',\n};\n\n/** Positive = inflow (deposit/dividend/interest/sell/transfer in). */\nfunction isInflow(type: BraTransactionType): boolean {\n return (\n type === 'DEPOSIT' ||\n type === 'DIVIDEND' ||\n type === 'INTEREST' ||\n type === 'SELL' ||\n type === 'TRANSFER'\n );\n}\n\nfunction parseNum(value: string): number | undefined {\n if (value.trim() === '') return undefined;\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n}\n\ninterface AccountFormState {\n name: string;\n type: BraAccountType;\n institution: string;\n currency: string;\n cashBalance: string;\n accountMask: string;\n householdId: string | null;\n}\n\nconst EMPTY_ACCOUNT_FORM: AccountFormState = {\n name: '',\n type: 'BANK',\n institution: '',\n currency: 'USD',\n cashBalance: '',\n accountMask: '',\n householdId: null,\n};\n\ninterface TransactionFormState {\n type: BraTransactionType;\n tradeDate: string;\n symbol: string;\n quantity: string;\n price: string;\n amount: string;\n fee: string;\n notes: string;\n}\n\nconst EMPTY_TX_FORM: TransactionFormState = {\n type: 'DEPOSIT',\n tradeDate: '',\n symbol: '',\n quantity: '',\n price: '',\n amount: '',\n fee: '',\n notes: '',\n};\n\nexport function AccountsPage() {\n // Full set drives the KPI aggregates (totals must cover every account).\n const allAccounts = useAllAccounts();\n const accounts = allAccounts.data;\n // Paged connection drives the interactive table + LoadMore.\n const paged = usePagedAccounts();\n\n const [selectedId, setSelectedId] = useState<string | undefined>(undefined);\n\n const [createOpen, setCreateOpen] = useState(false);\n const [createForm, setCreateForm] = useState<AccountFormState>(EMPTY_ACCOUNT_FORM);\n\n const [editAccount, setEditAccount] = useState<BraAccount | null>(null);\n const [editForm, setEditForm] = useState<AccountFormState>(EMPTY_ACCOUNT_FORM);\n\n const [archiveTarget, setArchiveTarget] = useState<BraAccount | null>(null);\n\n const [txOpen, setTxOpen] = useState(false);\n const [txForm, setTxForm] = useState<TransactionFormState>(EMPTY_TX_FORM);\n\n const createAccount = useCreateAccount();\n const updateAccount = useUpdateAccount();\n const archiveAccount = useArchiveAccount();\n const recordTransaction = useRecordTransaction();\n\n const totalCash = accounts.reduce((sum, a) => sum + (a.cashBalance ?? 0), 0);\n const activeCount = accounts.filter((a) => a.status === 'ACTIVE').length;\n const institutionCount = new Set(\n accounts.map((a) => a.institution).filter((i): i is string => Boolean(i))\n ).size;\n const displayCurrency = accounts[0]?.currency ?? 'USD';\n\n // Cash-by-account-type allocation — aggregated straight from the real accounts.\n const cashByType = useMemo<DonutDatum[]>(() => {\n const totals = new Map<BraAccountType, number>();\n for (const a of accounts) {\n const cash = a.cashBalance ?? 0;\n if (cash <= 0) continue;\n totals.set(a.type, (totals.get(a.type) ?? 0) + cash);\n }\n return [...totals.entries()]\n .map(([type, value]) => ({\n label: type.replace(/_/g, ' '),\n value,\n tone: TYPE_TONE[type],\n }))\n .sort((a, b) => b.value - a.value);\n }, [accounts]);\n\n function openCreate() {\n setCreateForm(EMPTY_ACCOUNT_FORM);\n setCreateOpen(true);\n }\n\n function submitCreate() {\n if (!createForm.name.trim()) return;\n createAccount.mutate(\n {\n name: createForm.name.trim(),\n type: createForm.type,\n institution: createForm.institution.trim() || null,\n currency: createForm.currency.trim() || 'USD',\n cashBalance: parseNum(createForm.cashBalance) ?? 0,\n accountMask: createForm.accountMask.trim() || null,\n householdId: createForm.householdId,\n },\n { onSuccess: () => setCreateOpen(false) }\n );\n }\n\n function openEdit(account: BraAccount) {\n setEditAccount(account);\n setEditForm({\n name: account.name,\n type: account.type,\n institution: account.institution ?? '',\n currency: account.currency,\n cashBalance: String(account.cashBalance ?? ''),\n accountMask: account.accountMask ?? '',\n householdId: account.householdId,\n });\n }\n\n function submitEdit() {\n if (!editAccount || !editForm.name.trim()) return;\n updateAccount.mutate(\n {\n id: editAccount.id,\n input: {\n name: editForm.name.trim(),\n type: editForm.type,\n institution: editForm.institution.trim() || null,\n currency: editForm.currency.trim() || 'USD',\n cashBalance: parseNum(editForm.cashBalance) ?? 0,\n accountMask: editForm.accountMask.trim() || null,\n householdId: editForm.householdId,\n },\n },\n { onSuccess: () => setEditAccount(null) }\n );\n }\n\n function submitArchive() {\n if (!archiveTarget) return;\n const id = archiveTarget.id;\n archiveAccount.mutate(id, {\n onSuccess: () => {\n setArchiveTarget(null);\n if (selectedId === id) setSelectedId(undefined);\n },\n });\n }\n\n function openRecordTx() {\n setTxForm(EMPTY_TX_FORM);\n setTxOpen(true);\n }\n\n const txAmount = parseNum(txForm.amount);\n const txAmountValid = txAmount !== undefined && txAmount > 0;\n const txValid = Boolean(selectedId && txForm.tradeDate) && txAmountValid;\n\n function submitTx() {\n if (!selectedId || !txForm.tradeDate || !txAmountValid) return;\n const tradeIso = new Date(txForm.tradeDate);\n if (Number.isNaN(tradeIso.getTime())) return;\n recordTransaction.mutate(\n {\n accountId: selectedId,\n type: txForm.type,\n tradeDate: tradeIso.toISOString(),\n symbol: txForm.symbol.trim() || null,\n quantity: parseNum(txForm.quantity),\n price: parseNum(txForm.price),\n amount: txAmount,\n fee: parseNum(txForm.fee),\n notes: txForm.notes.trim() || null,\n },\n { onSuccess: () => setTxOpen(false) }\n );\n }\n\n const moneyFmt = (v: number) => formatMoney(v, displayCurrency);\n\n return (\n <SectionShell\n title=\"Accounts\"\n description=\"Linked bank, brokerage and retirement accounts with recent activity.\"\n badges={[`${accounts.length} account${accounts.length === 1 ? '' : 's'}`]}\n actionLabel=\"New account\"\n onAction={openCreate}\n >\n {/* Hero — premium KPI tiles + live cash allocation donut */}\n <div className=\"grid gap-4 lg:grid-cols-3\">\n <div className=\"grid gap-4 sm:grid-cols-2 lg:col-span-2\">\n <FadeIn index={0}>\n <StatTile\n label=\"Total cash\"\n value={formatMoney(totalCash, displayCurrency)}\n glow\n icon={<Wallet className=\"size-4\" aria-hidden=\"true\" />}\n />\n </FadeIn>\n <FadeIn index={1}>\n <StatTile\n label=\"Total accounts\"\n value={String(accounts.length)}\n icon={<Landmark className=\"size-4\" aria-hidden=\"true\" />}\n />\n </FadeIn>\n <FadeIn index={2}>\n <StatTile\n label=\"Active\"\n value={`${activeCount}/${accounts.length}`}\n icon={<CheckCircle2 className=\"size-4\" aria-hidden=\"true\" />}\n />\n </FadeIn>\n <FadeIn index={3}>\n <StatTile\n label=\"Institutions\"\n value={String(institutionCount)}\n icon={<Building2 className=\"size-4\" aria-hidden=\"true\" />}\n />\n </FadeIn>\n </div>\n\n <FadeIn index={4}>\n <GlassCard className=\"h-full\" glow>\n <div className=\"mb-2 flex items-center gap-2\">\n <Layers className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n <h2 className=\"text-sm font-semibold text-text-primary\">Cash by account type</h2>\n </div>\n {cashByType.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n No cash balances recorded yet.\n </p>\n ) : (\n <DonutChart\n data={cashByType}\n centerValue={formatCompact(totalCash, displayCurrency)}\n centerLabel=\"Total cash\"\n height={200}\n valueFormatter={moneyFmt}\n />\n )}\n </GlassCard>\n </FadeIn>\n </div>\n\n <FadeIn index={5} as=\"section\">\n <Card className=\"transition-shadow motion-safe:hover:shadow-[var(--shadow-pop-hover,var(--shadow-elevation-2))]\">\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Landmark className=\"h-5 w-5\" aria-hidden=\"true\" />\n Linked accounts\n </CardTitle>\n <CardDescription>Select an account to view holdings and transactions.</CardDescription>\n </CardHeader>\n <CardContent>\n {paged.isLoading ? (\n <LoadingState />\n ) : paged.isError ? (\n <ErrorState onRetry={() => paged.refetch()} />\n ) : paged.items.length === 0 ? (\n <EmptyState\n icon={<Landmark className=\"size-6\" aria-hidden=\"true\" />}\n title=\"No accounts yet\"\n description=\"Add your first bank, brokerage or retirement account to start tracking net worth.\"\n action={\n <Button size=\"sm\" onClick={openCreate}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n New account\n </Button>\n }\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Account</TableHead>\n <TableHead>Type</TableHead>\n <TableHead>Institution</TableHead>\n <TableHead className=\"text-right\">Cash balance</TableHead>\n <TableHead>Status</TableHead>\n <TableHead className=\"w-12 text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {paged.items.map((account) => (\n <TableRow\n key={account.id}\n className=\"group cursor-pointer transition-colors hover:bg-bg-elevated\"\n onClick={() => setSelectedId(account.id)}\n >\n <TableCell>\n <div className=\"flex items-center gap-3\">\n <div\n className=\"flex h-9 w-9 items-center justify-center rounded-lg border border-border-subtle bg-bg-sunken text-xs font-semibold text-text-secondary transition-colors group-hover:border-[var(--color-action-primary-border)] group-hover:text-text-primary\"\n aria-hidden=\"true\"\n >\n {(account.institution ?? account.name).slice(0, 2).toUpperCase()}\n </div>\n <div className=\"min-w-0\">\n <p className=\"flex items-center gap-1 truncate font-medium text-text-primary\">\n {account.name}\n <ChevronRight\n className=\"size-3.5 shrink-0 text-text-secondary opacity-0 transition-opacity group-hover:opacity-100\"\n aria-hidden=\"true\"\n />\n </p>\n <p className=\"truncate text-xs text-text-secondary\">\n {account.institution ?? 'Manual account'}\n {account.accountMask ? ` · ••${account.accountMask}` : ''}\n </p>\n </div>\n </div>\n </TableCell>\n <TableCell>\n <Badge variant=\"outline\">{account.type.replace(/_/g, ' ')}</Badge>\n </TableCell>\n <TableCell className=\"text-sm text-text-secondary\">\n {account.institution ?? '—'}\n </TableCell>\n <TableCell className=\"text-right font-semibold tabular-nums text-text-primary\">\n {formatMoney(account.cashBalance, account.currency)}\n </TableCell>\n <TableCell>\n <Badge variant={statusBadgeVariant(account.status)}>\n {account.status}\n </Badge>\n </TableCell>\n <TableCell className=\"text-right\" onClick={(e) => e.stopPropagation()}>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label={`Actions for ${account.name}`}\n >\n <MoreHorizontal className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onClick={() => openEdit(account)}>\n <Pencil className=\"size-4\" aria-hidden=\"true\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuItem\n className=\"text-status-error-text\"\n onClick={() => setArchiveTarget(account)}\n >\n <ArchiveIcon className=\"size-4\" aria-hidden=\"true\" />\n Archive\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n <LoadMore\n hasMore={paged.hasMore}\n onClick={paged.loadMore}\n loading={paged.isFetchingNextPage}\n className=\"mt-4\"\n />\n </div>\n )}\n </CardContent>\n </Card>\n </FadeIn>\n\n {/* Create account dialog */}\n <Dialog open={createOpen} onOpenChange={setCreateOpen}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>New account</DialogTitle>\n <DialogDescription>Add a bank, brokerage or retirement account.</DialogDescription>\n </DialogHeader>\n <AccountForm form={createForm} onChange={setCreateForm} />\n <DialogFooter>\n <DialogClose asChild>\n <Button variant=\"outline\">Cancel</Button>\n </DialogClose>\n <Button\n onClick={submitCreate}\n disabled={!createForm.name.trim() || createAccount.isPending}\n >\n {createAccount.isPending ? 'Creating…' : 'Create account'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* Edit account dialog */}\n <Dialog open={Boolean(editAccount)} onOpenChange={(open) => !open && setEditAccount(null)}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>Edit account</DialogTitle>\n <DialogDescription>Update account details.</DialogDescription>\n </DialogHeader>\n <AccountForm form={editForm} onChange={setEditForm} />\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setEditAccount(null)}>\n Cancel\n </Button>\n <Button\n onClick={submitEdit}\n disabled={!editForm.name.trim() || updateAccount.isPending}\n >\n {updateAccount.isPending ? 'Saving…' : 'Save changes'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* Archive confirm dialog */}\n <ConfirmDialog\n open={Boolean(archiveTarget)}\n onOpenChange={(open) => !open && setArchiveTarget(null)}\n title=\"Archive account?\"\n description={\n archiveTarget\n ? `“${archiveTarget.name}” will be archived and hidden from active lists. This does not delete its history.`\n : undefined\n }\n confirmLabel=\"Archive account\"\n pendingLabel=\"Archiving…\"\n destructive\n pending={archiveAccount.isPending}\n onConfirm={submitArchive}\n />\n\n {/* Account detail sheet */}\n <AccountDetailSheet\n accountId={selectedId}\n onClose={() => setSelectedId(undefined)}\n onRecordTransaction={openRecordTx}\n />\n\n {/* Record transaction dialog */}\n <Dialog open={txOpen} onOpenChange={setTxOpen}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>Record transaction</DialogTitle>\n <DialogDescription>Log activity against the selected account.</DialogDescription>\n </DialogHeader>\n <div className=\"grid gap-4\">\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-type\">Type</Label>\n <Select\n value={txForm.type}\n onValueChange={(v) => setTxForm((f) => ({ ...f, type: v as BraTransactionType }))}\n >\n <SelectTrigger id=\"tx-type\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {TRANSACTION_TYPES.map((t) => (\n <SelectItem key={t} value={t}>\n {t.replace(/_/g, ' ')}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-date\">Trade date</Label>\n <Input\n id=\"tx-date\"\n type=\"date\"\n value={txForm.tradeDate}\n onChange={(e) => setTxForm((f) => ({ ...f, tradeDate: e.target.value }))}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-symbol\">Symbol</Label>\n <Input\n id=\"tx-symbol\"\n value={txForm.symbol}\n onChange={(e) => setTxForm((f) => ({ ...f, symbol: e.target.value }))}\n placeholder=\"e.g. VOO\"\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-amount\">Amount</Label>\n <Input\n id=\"tx-amount\"\n type=\"number\"\n min=\"0\"\n step=\"any\"\n value={txForm.amount}\n onChange={(e) => setTxForm((f) => ({ ...f, amount: e.target.value }))}\n aria-invalid={txForm.amount.trim() !== '' && !txAmountValid}\n aria-describedby=\"tx-amount-hint\"\n />\n <p id=\"tx-amount-hint\" className=\"text-xs text-text-secondary\">\n {txForm.amount.trim() !== '' && !txAmountValid\n ? 'Enter an amount greater than 0.'\n : 'Positive amount; direction is set by the type.'}\n </p>\n </div>\n </div>\n <div className=\"grid grid-cols-3 gap-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-qty\">Quantity</Label>\n <Input\n id=\"tx-qty\"\n type=\"number\"\n value={txForm.quantity}\n onChange={(e) => setTxForm((f) => ({ ...f, quantity: e.target.value }))}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-price\">Price</Label>\n <Input\n id=\"tx-price\"\n type=\"number\"\n value={txForm.price}\n onChange={(e) => setTxForm((f) => ({ ...f, price: e.target.value }))}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-fee\">Fee</Label>\n <Input\n id=\"tx-fee\"\n type=\"number\"\n value={txForm.fee}\n onChange={(e) => setTxForm((f) => ({ ...f, fee: e.target.value }))}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"tx-notes\">Notes</Label>\n <Textarea\n id=\"tx-notes\"\n value={txForm.notes}\n onChange={(e) => setTxForm((f) => ({ ...f, notes: e.target.value }))}\n placeholder=\"Optional note\"\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setTxOpen(false)}>\n Cancel\n </Button>\n <Button onClick={submitTx} disabled={!txValid || recordTransaction.isPending}>\n {recordTransaction.isPending ? 'Saving…' : 'Record transaction'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </SectionShell>\n );\n}\n\n// ============================================================================\n// Account form (shared by create + edit dialogs)\n// ============================================================================\n\nfunction AccountForm({\n form,\n onChange,\n}: {\n form: AccountFormState;\n onChange: (updater: (prev: AccountFormState) => AccountFormState) => void;\n}) {\n return (\n <div className=\"grid gap-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"acct-name\">Name</Label>\n <Input\n id=\"acct-name\"\n value={form.name}\n onChange={(e) => onChange((f) => ({ ...f, name: e.target.value }))}\n placeholder=\"e.g. Everyday Checking\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"acct-type\">Type</Label>\n <Select\n value={form.type}\n onValueChange={(v) => onChange((f) => ({ ...f, type: v as BraAccountType }))}\n >\n <SelectTrigger id=\"acct-type\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ACCOUNT_TYPES.map((t) => (\n <SelectItem key={t} value={t}>\n {t.replace(/_/g, ' ')}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"acct-currency\">Currency</Label>\n <Input\n id=\"acct-currency\"\n value={form.currency}\n onChange={(e) => onChange((f) => ({ ...f, currency: e.target.value.toUpperCase() }))}\n placeholder=\"USD\"\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"acct-institution\">Institution</Label>\n <Input\n id=\"acct-institution\"\n value={form.institution}\n onChange={(e) => onChange((f) => ({ ...f, institution: e.target.value }))}\n placeholder=\"e.g. Chase\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"acct-balance\">Cash balance</Label>\n <Input\n id=\"acct-balance\"\n type=\"number\"\n value={form.cashBalance}\n onChange={(e) => onChange((f) => ({ ...f, cashBalance: e.target.value }))}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"acct-mask\">Account mask</Label>\n <Input\n id=\"acct-mask\"\n value={form.accountMask}\n onChange={(e) => onChange((f) => ({ ...f, accountMask: e.target.value }))}\n placeholder=\"Last 4 digits\"\n />\n </div>\n </div>\n <HouseholdPicker\n value={form.householdId}\n onChange={(householdId) => onChange((f) => ({ ...f, householdId }))}\n />\n </div>\n );\n}\n\n// ============================================================================\n// Account detail sheet — holdings + recent transactions\n// ============================================================================\n\nfunction AccountDetailSheet({\n accountId,\n onClose,\n onRecordTransaction,\n}: {\n accountId: string | undefined;\n onClose: () => void;\n onRecordTransaction: () => void;\n}) {\n const { data: account, isLoading, isError, refetch } = useAccount(accountId);\n const txPaged = usePagedTransactions(accountId ? { accountId } : {});\n const transactions = accountId ? txPaged.items : [];\n\n // Holdings allocation by market value — real per-holding figures.\n const holdingsAlloc = useMemo<DonutDatum[]>(() => {\n if (!account) return [];\n return account.holdings\n .filter((h) => Number.isFinite(h.marketValue) && h.marketValue > 0)\n .map((h) => ({ label: h.symbol, value: h.marketValue }))\n .sort((a, b) => b.value - a.value);\n }, [account]);\n\n const holdingsTotal = holdingsAlloc.reduce((s, h) => s + h.value, 0);\n\n return (\n <Sheet open={Boolean(accountId)} onOpenChange={(open) => !open && onClose()}>\n <SheetContent className=\"flex w-full flex-col overflow-y-auto p-0 sm:max-w-xl\">\n <SheetHeader className=\"border-b border-border-subtle px-6 pb-4 pt-6\">\n <SheetTitle className=\"text-lg\">{account?.name ?? 'Account'}</SheetTitle>\n <SheetDescription>\n {account\n ? `${account.institution ?? 'Manual account'} · ${account.type.replace(/_/g, ' ')}`\n : 'Loading account details…'}\n </SheetDescription>\n </SheetHeader>\n\n {isLoading ? (\n <div className=\"p-6\">\n <LoadingState rows={3} />\n </div>\n ) : isError ? (\n <div className=\"p-6\">\n <ErrorState onRetry={() => refetch()} />\n </div>\n ) : !account ? (\n <div className=\"p-6\">\n <EmptyState\n title=\"Account not found\"\n description=\"This account is no longer available.\"\n />\n </div>\n ) : (\n <div className=\"space-y-6 p-6\">\n {/* Hero — cash balance with emerald glow + status + owner */}\n <GlassCard glow className=\"space-y-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">\n Cash balance\n </p>\n <p className=\"mt-1 text-3xl font-semibold tracking-tight text-text-primary\">\n {formatMoney(account.cashBalance, account.currency)}\n </p>\n </div>\n <Badge variant={statusBadgeVariant(account.status)}>{account.status}</Badge>\n </div>\n <div className=\"flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border-subtle pt-3 text-xs\">\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-text-secondary\">Owner</span>\n <ActorName id={account.ownerId} className=\"font-medium text-text-primary\" />\n </div>\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-text-secondary\">Holdings value</span>\n <span className=\"font-medium text-text-primary\">\n {holdingsTotal > 0 ? formatMoney(holdingsTotal, account.currency) : '—'}\n </span>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-text-secondary\">Positions</span>\n <span className=\"font-medium text-text-primary\">{account.holdings.length}</span>\n </div>\n </div>\n </GlassCard>\n\n {/* Allocation donut — market value split across positions */}\n {holdingsAlloc.length > 0 ? (\n <div className=\"rounded-xl border border-border-subtle bg-bg-surface p-4\">\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">\n Holdings allocation\n </h3>\n <DonutChart\n data={holdingsAlloc}\n centerValue={formatCompact(holdingsTotal, account.currency)}\n centerLabel=\"Market value\"\n height={180}\n valueFormatter={(v) => formatMoney(v, account.currency)}\n />\n </div>\n ) : null}\n\n {/* Tabbed detail: Holdings / Transactions / Documents */}\n <Tabs defaultValue=\"holdings\" className=\"w-full\">\n <TabsList className=\"w-full\">\n <TabsTrigger value=\"holdings\" className=\"flex-1\">\n Holdings\n </TabsTrigger>\n <TabsTrigger value=\"transactions\" className=\"flex-1\">\n Transactions\n </TabsTrigger>\n <TabsTrigger value=\"documents\" className=\"flex-1\">\n Documents\n </TabsTrigger>\n </TabsList>\n\n {/* Holdings */}\n <TabsContent value=\"holdings\" className=\"mt-4\">\n {account.holdings.length === 0 ? (\n <EmptyState\n icon={<Layers className=\"size-6\" aria-hidden=\"true\" />}\n title=\"No holdings\"\n description=\"This account has no positions recorded yet.\"\n />\n ) : (\n <div className=\"overflow-x-auto rounded-lg border border-border-subtle\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Holding</TableHead>\n <TableHead className=\"text-right\">Qty</TableHead>\n <TableHead className=\"text-right\">Price</TableHead>\n <TableHead className=\"text-right\">Market value</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {account.holdings.map((h) => (\n <TableRow key={h.id} className=\"transition-colors hover:bg-bg-elevated\">\n <TableCell>\n <p className=\"font-medium text-text-primary\">{h.symbol}</p>\n {h.name ? (\n <p className=\"truncate text-xs text-text-secondary\">{h.name}</p>\n ) : null}\n </TableCell>\n <TableCell className=\"text-right tabular-nums text-text-secondary\">\n {h.quantity}\n </TableCell>\n <TableCell className=\"text-right tabular-nums text-text-secondary\">\n {formatMoney(h.lastPrice, h.currency)}\n </TableCell>\n <TableCell className=\"text-right font-semibold tabular-nums text-text-primary\">\n {formatMoney(h.marketValue, h.currency)}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n )}\n </TabsContent>\n\n {/* Transactions */}\n <TabsContent value=\"transactions\" className=\"mt-4 space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Recent activity</h3>\n <Button size=\"sm\" onClick={onRecordTransaction}>\n <Receipt className=\"size-4\" aria-hidden=\"true\" />\n Record\n </Button>\n </div>\n {txPaged.isLoading ? (\n <LoadingState rows={2} />\n ) : txPaged.isError ? (\n <ErrorState onRetry={() => txPaged.refetch()} />\n ) : transactions.length === 0 ? (\n <EmptyState\n icon={<Receipt className=\"size-6\" aria-hidden=\"true\" />}\n title=\"No transactions\"\n description=\"Record a transaction to see activity here.\"\n action={\n <Button size=\"sm\" onClick={onRecordTransaction}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n Record transaction\n </Button>\n }\n />\n ) : (\n <div className=\"space-y-2\">\n {transactions.map((tx) => {\n const inflow = isInflow(tx.type);\n const Icon = inflow ? ArrowUpRight : ArrowDownRight;\n const tone = inflow ? 'text-status-success-text' : 'text-status-error-text';\n const iconBg = inflow ? 'bg-status-success-bg' : 'bg-status-error-bg';\n return (\n <div\n key={tx.id}\n className=\"flex items-center justify-between gap-3 rounded-lg border border-border-subtle bg-bg-surface p-3 transition-colors hover:bg-bg-elevated\"\n >\n <div className=\"flex min-w-0 items-center gap-3\">\n <span\n className={`flex size-8 shrink-0 items-center justify-center rounded-full ${iconBg} ${tone}`}\n aria-hidden=\"true\"\n >\n <Icon className=\"size-4\" />\n </span>\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"truncate text-sm font-medium text-text-primary\">\n {tx.type.replace(/_/g, ' ')}\n </span>\n {tx.symbol ? (\n <Badge variant=\"outline\" className=\"shrink-0\">\n {tx.symbol}\n </Badge>\n ) : null}\n </div>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {formatDate(tx.tradeDate)}\n </p>\n </div>\n </div>\n <span className={`shrink-0 text-sm font-semibold tabular-nums ${tone}`}>\n {inflow ? '+' : '−'}\n {formatMoney(Math.abs(tx.amount), tx.currency)}\n </span>\n </div>\n );\n })}\n <LoadMore\n hasMore={txPaged.hasMore}\n onClick={txPaged.loadMore}\n loading={txPaged.isFetchingNextPage}\n />\n </div>\n )}\n </TabsContent>\n\n {/* Documents — statements, KYC, tax packs (wspace-files reuse) */}\n <TabsContent value=\"documents\" className=\"mt-4\">\n <DocumentsPanel entityType=\"brainyrich-account\" entityId={account.id} />\n </TabsContent>\n </Tabs>\n </div>\n )}\n </SheetContent>\n </Sheet>\n );\n}\n"],"mappings":"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"}
|