Spaces:
Running
Running
Manus commited on
Commit ·
09e85dd
1
Parent(s): 00148af
Complete unified frontend workflow screens
Browse files- manus_write_test.txt +0 -3
- src/pages/CrossModelScreens.tsx +3 -2
- src/pages/FormulaScreens.tsx +3 -3
- src/pages/InputScreens.tsx +5 -3
- src/pages/ModelWorkspaceScreens.tsx +7 -5
- src/pages/ModelsPage.tsx +1 -1
- src/pages/ProjectionScreens.tsx +3 -3
- src/pages/ReportScreens.tsx +3 -1
- src/pages/ResultScreens.tsx +3 -1
manus_write_test.txt
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
Hugging Face write-access test
|
| 2 |
-
Created by Manus on 2026-09-28T19:45:00+01:00
|
| 3 |
-
This file can be safely deleted.
|
|
|
|
|
|
|
|
|
|
|
|
src/pages/CrossModelScreens.tsx
CHANGED
|
@@ -1,9 +1,10 @@
|
|
| 1 |
import { ArrowLeft, ArrowRight, Check, Database, GitBranch, Link2, Network, ShieldCheck, Workflow } from 'lucide-react'
|
| 2 |
import { StatusBadge } from '../components/ui/StatusBadge'
|
| 3 |
-
import { useApp } from '../context/useApp'
|
| 4 |
|
| 5 |
type MappingRow = [string,string,string,string]
|
| 6 |
const mappings: MappingRow[] = [['net_reserve','GAAP Cohort · net_reserve','Currency · Projection year × scenario','Mapped'],['policy_cash_flow','GAAP Cohort · policy_cash_flow','Currency · Policy year × scenario','Mapped'],['reserve_change_pct','GAAP Cohort · reserve_change_pct','Percent · Projection year × scenario','Warning']]
|
| 7 |
-
export function OutputMappingPage({ onBack, onTrace }: { onBack: () => void; onTrace: () => void
|
| 8 |
export function CrossModelTracePage({ onBack }: { onBack: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Output Mapping</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Cross-model Trace</span><h2 className="section-title">Which cash-flow output fed this GAAP result?</h2><p className="muted">The trace crosses the published-output contract without losing model, run, value, or source context.</p></div><StatusBadge status="Validated" /></div><div className="cross-trace-path"><TraceBox icon={<Workflow size={16} />} title="GAAP net reserve" meta="Result · $1,245.6M" /><ArrowRight size={20} /><TraceBox icon={<Database size={16} />} title="GAAP Cohort input" meta="net_reserve · mapped" /><ArrowRight size={20} /><TraceBox icon={<Network size={16} />} title="Published output" meta="Cash Flow Model · v3.2" /><ArrowRight size={20} /><TraceBox icon={<GitBranch size={16} />} title="Cash Flow run" meta="JR-20261231-001 · completed" /></div><div className="panel"><h2 className="section-title">Boundary provenance</h2><div className="cross-provenance"><span><small>Producer</small><b>Cash Flow Model v3.2</b></span><span><small>Output</small><b>net_reserve</b></span><span><small>Mapping</small><b>v1.2 · validated</b></span><span><small>Consumer</small><b>GAAP Cohort Model v2.4</b></span></div></div></div> }
|
| 9 |
function TraceBox({ icon, title, meta }: { icon: React.ReactNode; title: string; meta: string }) { return <span className="cross-trace-box"><i>{icon}</i><b>{title}</b><small>{meta}</small></span> }
|
|
|
|
|
|
|
|
|
| 1 |
import { ArrowLeft, ArrowRight, Check, Database, GitBranch, Link2, Network, ShieldCheck, Workflow } from 'lucide-react'
|
| 2 |
import { StatusBadge } from '../components/ui/StatusBadge'
|
|
|
|
| 3 |
|
| 4 |
type MappingRow = [string,string,string,string]
|
| 5 |
const mappings: MappingRow[] = [['net_reserve','GAAP Cohort · net_reserve','Currency · Projection year × scenario','Mapped'],['policy_cash_flow','GAAP Cohort · policy_cash_flow','Currency · Policy year × scenario','Mapped'],['reserve_change_pct','GAAP Cohort · reserve_change_pct','Percent · Projection year × scenario','Warning']]
|
| 6 |
+
export function OutputMappingPage({ onBack, onTrace, onLinkage }: { onBack: () => void; onTrace: () => void; onLinkage: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Published Outputs</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Output Mapping · downstream contract</span><h2 className="section-title">Cash Flow Model → GAAP Cohort Model</h2><p className="muted">Map producer outputs to downstream variables with type, dimension, transformation, and source-run validation.</p></div><StatusBadge status="Validated" /></div><div className="mapping-contract-grid"><div className="panel"><div className="panel-title-row"><h2 className="section-title">Variable mappings</h2><StatusBadge status="Validated" /></div><div className="cross-mapping-table">{mappings.map(row => <div className="cross-mapping-row" key={row[0]}><span><b>{row[0]}</b><small>Producer output</small></span><ArrowRight size={14} /><span><b>{row[1]}</b><small>{row[2]}</small></span><StatusBadge status={row[3] === 'Mapped' ? 'Validated' : 'Needs review'} /><ArrowRight size={14} /></div>)}</div></div><aside className="panel"><h2 className="section-title">Compatibility</h2><div className="check-list"><span><Check size={15} />Units compatible</span><span><Check size={15} />Dimensions compatible</span><span className="check-warning"><ShieldCheck size={15} />1 warning: stale mapping version</span><span><Check size={15} />Producer output schema valid</span></div><button className="button button-primary full-button" onClick={onTrace}><GitBranch size={15} />Trace across boundary</button></aside></div><div className="workspace-callout"><Link2 size={19} /><span><b>Run linkage preserved</b><small>Downstream values resolve to Cash Flow Run JR-20261231-001 · Model v3.2.</small></span><button onClick={onLinkage}>Open run linkage <ArrowRight size={15} /></button></div></div> }
|
| 7 |
export function CrossModelTracePage({ onBack }: { onBack: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Output Mapping</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Cross-model Trace</span><h2 className="section-title">Which cash-flow output fed this GAAP result?</h2><p className="muted">The trace crosses the published-output contract without losing model, run, value, or source context.</p></div><StatusBadge status="Validated" /></div><div className="cross-trace-path"><TraceBox icon={<Workflow size={16} />} title="GAAP net reserve" meta="Result · $1,245.6M" /><ArrowRight size={20} /><TraceBox icon={<Database size={16} />} title="GAAP Cohort input" meta="net_reserve · mapped" /><ArrowRight size={20} /><TraceBox icon={<Network size={16} />} title="Published output" meta="Cash Flow Model · v3.2" /><ArrowRight size={20} /><TraceBox icon={<GitBranch size={16} />} title="Cash Flow run" meta="JR-20261231-001 · completed" /></div><div className="panel"><h2 className="section-title">Boundary provenance</h2><div className="cross-provenance"><span><small>Producer</small><b>Cash Flow Model v3.2</b></span><span><small>Output</small><b>net_reserve</b></span><span><small>Mapping</small><b>v1.2 · validated</b></span><span><small>Consumer</small><b>GAAP Cohort Model v2.4</b></span></div></div></div> }
|
| 8 |
function TraceBox({ icon, title, meta }: { icon: React.ReactNode; title: string; meta: string }) { return <span className="cross-trace-box"><i>{icon}</i><b>{title}</b><small>{meta}</small></span> }
|
| 9 |
+
|
| 10 |
+
export function RunLinkagePage({ onBack }: { onBack: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Output Mapping</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Run Linkage · immutable provenance</span><h2 className="section-title">Cash Flow Run JR-20261231-001</h2><p className="muted">The downstream result resolves to the exact producer model version, output contract, and pinned execution snapshot.</p></div><StatusBadge status="Validated" /></div><div className="panel"><div className="cross-provenance"><span><small>Producer model</small><b>Cash Flow Model · v3.2</b></span><span><small>Published output</small><b>net_reserve · Currency</b></span><span><small>Mapping contract</small><b>v1.2 · validated</b></span><span><small>Execution snapshot</small><b>JR-20261231-001 · completed</b></span></div><div className="check-list"><span><Check size={15} />Inputs pinned at execution</span><span><Check size={15} />Formula lineage captured</span><span><Check size={15} />Consumer model acknowledged contract</span></div></div></div> }
|
src/pages/FormulaScreens.tsx
CHANGED
|
@@ -8,11 +8,11 @@ type FormulaView = 'library' | 'detail' | 'inheritance' | 'compare' | 'wizard'
|
|
| 8 |
const groups = [['Universal reserve logic','42 formulas','Shared','8 consumers'],['Mortality & lapse','31 formulas','Inherited','4 consumers'],['Policy behavior','18 formulas','Overridden','2 consumers'],['Expense & fees','22 formulas','Local','1 consumer']]
|
| 9 |
export function FormulaWorkspacePage() { const { notify } = useApp(); const [view,setView] = useState<FormulaView>('library'); if(view === 'detail') return <FormulaDetail onBack={() => setView('library')} onInheritance={() => setView('inheritance')} />; if(view === 'inheritance') return <InheritanceMap onBack={() => setView('library')} onCompare={() => setView('compare')} />; if(view === 'compare') return <OverrideCompare onBack={() => setView('inheritance')} />; if(view === 'wizard') return <InheritanceWizard onBack={() => setView('library')} onSave={() => { setView('library'); notify('New profile saved with inherited formulas') }} />; return <div className="workspace-stack"><WorkspaceToolbar searchPlaceholder="Search formula groups, formulas, consumers..." action="New inheritance" actionIcon={<Plus size={15} />} onAction={() => setView('wizard')} /><div className="formula-subnav"><button className="formula-subnav-active">Formula Groups</button><button onClick={() => setView('detail')}>Formula Detail</button><button onClick={() => setView('inheritance')}>Inheritance Map</button><button onClick={() => setView('wizard')}>New Block / Profile</button></div><div className="formula-screen-grid"><div className="panel formula-library"><div className="panel-title-row"><div><span className="eyebrow">Global formula library</span><h2 className="section-title">Formula Groups</h2><p className="muted">Reusable, versioned logic bundles with visible lineage and consumers.</p></div><button className="filter-chip"><Network size={14} />All states</button></div>{groups.map(([name,count,state,consumers]) => <button className="formula-row" key={name} onClick={() => setView('detail')}><span className="formula-icon"><Workflow size={17} /></span><span><b>{name}</b><small>{count} · {consumers}</small></span><StatusBadge status={state as 'Shared' | 'Inherited' | 'Overridden' | 'Local'} /><ArrowRight size={16} /></button>)}</div><aside className="panel formula-overview"><span className="eyebrow">Selected group</span><h2 className="section-title">Universal reserve logic</h2><p className="muted">Shared actuarial logic reused across blocks and profiles.</p><div className="formula-overview-stats"><ContextStat label="Members" value="42" /><ContextStat label="Consumers" value="8" /><ContextStat label="Version" value="v4.1" /></div><button className="button button-primary full-button" onClick={() => setView('detail')}>Open group detail <ArrowRight size={15} /></button><button className="button button-secondary full-button" onClick={() => setView('inheritance')}><GitBranch size={15} />View inheritance map</button></aside></div></div> }
|
| 10 |
function ContextStat({ label, value }: { label: string; value: string }) { return <span><small>{label}</small><b>{value}</b></span> }
|
| 11 |
-
function FormulaDetail({ onBack, onInheritance }: { onBack: () => void; onInheritance: () => void }) { const {
|
| 12 |
function DatabaseIcon() { return <Layers3 size={16} /> }
|
| 13 |
function DependencyCard({ icon, title, items }: { icon: ReactNode; title: string; items: string[] }) { return <div className="dependency-card"><div><span>{icon}</span><b>{title}</b></div>{items.map(item => <span key={item}>{item}<ArrowRight size={12} /></span>)}</div> }
|
| 14 |
function InheritanceMap({ onBack, onCompare }: { onBack: () => void; onCompare: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Formula Groups</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Inheritance / Relationship Map</span><h2 className="section-title">How reserve_t is reused</h2><p className="muted">Source groups, local overrides, blocks, profiles, bases, and consumers remain visible together.</p></div><StatusBadge status="Inherited" /></div><div className="inheritance-map"><MapNode title="Universal reserve logic" meta="Shared · v4.1" /><ArrowRight size={20} /><MapNode title="GAAP Reserve Base" meta="Inherited" /><ArrowRight size={20} /><MapNode title="reserve_t" meta="Formula · 8 consumers" /><ArrowRight size={20} /><MapNode title="net_reserve" meta="Published output" /></div><div className="panel inheritance-table"><div className="panel-title-row"><h2 className="section-title">Reuse and override states</h2><button className="button button-secondary" onClick={onCompare}><GitCompare size={15} />Open override compare</button></div>{[['MYGA · GAAP Reserve Base','Inherited','Source unchanged'],['FIA · Stat','Shared','Same source group'],['RILA · Embedded Value','Overridden','Local expense adjustment']].map(row => <div className="inheritance-row" key={row[0]}><span><b>{row[0]}</b><small>{row[2]}</small></span><StatusBadge status={row[1] as 'Shared' | 'Inherited' | 'Overridden'} /><ArrowRight size={15} /></div>)}</div></div> }
|
| 15 |
function MapNode({ title, meta }: { title: string; meta: string }) { return <span className="map-node"><GitBranch size={16} /><b>{title}</b><small>{meta}</small></span> }
|
| 16 |
-
function OverrideCompare({ onBack }: { onBack: () => void }) { const { notify } = useApp(); return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Inheritance Map</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Override Compare</span><h2 className="section-title">Expense & fees · source vs local</h2><p className="muted">The local override retains its source relationship and can be restored, rebased, or intentionally forked.</p></div><StatusBadge status=
|
| 17 |
-
function InheritanceWizard({ onBack, onSave }: { onBack: () => void; onSave: () => void }) { const [step,setStep] = useState(1); return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Formula Groups</button><div className="panel detail-workspace-head"><div><span className="eyebrow">New Block / Profile Wizard · step {step} of 3</span><h2 className="section-title">Create GAAP Reserve Profile</h2><p className="muted">Select multiple formula sources, choose members, resolve conflicts, then preview the resulting structure.</p></div><StatusBadge status="Draft" /></div><div className="wizard-steps"><span className={step >= 1 ? 'wizard-step-active' : ''}>1 Sources</span><span className={step >= 2 ? 'wizard-step-active' : ''}>2 Select formulas</span><span className={step >= 3 ? 'wizard-step-active' : ''}>3 Resolve and save</span></div><div className="panel wizard-panel">{step === 1 && <><h2 className="section-title">Choose source groups</h2><WizardChoice label="Universal reserve logic · v4.1" checked /><WizardChoice label="Mortality & lapse · v3.8" checked /><WizardChoice label="Expense & fees · v2.0" /></>}{step === 2 && <><h2 className="section-title">Select formulas to inherit</h2><WizardChoice label="All 42 reserve formulas" checked /><WizardChoice label="31 mortality and lapse formulas" checked /><WizardChoice label="18 expense formulas" /></>}{step === 3 && <><h2 className="section-title">Resolve conflicts</h2><div className="conflict-row"><ShieldCheck size={17} /><span><b>3 duplicate formulas detected</b><small>Choose source priority or keep local versions.</small></span><button className="button button-secondary">Review conflicts</button></div><div className="check-list"><span><Check size={15} />Final structure preview generated</span><span><Check size={15} />Source relationships retained</span><span><Check size={15} />Downstream consumers unaffected</span></div></>}</div><div className="wizard-actions"><button className="button button-secondary" onClick={onBack}>Cancel</button>{step < 3 ? <button className="button button-primary" onClick={() => setStep(step + 1)}>Continue <ArrowRight size={15} /></button> : <button className="button button-primary" onClick={onSave}><Check size={15} />Save profile</button>}</div></div> }
|
| 18 |
function WizardChoice({ label, checked }: { label: string; checked?: boolean }) { return <label className="wizard-choice"><input type="checkbox" defaultChecked={checked} /><span><b>{label}</b><small>Shared source · validated · 8 consumers</small></span><ArrowRight size={14} /></label> }
|
|
|
|
| 8 |
const groups = [['Universal reserve logic','42 formulas','Shared','8 consumers'],['Mortality & lapse','31 formulas','Inherited','4 consumers'],['Policy behavior','18 formulas','Overridden','2 consumers'],['Expense & fees','22 formulas','Local','1 consumer']]
|
| 9 |
export function FormulaWorkspacePage() { const { notify } = useApp(); const [view,setView] = useState<FormulaView>('library'); if(view === 'detail') return <FormulaDetail onBack={() => setView('library')} onInheritance={() => setView('inheritance')} />; if(view === 'inheritance') return <InheritanceMap onBack={() => setView('library')} onCompare={() => setView('compare')} />; if(view === 'compare') return <OverrideCompare onBack={() => setView('inheritance')} />; if(view === 'wizard') return <InheritanceWizard onBack={() => setView('library')} onSave={() => { setView('library'); notify('New profile saved with inherited formulas') }} />; return <div className="workspace-stack"><WorkspaceToolbar searchPlaceholder="Search formula groups, formulas, consumers..." action="New inheritance" actionIcon={<Plus size={15} />} onAction={() => setView('wizard')} /><div className="formula-subnav"><button className="formula-subnav-active">Formula Groups</button><button onClick={() => setView('detail')}>Formula Detail</button><button onClick={() => setView('inheritance')}>Inheritance Map</button><button onClick={() => setView('wizard')}>New Block / Profile</button></div><div className="formula-screen-grid"><div className="panel formula-library"><div className="panel-title-row"><div><span className="eyebrow">Global formula library</span><h2 className="section-title">Formula Groups</h2><p className="muted">Reusable, versioned logic bundles with visible lineage and consumers.</p></div><button className="filter-chip"><Network size={14} />All states</button></div>{groups.map(([name,count,state,consumers]) => <button className="formula-row" key={name} onClick={() => setView('detail')}><span className="formula-icon"><Workflow size={17} /></span><span><b>{name}</b><small>{count} · {consumers}</small></span><StatusBadge status={state as 'Shared' | 'Inherited' | 'Overridden' | 'Local'} /><ArrowRight size={16} /></button>)}</div><aside className="panel formula-overview"><span className="eyebrow">Selected group</span><h2 className="section-title">Universal reserve logic</h2><p className="muted">Shared actuarial logic reused across blocks and profiles.</p><div className="formula-overview-stats"><ContextStat label="Members" value="42" /><ContextStat label="Consumers" value="8" /><ContextStat label="Version" value="v4.1" /></div><button className="button button-primary full-button" onClick={() => setView('detail')}>Open group detail <ArrowRight size={15} /></button><button className="button button-secondary full-button" onClick={() => setView('inheritance')}><GitBranch size={15} />View inheritance map</button></aside></div></div> }
|
| 10 |
function ContextStat({ label, value }: { label: string; value: string }) { return <span><small>{label}</small><b>{value}</b></span> }
|
| 11 |
+
function FormulaDetail({ onBack, onInheritance }: { onBack: () => void; onInheritance: () => void }) { const { openInvestigation } = useApp(); return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Formula Groups</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Formula Detail / Editor · shared</span><h2 className="section-title">reserve_t</h2><p className="muted">Universal reserve logic · v4.1 · used by 8 consumers</p></div><StatusBadge status="Shared" /></div><div className="formula-detail-grid"><div className="panel"><div className="formula-expression"><span>fx</span><code>reserve_t = reserve_(t-1) + premium_t - benefit_t - expense_t</code></div><div className="formula-explanation"><h2 className="section-title">Readable explanation</h2><p className="muted">The reserve at the current period equals the prior reserve plus premium, less benefits and expenses.</p></div><div className="formula-dependency-grid"><DependencyCard icon={<DatabaseIcon />} title="Inputs" items={['premium_t','benefit_t','expense_t']} /><DependencyCard icon={<GitBranch size={16} />} title="Dependencies" items={['reserve_(t-1)','policy_status']} /><DependencyCard icon={<Network size={16} />} title="Dependents" items={['net_reserve','reserve_change_pct']} /></div></div><aside className="panel"><h2 className="section-title">Provenance and checks</h2><div className="check-list"><span><Check size={15} />Source: Universal reserve logic v4.1</span><span><Check size={15} />Inherited by GAAP Reserve Base</span><span><Check size={15} />12 tests passing</span><span><Check size={15} />Trace lineage available</span></div><button className="button button-primary full-button" onClick={() => openInvestigation({ kind: 'trace', runId: 'JR-20261231-001', metric: 'reserve_t' })}><GitBranch size={15} />Trace formula</button><button className="button button-secondary full-button" onClick={onInheritance}><GitCompare size={15} />Compare source / local</button></aside></div></div> }
|
| 12 |
function DatabaseIcon() { return <Layers3 size={16} /> }
|
| 13 |
function DependencyCard({ icon, title, items }: { icon: ReactNode; title: string; items: string[] }) { return <div className="dependency-card"><div><span>{icon}</span><b>{title}</b></div>{items.map(item => <span key={item}>{item}<ArrowRight size={12} /></span>)}</div> }
|
| 14 |
function InheritanceMap({ onBack, onCompare }: { onBack: () => void; onCompare: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Formula Groups</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Inheritance / Relationship Map</span><h2 className="section-title">How reserve_t is reused</h2><p className="muted">Source groups, local overrides, blocks, profiles, bases, and consumers remain visible together.</p></div><StatusBadge status="Inherited" /></div><div className="inheritance-map"><MapNode title="Universal reserve logic" meta="Shared · v4.1" /><ArrowRight size={20} /><MapNode title="GAAP Reserve Base" meta="Inherited" /><ArrowRight size={20} /><MapNode title="reserve_t" meta="Formula · 8 consumers" /><ArrowRight size={20} /><MapNode title="net_reserve" meta="Published output" /></div><div className="panel inheritance-table"><div className="panel-title-row"><h2 className="section-title">Reuse and override states</h2><button className="button button-secondary" onClick={onCompare}><GitCompare size={15} />Open override compare</button></div>{[['MYGA · GAAP Reserve Base','Inherited','Source unchanged'],['FIA · Stat','Shared','Same source group'],['RILA · Embedded Value','Overridden','Local expense adjustment']].map(row => <div className="inheritance-row" key={row[0]}><span><b>{row[0]}</b><small>{row[2]}</small></span><StatusBadge status={row[1] as 'Shared' | 'Inherited' | 'Overridden'} /><ArrowRight size={15} /></div>)}</div></div> }
|
| 15 |
function MapNode({ title, meta }: { title: string; meta: string }) { return <span className="map-node"><GitBranch size={16} /><b>{title}</b><small>{meta}</small></span> }
|
| 16 |
+
function OverrideCompare({ onBack }: { onBack: () => void }) { const { notify } = useApp(); const [state, setState] = useState<'Overridden' | 'Restored' | 'Rebased' | 'Forked'>('Overridden'); return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Inheritance Map</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Override Compare</span><h2 className="section-title">Expense & fees · source vs local</h2><p className="muted">The local override retains its source relationship and can be restored, rebased, or intentionally forked.</p></div><StatusBadge status={state === 'Overridden' ? 'Overridden' : 'Validated'} /></div><div className="diff-grid"><div className="panel"><h2 className="section-title">Source · v4.1</h2><pre>expense_t = base_expense_t + premium_t × 0.02</pre></div><div className="panel diff-local"><h2 className="section-title">Local · v4.1 + override</h2><pre>{state === 'Restored' ? 'expense_t = base_expense_t + premium_t × 0.02' : state === 'Forked' ? 'expense_t = base_expense_t + premium_t × 0.025 // forked' : 'expense_t = base_expense_t + premium_t × 0.025'}</pre></div></div><div className="workspace-callout"><Split size={19} /><span><b>Reason for override</b><small>GAAP basis applies a higher acquisition expense assumption for this block.</small></span><button onClick={() => { setState('Restored'); notify('Local override restored to source') }}><RotateCcw size={15} />Restore source</button><button onClick={() => { setState('Rebased'); notify('Override rebased on latest source') }}><RefreshCw size={15} />Rebase</button><button onClick={() => { setState('Forked'); notify('Local formula fork created') }}><GitBranch size={15} />Fork</button></div></div> }
|
| 17 |
+
export function InheritanceWizard({ onBack, onSave }: { onBack: () => void; onSave: () => void }) { const [step,setStep] = useState(1); return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Formula Groups</button><div className="panel detail-workspace-head"><div><span className="eyebrow">New Block / Profile Wizard · step {step} of 3</span><h2 className="section-title">Create GAAP Reserve Profile</h2><p className="muted">Select multiple formula sources, choose members, resolve conflicts, then preview the resulting structure.</p></div><StatusBadge status="Draft" /></div><div className="wizard-steps"><span className={step >= 1 ? 'wizard-step-active' : ''}>1 Sources</span><span className={step >= 2 ? 'wizard-step-active' : ''}>2 Select formulas</span><span className={step >= 3 ? 'wizard-step-active' : ''}>3 Resolve and save</span></div><div className="panel wizard-panel">{step === 1 && <><h2 className="section-title">Choose source groups</h2><WizardChoice label="Universal reserve logic · v4.1" checked /><WizardChoice label="Mortality & lapse · v3.8" checked /><WizardChoice label="Expense & fees · v2.0" /></>}{step === 2 && <><h2 className="section-title">Select formulas to inherit</h2><WizardChoice label="All 42 reserve formulas" checked /><WizardChoice label="31 mortality and lapse formulas" checked /><WizardChoice label="18 expense formulas" /></>}{step === 3 && <><h2 className="section-title">Resolve conflicts</h2><div className="conflict-row"><ShieldCheck size={17} /><span><b>3 duplicate formulas detected</b><small>Choose source priority or keep local versions.</small></span><button className="button button-secondary">Review conflicts</button></div><div className="check-list"><span><Check size={15} />Final structure preview generated</span><span><Check size={15} />Source relationships retained</span><span><Check size={15} />Downstream consumers unaffected</span></div></>}</div><div className="wizard-actions"><button className="button button-secondary" onClick={onBack}>Cancel</button>{step < 3 ? <button className="button button-primary" onClick={() => setStep(step + 1)}>Continue <ArrowRight size={15} /></button> : <button className="button button-primary" onClick={onSave}><Check size={15} />Save profile</button>}</div></div> }
|
| 18 |
function WizardChoice({ label, checked }: { label: string; checked?: boolean }) { return <label className="wizard-choice"><input type="checkbox" defaultChecked={checked} /><span><b>{label}</b><small>Shared source · validated · 8 consumers</small></span><ArrowRight size={14} /></label> }
|
src/pages/InputScreens.tsx
CHANGED
|
@@ -5,13 +5,15 @@ import { StatusBadge } from '../components/ui/StatusBadge'
|
|
| 5 |
import { SummaryTile } from '../components/ui/SummaryTile'
|
| 6 |
import { WorkspaceToolbar } from '../components/ui/WorkspaceToolbar'
|
| 7 |
|
| 8 |
-
type InputView = 'library' | 'liability' | 'asset' | 'assumptions' | 'factors' | 'projection' | 'validation'
|
| 9 |
const inputRows = [['Liability Inforce','MYGA liability file','v2026.12','24,810 records','Validated'],['Asset Inforce','MYGA asset holdings','v2026.11','8,402 records','Validated'],['Assumption Tables','MYGA reserve assumptions','v3.2','18 dimensions','Validated'],['Factor Tables','Surrender and mortality factors','v1.8','42,116 cells','Needs review'],['Projection Inputs','Q4 valuation controls','v2026.12','26 fields','Validated']]
|
| 10 |
|
| 11 |
-
export function InputLibraryPage() { const [view, setView] = useState<InputView>('library'); const select = (next: InputView) => setView(next); if (view !== 'library') return <InputDetailView view={view} onBack={() => select('library')} onNavigate={select} />; return <div className="workspace-stack"><WorkspaceToolbar searchPlaceholder="Search input libraries, tables, mappings..." action="Import input" actionIcon={<Upload size={15} />} /><div className="input-subnav">{[['library','Input Library'],['liability','Liability Inforce'],['asset','Asset Inforce'],['assumptions','Assumption Tables'],['factors','Factor Tables'],['projection','Projection Inputs'],['validation','Validation Center']].map(([key,label]) => <button className={view === key ? 'input-subnav-active' : ''} key={key} onClick={() => select(key as InputView)}>{label}</button>)}</div><div className="model-summary-grid"><SummaryTile icon={<Database size={18} />} label="Input objects" value="24" tone="dark" /><SummaryTile icon={<Check size={18} />} label="Validated" value="19" /><SummaryTile icon={<AlertTriangle size={18} />} label="Needs review" value="3" /><SummaryTile icon={<Link2 size={18} />} label="Mappings" value="42" /></div><div className="panel table-panel"><div className="panel-title-row"><div><span className="eyebrow">MYGA · Model v3.2 · GAAP</span><h2 className="section-title">Input Library</h2><p className="muted">Versioned input objects connected to the active model and projection scope.</p></div><button className="view-all" onClick={() => select('validation')}><ShieldCheck size={14} />Validation Center</button></div><div className="input-table">{inputRows.map(([name,detail,version,count,status]) => <button className="input-row" key={name} onClick={() => select(name === 'Liability Inforce' ? 'liability' : name === 'Asset Inforce' ? 'asset' : name === 'Assumption Tables' ? 'assumptions' : name === 'Factor Tables' ? 'factors' : 'projection')}><span className="model-name"><span className="model-icon"><Database size={16} /></span><span><b>{name}</b><small>{detail}</small></span></span><span>{version}</span><span>MYGA · GAAP</span><span>{count}</span><span><StatusBadge status={status as 'Validated' | 'Needs review'} /></span><ArrowRight size={17} /></button>)}</div></div><div className="workspace-callout"><ShieldCheck size={20} /><span><b>3 issues need review</b><small>All blocking mappings are complete. Review factor-table warnings before the next projection run.</small></span><button onClick={() => select('validation')}>Review issues <ArrowRight size={15} /></button></div></div> }
|
| 12 |
|
| 13 |
-
function InputDetailView({ view, onBack, onNavigate }: { view: InputView; onBack: () => void; onNavigate: (view: InputView) => void }) { const { notify } = useApp(); const config: Record<Exclude<InputView,'library'>, { title: string; eyebrow: string; description: string }> = { liability: { title: 'Liability Inforce', eyebrow: 'Input object · imported detail', description: 'Preview records, map segments and confirm existing versus future-purchase scope.' }, asset: { title: 'Asset Inforce', eyebrow: 'Input object · imported detail', description: 'Review holdings, portfolio mapping, and downstream asset cash-flow consumers.' }, assumptions: { title: 'Assumption Tables', eyebrow: 'Table editor · versioned dimensions', description: 'Inspect effective dates, slices, sparse behavior, and selected assumption cells.' }, factors: { title: 'Factor Tables', eyebrow: 'Reference table · needs review', description: 'Review surrender and mortality factors with issue-level validation.' }, projection: { title: 'Projection Inputs', eyebrow: 'Projection scope · Q4 valuation controls', description: 'Set projection-level controls without mutating the shared input library.' }, validation: { title: 'Validation Center', eyebrow: 'Cross-workspace issue queue', description: 'Jump from validation issues to the exact input, mapping, or projection control.' } }; const item = config[view as Exclude<InputView, 'library'>]; return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Input Library</button><div className="panel detail-workspace-head"><div><span className="eyebrow">{item.eyebrow}</span><h2 className="section-title">{item.title}</h2><p className="muted">{item.description}</p></div><StatusBadge status={view === 'factors' ? 'Needs review' : 'Validated'} /></div>{view === 'validation' ? <ValidationIssues onOpen={(target) => onNavigate(target === 'Factor Tables' ? 'factors' : target === 'Asset Inforce' ? 'asset' : 'projection')} /> : <InputEditor view={view} onNotify={notify} />}</div> }
|
| 14 |
|
| 15 |
function InputEditor({ view, onNotify }: { view: InputView; onNotify: (message: string) => void }) { const isTable = view === 'assumptions' || view === 'factors'; return <div className="input-detail-grid"><div className="panel"><div className="panel-title-row"><h2 className="section-title">{isTable ? 'Dimension and slice preview' : 'Source and mapping preview'}</h2><button className="view-all" onClick={() => onNotify('Import wizard opened')}><Upload size={14} />Import new version</button></div><div className="input-preview-grid">{(isTable ? ['Effective date','Scenario','Age band','Policy year','Cell value','Sparse cells'] : ['Source file','Imported at','Records','Segments / LOB','Existing business','Future purchases']).map((label,index) => <span key={label}><small>{label}</small><b>{['2026-12-31','Base scenario','All bands','Year 5','4.25%','18 cells'][index] ?? 'MYGA liability file'}</b></span>)}</div>{isTable ? <div className="factor-grid">{['Year','Base','Low rate','High rate'].map(label => <b key={label}>{label}</b>)}{['1','2','3','4','5','6'].flatMap(year => [<span key={`${year}-a`}>{year}</span>,<span key={`${year}-b`}>4.10%</span>,<span key={`${year}-c`}>4.25%</span>,<span key={`${year}-d`}>4.55%</span>])}</div> : <div className="mapping-flow"><span><FileText size={18} />Source records<b>24,810</b></span><ArrowRight size={18} /><span><Layers3 size={18} />Mapped segments<b>12 / 12</b></span><ArrowRight size={18} /><span><Database size={18} />Projection inputs<b>Ready</b></span></div>}</div><aside className="panel"><span className="eyebrow">Validation and provenance</span><h2 className="section-title">Checks</h2><div className="check-list"><span><Check size={15} />Schema compatible</span><span><Check size={15} />Effective dates covered</span><span className={view === 'factors' ? 'check-warning' : ''}>{view === 'factors' ? <AlertTriangle size={15} /> : <Check size={15} />} {view === 'factors' ? '3 cells need review' : 'No blocking issues'}</span></div><button className="button button-primary full-button" onClick={() => onNotify('Input changes saved')}>Save and validate <Check size={15} /></button><button className="button button-secondary full-button" onClick={() => onNotify('Mentor context opened')}>Ask Mentor <ArrowRight size={15} /></button></aside></div> }
|
| 16 |
|
| 17 |
function ValidationIssues({ onOpen }: { onOpen: (target: 'Factor Tables' | 'Asset Inforce' | 'Projection Inputs') => void }) { return <div className="panel validation-issues"><div className="panel-title-row"><div><h2 className="section-title">Open issues</h2><p className="muted">Warnings are non-blocking; resolve before publishing the next Run Set.</p></div><button className="button button-primary" onClick={() => onOpen('Factor Tables')}><Check size={15} />Run validation</button></div>{[['Factor Tables','Three lapse-rate cells are outside the expected calibration range.','Needs review'],['Asset Inforce','One portfolio mapping uses an older source version.','Needs review'],['Projection Inputs','Trace capture scope is not explicitly selected.','Needs review']].map(([title,body,status]) => <button className="issue-row" key={title} onClick={() => onOpen(title as 'Factor Tables' | 'Asset Inforce' | 'Projection Inputs')}><AlertTriangle size={18} /><span><b>{title}</b><small>{body}</small></span><StatusBadge status={status as 'Needs review'} /><ArrowRight size={16} /></button>)}</div> }
|
|
|
|
|
|
|
|
|
| 5 |
import { SummaryTile } from '../components/ui/SummaryTile'
|
| 6 |
import { WorkspaceToolbar } from '../components/ui/WorkspaceToolbar'
|
| 7 |
|
| 8 |
+
type InputView = 'library' | 'liability' | 'asset' | 'assumptions' | 'factors' | 'projection' | 'validation' | 'mappings'
|
| 9 |
const inputRows = [['Liability Inforce','MYGA liability file','v2026.12','24,810 records','Validated'],['Asset Inforce','MYGA asset holdings','v2026.11','8,402 records','Validated'],['Assumption Tables','MYGA reserve assumptions','v3.2','18 dimensions','Validated'],['Factor Tables','Surrender and mortality factors','v1.8','42,116 cells','Needs review'],['Projection Inputs','Q4 valuation controls','v2026.12','26 fields','Validated']]
|
| 10 |
|
| 11 |
+
export function InputLibraryPage() { const [view, setView] = useState<InputView>('library'); const select = (next: InputView) => setView(next); if (view !== 'library') return <InputDetailView view={view} onBack={() => select('library')} onNavigate={select} />; return <div className="workspace-stack"><WorkspaceToolbar searchPlaceholder="Search input libraries, tables, mappings..." action="Import input" actionIcon={<Upload size={15} />} /><div className="input-subnav">{[['library','Input Library'],['mappings','Mappings'],['liability','Liability Inforce'],['asset','Asset Inforce'],['assumptions','Assumption Tables'],['factors','Factor Tables'],['projection','Projection Inputs'],['validation','Validation Center']].map(([key,label]) => <button className={view === key ? 'input-subnav-active' : ''} key={key} onClick={() => select(key as InputView)}>{label}</button>)}</div><div className="model-summary-grid"><SummaryTile icon={<Database size={18} />} label="Input objects" value="24" tone="dark" /><SummaryTile icon={<Check size={18} />} label="Validated" value="19" /><SummaryTile icon={<AlertTriangle size={18} />} label="Needs review" value="3" /><SummaryTile icon={<Link2 size={18} />} label="Mappings" value="42" /></div><div className="panel table-panel"><div className="panel-title-row"><div><span className="eyebrow">MYGA · Model v3.2 · GAAP</span><h2 className="section-title">Input Library</h2><p className="muted">Versioned input objects connected to the active model and projection scope.</p></div><button className="view-all" onClick={() => select('validation')}><ShieldCheck size={14} />Validation Center</button></div><div className="input-table">{inputRows.map(([name,detail,version,count,status]) => <button className="input-row" key={name} onClick={() => select(name === 'Liability Inforce' ? 'liability' : name === 'Asset Inforce' ? 'asset' : name === 'Assumption Tables' ? 'assumptions' : name === 'Factor Tables' ? 'factors' : 'projection')}><span className="model-name"><span className="model-icon"><Database size={16} /></span><span><b>{name}</b><small>{detail}</small></span></span><span>{version}</span><span>MYGA · GAAP</span><span>{count}</span><span><StatusBadge status={status as 'Validated' | 'Needs review'} /></span><ArrowRight size={17} /></button>)}</div></div><div className="workspace-callout"><ShieldCheck size={20} /><span><b>3 issues need review</b><small>All blocking mappings are complete. Review factor-table warnings before the next projection run.</small></span><button onClick={() => select('validation')}>Review issues <ArrowRight size={15} /></button></div></div> }
|
| 12 |
|
| 13 |
+
function InputDetailView({ view, onBack, onNavigate }: { view: InputView; onBack: () => void; onNavigate: (view: InputView) => void }) { const { notify } = useApp(); const config: Record<Exclude<InputView,'library'>, { title: string; eyebrow: string; description: string }> = { liability: { title: 'Liability Inforce', eyebrow: 'Input object · imported detail', description: 'Preview records, map segments and confirm existing versus future-purchase scope.' }, asset: { title: 'Asset Inforce', eyebrow: 'Input object · imported detail', description: 'Review holdings, portfolio mapping, and downstream asset cash-flow consumers.' }, assumptions: { title: 'Assumption Tables', eyebrow: 'Table editor · versioned dimensions', description: 'Inspect effective dates, slices, sparse behavior, and selected assumption cells.' }, factors: { title: 'Factor Tables', eyebrow: 'Reference table · needs review', description: 'Review surrender and mortality factors with issue-level validation.' }, projection: { title: 'Projection Inputs', eyebrow: 'Projection scope · Q4 valuation controls', description: 'Set projection-level controls without mutating the shared input library.' }, validation: { title: 'Validation Center', eyebrow: 'Cross-workspace issue queue', description: 'Jump from validation issues to the exact input, mapping, or projection control.' }, mappings: { title: 'Input Mappings', eyebrow: 'Mapping workspace · source to model', description: 'Review source fields, target variables, transformations, and validation state.' } }; const item = config[view as Exclude<InputView, 'library'>]; return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Input Library</button><div className="panel detail-workspace-head"><div><span className="eyebrow">{item.eyebrow}</span><h2 className="section-title">{item.title}</h2><p className="muted">{item.description}</p></div><StatusBadge status={view === 'factors' ? 'Needs review' : 'Validated'} /></div>{view === 'validation' ? <ValidationIssues onOpen={(target) => onNavigate(target === 'Factor Tables' ? 'factors' : target === 'Asset Inforce' ? 'asset' : 'projection')} /> : view === 'mappings' ? <MappingWorkspace onNotify={notify} /> : <InputEditor view={view} onNotify={notify} />}</div> }
|
| 14 |
|
| 15 |
function InputEditor({ view, onNotify }: { view: InputView; onNotify: (message: string) => void }) { const isTable = view === 'assumptions' || view === 'factors'; return <div className="input-detail-grid"><div className="panel"><div className="panel-title-row"><h2 className="section-title">{isTable ? 'Dimension and slice preview' : 'Source and mapping preview'}</h2><button className="view-all" onClick={() => onNotify('Import wizard opened')}><Upload size={14} />Import new version</button></div><div className="input-preview-grid">{(isTable ? ['Effective date','Scenario','Age band','Policy year','Cell value','Sparse cells'] : ['Source file','Imported at','Records','Segments / LOB','Existing business','Future purchases']).map((label,index) => <span key={label}><small>{label}</small><b>{['2026-12-31','Base scenario','All bands','Year 5','4.25%','18 cells'][index] ?? 'MYGA liability file'}</b></span>)}</div>{isTable ? <div className="factor-grid">{['Year','Base','Low rate','High rate'].map(label => <b key={label}>{label}</b>)}{['1','2','3','4','5','6'].flatMap(year => [<span key={`${year}-a`}>{year}</span>,<span key={`${year}-b`}>4.10%</span>,<span key={`${year}-c`}>4.25%</span>,<span key={`${year}-d`}>4.55%</span>])}</div> : <div className="mapping-flow"><span><FileText size={18} />Source records<b>24,810</b></span><ArrowRight size={18} /><span><Layers3 size={18} />Mapped segments<b>12 / 12</b></span><ArrowRight size={18} /><span><Database size={18} />Projection inputs<b>Ready</b></span></div>}</div><aside className="panel"><span className="eyebrow">Validation and provenance</span><h2 className="section-title">Checks</h2><div className="check-list"><span><Check size={15} />Schema compatible</span><span><Check size={15} />Effective dates covered</span><span className={view === 'factors' ? 'check-warning' : ''}>{view === 'factors' ? <AlertTriangle size={15} /> : <Check size={15} />} {view === 'factors' ? '3 cells need review' : 'No blocking issues'}</span></div><button className="button button-primary full-button" onClick={() => onNotify('Input changes saved')}>Save and validate <Check size={15} /></button><button className="button button-secondary full-button" onClick={() => onNotify('Mentor context opened')}>Ask Mentor <ArrowRight size={15} /></button></aside></div> }
|
| 16 |
|
| 17 |
function ValidationIssues({ onOpen }: { onOpen: (target: 'Factor Tables' | 'Asset Inforce' | 'Projection Inputs') => void }) { return <div className="panel validation-issues"><div className="panel-title-row"><div><h2 className="section-title">Open issues</h2><p className="muted">Warnings are non-blocking; resolve before publishing the next Run Set.</p></div><button className="button button-primary" onClick={() => onOpen('Factor Tables')}><Check size={15} />Run validation</button></div>{[['Factor Tables','Three lapse-rate cells are outside the expected calibration range.','Needs review'],['Asset Inforce','One portfolio mapping uses an older source version.','Needs review'],['Projection Inputs','Trace capture scope is not explicitly selected.','Needs review']].map(([title,body,status]) => <button className="issue-row" key={title} onClick={() => onOpen(title as 'Factor Tables' | 'Asset Inforce' | 'Projection Inputs')}><AlertTriangle size={18} /><span><b>{title}</b><small>{body}</small></span><StatusBadge status={status as 'Needs review'} /><ArrowRight size={16} /></button>)}</div> }
|
| 18 |
+
|
| 19 |
+
function MappingWorkspace({ onNotify }: { onNotify: (message: string) => void }) { const rows = [['policy_id','policy_id','Direct','Validated'],['premium_amount','premium_t','Currency normalization','Validated'],['lapse_band','lapse_rate','Band lookup · v1.8','Needs review'],['asset_market_value','asset_value','Portfolio mapping · v2026.11','Validated']]; return <div className="panel"><div className="panel-title-row"><div><h2 className="section-title">Source-to-model mappings</h2><p className="muted">Every mapping retains source field, target variable, transformation, and validation provenance.</p></div><button className="button button-primary" onClick={() => onNotify('Mapping validation completed')}><ShieldCheck size={15} />Validate mappings</button></div><div className="input-table"><div className="input-table-head"><span>Source field</span><span>Target variable</span><span>Transformation</span><span>Status</span><span /></div>{rows.map(row => <button className="input-row" key={row[0]} onClick={() => onNotify(`${row[0]} mapping selected`)}><span><b>{row[0]}</b></span><span>{row[1]}</span><span>{row[2]}</span><span><StatusBadge status={row[3] as 'Validated' | 'Needs review'} /></span><ArrowRight size={16} /></button>)}</div></div> }
|
src/pages/ModelWorkspaceScreens.tsx
CHANGED
|
@@ -1,13 +1,15 @@
|
|
| 1 |
import { useState, type ReactNode } from 'react'
|
| 2 |
import { ArrowLeft, ArrowRight, Box, Check, ChevronRight, Code2, Database, FileOutput, GitBranch, Layers3, Link2, Network, ShieldCheck, Workflow } from 'lucide-react'
|
| 3 |
import { StatusBadge } from '../components/ui/StatusBadge'
|
| 4 |
-
import {
|
| 5 |
-
import {
|
| 6 |
|
| 7 |
-
type ModelView = 'workspace' | 'basis' | 'interface' | 'mapping' | 'cross-trace'
|
| 8 |
-
export function ModelWorkspacePage({ onBack }: { onBack: () => void }) { const [view,setView] = useState<ModelView>('workspace'); if(view === 'basis') return <BasisDetail onBack={() => setView('workspace')} />; if(view === 'interface') return <ModelInterface onBack={() => setView('workspace')} onMapping={() => setView('mapping')} />; if(view === 'mapping') return <OutputMappingPage onBack={() => setView('interface')} onTrace={() => setView('cross-trace')} />; if(view === 'cross-trace') return <CrossModelTracePage onBack={() => setView('mapping')} />; return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Model Library</button><div className="panel model-workspace-hero"><div><span className="eyebrow">Model Workspace · active context</span><h2 className="section-title">MYGA Reserve Model</h2><p className="muted">Model v3.2 · MYGA · GAAP Reserve Base · active production context</p></div><StatusBadge status="Validated" /></div><div className="workspace-tabs"><button className="workspace-tab-active">Hierarchy</button><button onClick={() => setView('basis')}>Block / Profile / Basis</button><button onClick={() => setView('interface')}>Published Outputs</button></div><div className="model-hierarchy-grid"><div className="panel hierarchy-panel"><div className="panel-title-row"><div><span className="eyebrow">Structure</span><h2 className="section-title">Model hierarchy</h2></div><button className="view-all"><ShieldCheck size={14} />Validated</button></div><TreeRow icon={<Box size={16} />} label="MYGA Reserve Model" meta="Model · v3.2" active depth={0} /><TreeRow icon={<Layers3 size={16} />} label="MYGA" meta="Domain / product" depth={1} /><TreeRow icon={<Workflow size={16} />} label="GAAP Reserve Base" meta="Block · profile · basis" depth={2} onClick={() => setView('basis')} /><TreeRow icon={<GitBranch size={16} />} label="Universal reserve logic" meta="Formula Group · Shared · v4.1" depth={3} /><TreeRow icon={<GitBranch size={16} />} label="Mortality & lapse" meta="Formula Group · Inherited · v3.8" depth={3} /><TreeRow icon={<Database size={16} />} label="Inputs and assumptions" meta="5 linked objects" depth={2} /><TreeRow icon={<FileOutput size={16} />} label="Published outputs" meta="5 interface variables" depth={2} onClick={() => setView('interface')} /></div><aside className="panel model-context-panel"><span className="eyebrow">Model context</span><h2 className="section-title">What this model owns</h2><ContextFact icon={<Workflow size={15} />} label="Logic" value="3 formula groups · 113 formulas" /><ContextFact icon={<Database size={15} />} label="Inputs" value="24 objects · 42 mappings" /><ContextFact icon={<FileOutput size={15} />} label="Outputs" value="5 published variables" /><ContextFact icon={<Network size={15} />} label="Consumers" value="3 Projection Sets · 18 runs" /><button className="button button-primary full-button" onClick={() => setView('basis')}>Inspect active basis <ArrowRight size={15} /></button></aside></div></div> }
|
| 9 |
function TreeRow({ icon, label, meta, depth, active, onClick }: { icon: ReactNode; label: string; meta: string; depth: number; active?: boolean; onClick?: () => void }) { return <button className={`tree-row ${active ? 'tree-row-active' : ''}`} style={{ paddingLeft: `${14 + depth * 22}px` }} onClick={onClick}><ChevronRight size={13} /><span className="tree-row-icon">{icon}</span><span><b>{label}</b><small>{meta}</small></span>{onClick && <ArrowRight size={14} />}</button> }
|
| 10 |
function ContextFact({ icon, label, value }: { icon: ReactNode; label: string; value: string }) { return <div className="context-fact"><span>{icon}</span><span><small>{label}</small><b>{value}</b></span></div> }
|
| 11 |
-
function BasisDetail({ onBack }: { onBack: () => void
|
| 12 |
function CompositionRow({ state, title, detail }: { state: 'Shared' | 'Inherited' | 'Overridden'; title: string; detail: string }) { return <button className="composition-row"><span className="composition-icon"><GitBranch size={15} /></span><span><b>{title}</b><small>{detail}</small></span><StatusBadge status={state} /><ArrowRight size={14} /></button> }
|
| 13 |
function ModelInterface({ onBack, onMapping }: { onBack: () => void; onMapping: () => void }) { const outputs = [['net_reserve','Currency','Projection year × scenario','3 consumers'],['reserve_change_pct','Percent','Projection year × scenario','5 consumers'],['policy_cash_flow','Currency','Policy year × scenario','2 consumers'],['lapse_rate','Percent','Policy year × band','4 consumers'],['expense_load','Currency','Projection year × scenario','2 consumers']]; return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Model Workspace</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Model Interface / Published Outputs</span><h2 className="section-title">MYGA Reserve Model outputs</h2><p className="muted">Named variables that downstream Projection Sets and models can consume with versioned contracts.</p></div><StatusBadge status="Validated" /></div><div className="panel interface-table"><div className="interface-table-head"><span>Published variable</span><span>Type</span><span>Dimension</span><span>Consumers</span><span /></div>{outputs.map(row => <button className="interface-row" key={row[0]} onClick={onMapping}><span><Code2 size={15} /><b>{row[0]}</b></span><span>{row[1]}</span><span>{row[2]}</span><span>{row[3]}</span><ArrowRight size={15} /></button>)}</div><div className="workspace-callout"><Check size={19} /><span><b>Interface contract validated</b><small>All published variables have schema, unit, dimension, producer version, and consumer references.</small></span><button onClick={onMapping}>Open output mapping <ArrowRight size={15} /></button></div></div> }
|
|
|
|
|
|
|
|
|
| 1 |
import { useState, type ReactNode } from 'react'
|
| 2 |
import { ArrowLeft, ArrowRight, Box, Check, ChevronRight, Code2, Database, FileOutput, GitBranch, Layers3, Link2, Network, ShieldCheck, Workflow } from 'lucide-react'
|
| 3 |
import { StatusBadge } from '../components/ui/StatusBadge'
|
| 4 |
+
import { CrossModelTracePage, OutputMappingPage, RunLinkagePage } from './CrossModelScreens'
|
| 5 |
+
import { InheritanceWizard } from './FormulaScreens'
|
| 6 |
|
| 7 |
+
type ModelView = 'workspace' | 'basis' | 'interface' | 'mapping' | 'cross-trace' | 'linkage' | 'wizard' | 'relationships'
|
| 8 |
+
export function ModelWorkspacePage({ onBack }: { onBack: () => void }) { const [view,setView] = useState<ModelView>('workspace'); if(view === 'basis') return <BasisDetail onBack={() => setView('workspace')} onWizard={() => setView('wizard')} onRelationships={() => setView('relationships')} />; if(view === 'interface') return <ModelInterface onBack={() => setView('workspace')} onMapping={() => setView('mapping')} />; if(view === 'mapping') return <OutputMappingPage onBack={() => setView('interface')} onTrace={() => setView('cross-trace')} onLinkage={() => setView('linkage')} />; if(view === 'cross-trace') return <CrossModelTracePage onBack={() => setView('mapping')} />; if(view === 'linkage') return <RunLinkagePage onBack={() => setView('mapping')} />; if(view === 'wizard') return <InheritanceWizard onBack={() => setView('basis')} onSave={() => setView('basis')} />; if(view === 'relationships') return <RelationshipMap onBack={() => setView('basis')} />; return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Model Library</button><div className="panel model-workspace-hero"><div><span className="eyebrow">Model Workspace · active context</span><h2 className="section-title">MYGA Reserve Model</h2><p className="muted">Model v3.2 · MYGA · GAAP Reserve Base · active production context</p></div><StatusBadge status="Validated" /></div><div className="workspace-tabs"><button className="workspace-tab-active">Hierarchy</button><button onClick={() => setView('basis')}>Block / Profile / Basis</button><button onClick={() => setView('interface')}>Published Outputs</button></div><div className="model-hierarchy-grid"><div className="panel hierarchy-panel"><div className="panel-title-row"><div><span className="eyebrow">Structure</span><h2 className="section-title">Model hierarchy</h2></div><button className="view-all"><ShieldCheck size={14} />Validated</button></div><TreeRow icon={<Box size={16} />} label="MYGA Reserve Model" meta="Model · v3.2" active depth={0} /><TreeRow icon={<Layers3 size={16} />} label="MYGA" meta="Domain / product" depth={1} /><TreeRow icon={<Workflow size={16} />} label="GAAP Reserve Base" meta="Block · profile · basis" depth={2} onClick={() => setView('basis')} /><TreeRow icon={<GitBranch size={16} />} label="Universal reserve logic" meta="Formula Group · Shared · v4.1" depth={3} /><TreeRow icon={<GitBranch size={16} />} label="Mortality & lapse" meta="Formula Group · Inherited · v3.8" depth={3} /><TreeRow icon={<Database size={16} />} label="Inputs and assumptions" meta="5 linked objects" depth={2} /><TreeRow icon={<FileOutput size={16} />} label="Published outputs" meta="5 interface variables" depth={2} onClick={() => setView('interface')} /></div><aside className="panel model-context-panel"><span className="eyebrow">Model context</span><h2 className="section-title">What this model owns</h2><ContextFact icon={<Workflow size={15} />} label="Logic" value="3 formula groups · 113 formulas" /><ContextFact icon={<Database size={15} />} label="Inputs" value="24 objects · 42 mappings" /><ContextFact icon={<FileOutput size={15} />} label="Outputs" value="5 published variables" /><ContextFact icon={<Network size={15} />} label="Consumers" value="3 Projection Sets · 18 runs" /><button className="button button-primary full-button" onClick={() => setView('basis')}>Inspect active basis <ArrowRight size={15} /></button></aside></div></div> }
|
| 9 |
function TreeRow({ icon, label, meta, depth, active, onClick }: { icon: ReactNode; label: string; meta: string; depth: number; active?: boolean; onClick?: () => void }) { return <button className={`tree-row ${active ? 'tree-row-active' : ''}`} style={{ paddingLeft: `${14 + depth * 22}px` }} onClick={onClick}><ChevronRight size={13} /><span className="tree-row-icon">{icon}</span><span><b>{label}</b><small>{meta}</small></span>{onClick && <ArrowRight size={14} />}</button> }
|
| 10 |
function ContextFact({ icon, label, value }: { icon: ReactNode; label: string; value: string }) { return <div className="context-fact"><span>{icon}</span><span><small>{label}</small><b>{value}</b></span></div> }
|
| 11 |
+
function BasisDetail({ onBack, onWizard, onRelationships }: { onBack: () => void; onWizard: () => void; onRelationships: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Model Workspace</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Block / Profile / Basis detail</span><h2 className="section-title">GAAP Reserve Base</h2><p className="muted">The active basis composes shared and inherited logic for the MYGA reserve model.</p></div><StatusBadge status="Validated" /></div><div className="basis-detail-grid"><div className="panel"><h2 className="section-title">Logic composition</h2><CompositionRow state="Shared" title="Universal reserve logic" detail="42 formulas · source group v4.1" /><CompositionRow state="Inherited" title="Mortality & lapse" detail="31 formulas · inherited from Core Actuarial" /><CompositionRow state="Overridden" title="Expense & fees" detail="22 formulas · local override retained" /><button className="button button-secondary full-button" onClick={onWizard}><GitBranch size={15} />Inherit from another group</button></div><aside className="panel"><h2 className="section-title">Basis metadata</h2><ContextFact icon={<Box size={15} />} label="Product" value="MYGA" /><ContextFact icon={<ShieldCheck size={15} />} label="Validation" value="All checks passed" /><ContextFact icon={<Link2 size={15} />} label="Projection consumers" value="3 active" /><button className="button button-primary full-button" onClick={onRelationships}>Open relationship map <Network size={15} /></button></aside></div></div> }
|
| 12 |
function CompositionRow({ state, title, detail }: { state: 'Shared' | 'Inherited' | 'Overridden'; title: string; detail: string }) { return <button className="composition-row"><span className="composition-icon"><GitBranch size={15} /></span><span><b>{title}</b><small>{detail}</small></span><StatusBadge status={state} /><ArrowRight size={14} /></button> }
|
| 13 |
function ModelInterface({ onBack, onMapping }: { onBack: () => void; onMapping: () => void }) { const outputs = [['net_reserve','Currency','Projection year × scenario','3 consumers'],['reserve_change_pct','Percent','Projection year × scenario','5 consumers'],['policy_cash_flow','Currency','Policy year × scenario','2 consumers'],['lapse_rate','Percent','Policy year × band','4 consumers'],['expense_load','Currency','Projection year × scenario','2 consumers']]; return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Model Workspace</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Model Interface / Published Outputs</span><h2 className="section-title">MYGA Reserve Model outputs</h2><p className="muted">Named variables that downstream Projection Sets and models can consume with versioned contracts.</p></div><StatusBadge status="Validated" /></div><div className="panel interface-table"><div className="interface-table-head"><span>Published variable</span><span>Type</span><span>Dimension</span><span>Consumers</span><span /></div>{outputs.map(row => <button className="interface-row" key={row[0]} onClick={onMapping}><span><Code2 size={15} /><b>{row[0]}</b></span><span>{row[1]}</span><span>{row[2]}</span><span>{row[3]}</span><ArrowRight size={15} /></button>)}</div><div className="workspace-callout"><Check size={19} /><span><b>Interface contract validated</b><small>All published variables have schema, unit, dimension, producer version, and consumer references.</small></span><button onClick={onMapping}>Open output mapping <ArrowRight size={15} /></button></div></div> }
|
| 14 |
+
|
| 15 |
+
function RelationshipMap({ onBack }: { onBack: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Basis detail</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Formula relationship map</span><h2 className="section-title">GAAP Reserve Base usage map</h2><p className="muted">Follow shared, inherited, overridden, and published relationships without leaving the model architecture context.</p></div><StatusBadge status="Validated" /></div><div className="inheritance-map"><span className="map-node"><GitBranch size={16} /><b>Universal reserve logic</b><small>Shared · v4.1 · 42 formulas</small></span><ArrowRight size={20} /><span className="map-node"><GitBranch size={16} /><b>GAAP Reserve Base</b><small>Basis · 3 formula groups</small></span><ArrowRight size={20} /><span className="map-node"><Code2 size={16} /><b>reserve_t</b><small>Formula · 8 consumers</small></span><ArrowRight size={20} /><span className="map-node"><FileOutput size={16} /><b>net_reserve</b><small>Published output · 3 consumers</small></span></div><div className="panel"><h2 className="section-title">Relationship checks</h2><div className="check-list"><span><Check size={15} />Source version retained</span><span><Check size={15} />Local overrides visible</span><span><Check size={15} />Downstream consumers linked</span></div></div></div> }
|
src/pages/ModelsPage.tsx
CHANGED
|
@@ -20,7 +20,7 @@ export function ModelsPage() {
|
|
| 20 |
const [selected, setSelected] = useState<Model | null>(null)
|
| 21 |
const [workspaceOpen, setWorkspaceOpen] = useState(false)
|
| 22 |
if (workspaceOpen) return <ModelWorkspacePage onBack={() => setWorkspaceOpen(false)} />
|
| 23 |
-
return <div className="models-reference"><div className="model-tabs"><button className="model-tab model-tab-active">All <b>
|
| 24 |
}
|
| 25 |
|
| 26 |
function SummaryCard({ dark, icon, label, value, foot, extra }: { dark?: boolean; icon: ReactNode; label: string; value: string; foot: string; extra?: string }) { return <div className={`reference-summary-card ${dark ? 'reference-summary-dark' : ''}`}><span className="reference-summary-icon">{icon}</span><div><small>{label}</small><strong>{value}</strong><span>{extra && <em>{extra}</em>}{foot}</span></div></div> }
|
|
|
|
| 20 |
const [selected, setSelected] = useState<Model | null>(null)
|
| 21 |
const [workspaceOpen, setWorkspaceOpen] = useState(false)
|
| 22 |
if (workspaceOpen) return <ModelWorkspacePage onBack={() => setWorkspaceOpen(false)} />
|
| 23 |
+
return <div className="models-reference"><div className="model-tabs"><button className="model-tab model-tab-active">All <b>5</b></button><button className="model-tab">Owned <b>4</b></button><button className="model-tab">Shared <b>8</b></button><div className="model-filter-actions"><button className="model-product-filter" onClick={() => notify('Product filter opened')}>All products <ChevronDown size={16} /></button><button className="square-control" onClick={() => notify('Model filters opened')}><Filter size={18} /></button></div></div><div className={`models-content-grid ${selected ? 'has-model-detail' : 'models-content-grid-full'}`}><div className="models-main-column"><div className="model-summary-reference"><SummaryCard dark icon={<Box size={24} />} label="Total models" value="12" foot="Across all products" extra="↑ 2 new" /><SummaryCard icon={<UserRound size={24} />} label="Owned by you" value="4" foot="33% of total" /><SummaryCard icon={<UsersRound size={24} />} label="Shared with you" value="8" foot="67% of total" /></div><div className="models-list-header"><h2>Models <span>(5)</span></h2><div><button className="sort-button" onClick={() => notify('Sort options opened')}><ArrowDownUp size={16} />Last updated <ChevronDown size={15} /></button><button className="square-control view-selected" onClick={() => notify('Grid view selected')}><Grid2X2 size={16} /></button><button className="square-control view-selected" onClick={() => notify('List view selected')}><Archive size={16} /></button></div></div><div className="reference-model-list">{libraryModels.map(model => <ModelListRow key={model.name} model={model} selected={selected?.name === model.name} onSelect={() => setSelected(model)} onOpen={() => setWorkspaceOpen(true)} />)}</div><div className="model-list-foot">5 of 5 models · select Open model to enter the architecture workspace</div></div>{selected && <ModelDetail model={selected} notify={notify} onClose={() => setSelected(null)} onOpen={() => setWorkspaceOpen(true)} />}</div></div>
|
| 24 |
}
|
| 25 |
|
| 26 |
function SummaryCard({ dark, icon, label, value, foot, extra }: { dark?: boolean; icon: ReactNode; label: string; value: string; foot: string; extra?: string }) { return <div className={`reference-summary-card ${dark ? 'reference-summary-dark' : ''}`}><span className="reference-summary-icon">{icon}</span><div><small>{label}</small><strong>{value}</strong><span>{extra && <em>{extra}</em>}{foot}</span></div></div> }
|
src/pages/ProjectionScreens.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import { WorkspaceToolbar } from '../components/ui/WorkspaceToolbar'
|
|
| 6 |
|
| 7 |
type ProjectionView = 'library' | 'editor' | 'validation'
|
| 8 |
const projectionSets = [['MYGA GAAP Reserve','v1.0','Base scenario','2026–2045','Ready'],['MYGA Low Rate Sensitivity','v0.8','Low Interest Rate','2026–2045','Needs review'],['FIA VM-21 Deterministic','v2.3','VM-21 Deterministic','2026–2045','Ready']]
|
| 9 |
-
export function ProjectionLibraryPage() { const { notify } = useApp(); const [view,setView] = useState<ProjectionView>('library'); if(view === 'editor') return <ProjectionEditor onBack={() => setView('library')} onSave={() => { setView('library'); notify('Projection Set saved') }} />; if(view === 'validation') return <ProjectionValidation onBack={() => setView('library')} />; return <div className="workspace-stack"><WorkspaceToolbar searchPlaceholder="Search Projection Sets by model, purpose, scenario..." action="New Projection Set" actionIcon={<Plus size={15} />} onAction={() => setView('editor')} /><div className="projection-subnav"><button className="projection-subnav-active">Projection Set Library</button><button onClick={() => setView('editor')}>Create / Edit</button><button onClick={() => setView('validation')}>Validation</button></div><div className="panel table-panel"><div className="panel-title-row"><div><span className="eyebrow">Execution preparation</span><h2 className="section-title">Projection Set Library</h2><p className="muted">Reusable configurations define exactly what the model calculates before a Run Set is created.</p></div><button className="view-all" onClick={() => setView('validation')}><ShieldCheck size={14} />Validation</button></div><div className="projection-cards">{projectionSets.map(([name,version,scenario,horizon,status]) => <button className="projection-card" key={name} onClick={() => setView('editor')}><div className="projection-card-head"><span className="projection-icon"><LineChart size={17} /></span><StatusBadge status={status as 'Validated' | 'Needs review'} /></div><b>{name}</b><small>{version} · {scenario}</small><div className="projection-meta"><span><CalendarDays size={13} />{horizon}</span><span><Database size={13} />4 inputs</span><span><Workflow size={13} />42 formulas</span></div><div className="projection-footer"><span>Output capture complete</span><ArrowRight size={16} /></div></button>)}</div></div><div className="workspace-callout"><Check size={20} /><span><b>Projection Set readiness</b><small>MYGA GAAP Reserve is compatible with Model v3.2, Base scenario, and the current input library.</small></span><button onClick={() =>
|
| 10 |
-
function ProjectionEditor({ onBack, onSave }: { onBack: () => void; onSave: () => void }) { const { notify } = useApp(); return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Projection Set Library</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Projection Set · create / edit</span><h2 className="section-title">MYGA GAAP Reserve</h2><p className="muted">Define model, basis, inputs, scenario subset, projection keys, outputs, and trace capture.</p></div><StatusBadge status="Validated" /></div><div className="projection-editor-grid"><div className="panel"><ProjectionEditorRow icon={<Database size={16} />} title="Model / block / basis" value="MYGA Reserve Model · Base · GAAP" /><ProjectionEditorRow icon={<Database size={16} />} title="Liabilities and assets" value="Liability Inforce · Asset Inforce" /><ProjectionEditorRow icon={<GitBranch size={16} />} title="Assumptions and scenarios" value="MYGA assumptions · Base scenario" /><ProjectionEditorRow icon={<LineChart size={16} />} title="Projection characteristics" value="2026–2045 · Policy year · Monthly" /><ProjectionEditorRow icon={<Workflow size={16} />} title="Output capture and trace scope" value="Net reserve · cash flows · lineage enabled" /></div><aside className="panel"><h2 className="section-title">Validation</h2><div className="preflight-list"><span><Check size={15} />All required inputs mapped</span><span><Check size={15} />Scenario subset compatible</span><span><Check size={15} />Projection keys selected</span><span><Check size={15} />Output capture configured</span></div><button className="button button-primary full-button" onClick={onSave}><Check size={15} />Save Projection Set</button><button className="button button-secondary full-button" onClick={() => notify('Projection Set duplicated')}>Duplicate version</button></aside></div></div> }
|
| 11 |
-
function ProjectionEditorRow({ icon, title, value }: { icon: React.ReactNode; title: string; value: string }) { return <button className=
|
| 12 |
function ProjectionValidation({ onBack }: { onBack: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Projection Set Library</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Projection Set validation</span><h2 className="section-title">MYGA GAAP Reserve readiness</h2><p className="muted">Confirm compatibility before attaching the Projection Set to a Run Set.</p></div><StatusBadge status="Validated" /></div><div className="validation-issues"><div className="panel-title-row"><h2 className="section-title">Validation checks</h2><StatusBadge status="Validated" /></div>{['Model v3.2 and GAAP basis are compatible','All input versions are pinned','Base scenario has a complete horizon','Trace and report capture scopes are configured'].map(check => <div className="issue-row" key={check}><Check size={18} color="#6fa426" /><span><b>{check}</b><small>Validated for current active context</small></span><StatusBadge status="Validated" /><ArrowRight size={16} /></div>)}</div></div> }
|
|
|
|
| 6 |
|
| 7 |
type ProjectionView = 'library' | 'editor' | 'validation'
|
| 8 |
const projectionSets = [['MYGA GAAP Reserve','v1.0','Base scenario','2026–2045','Ready'],['MYGA Low Rate Sensitivity','v0.8','Low Interest Rate','2026–2045','Needs review'],['FIA VM-21 Deterministic','v2.3','VM-21 Deterministic','2026–2045','Ready']]
|
| 9 |
+
export function ProjectionLibraryPage() { const { notify, setActiveScreen } = useApp(); const [view,setView] = useState<ProjectionView>('library'); if(view === 'editor') return <ProjectionEditor onBack={() => setView('library')} onSave={() => { setView('library'); notify('Projection Set saved') }} />; if(view === 'validation') return <ProjectionValidation onBack={() => setView('library')} />; return <div className="workspace-stack"><WorkspaceToolbar searchPlaceholder="Search Projection Sets by model, purpose, scenario..." action="New Projection Set" actionIcon={<Plus size={15} />} onAction={() => setView('editor')} /><div className="projection-subnav"><button className="projection-subnav-active">Projection Set Library</button><button onClick={() => setView('editor')}>Create / Edit</button><button onClick={() => setView('validation')}>Validation</button></div><div className="panel table-panel"><div className="panel-title-row"><div><span className="eyebrow">Execution preparation</span><h2 className="section-title">Projection Set Library</h2><p className="muted">Reusable configurations define exactly what the model calculates before a Run Set is created.</p></div><button className="view-all" onClick={() => setView('validation')}><ShieldCheck size={14} />Validation</button></div><div className="projection-cards">{projectionSets.map(([name,version,scenario,horizon,status]) => <button className="projection-card" key={name} onClick={() => setView('editor')}><div className="projection-card-head"><span className="projection-icon"><LineChart size={17} /></span><StatusBadge status={status as 'Validated' | 'Needs review'} /></div><b>{name}</b><small>{version} · {scenario}</small><div className="projection-meta"><span><CalendarDays size={13} />{horizon}</span><span><Database size={13} />4 inputs</span><span><Workflow size={13} />42 formulas</span></div><div className="projection-footer"><span>Output capture complete</span><ArrowRight size={16} /></div></button>)}</div></div><div className="workspace-callout"><Check size={20} /><span><b>Projection Set readiness</b><small>MYGA GAAP Reserve is compatible with Model v3.2, Base scenario, and the current input library.</small></span><button onClick={() => setActiveScreen('Runs')}>Create Run Set <ArrowRight size={15} /></button></div></div> }
|
| 10 |
+
function ProjectionEditor({ onBack, onSave }: { onBack: () => void; onSave: () => void }) { const { notify } = useApp(); const [selected, setSelected] = useState('Model / block / basis'); return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Projection Set Library</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Projection Set · create / edit</span><h2 className="section-title">MYGA GAAP Reserve</h2><p className="muted">Define model, basis, inputs, scenario subset, projection keys, outputs, and trace capture.</p></div><StatusBadge status="Validated" /></div><div className="projection-editor-grid"><div className="panel"><ProjectionEditorRow selected={selected === 'Model / block / basis'} onClick={() => setSelected('Model / block / basis')} icon={<Database size={16} />} title="Model / block / basis" value="MYGA Reserve Model · Base · GAAP" /><ProjectionEditorRow selected={selected === 'Liabilities and assets'} onClick={() => setSelected('Liabilities and assets')} icon={<Database size={16} />} title="Liabilities and assets" value="Liability Inforce · Asset Inforce" /><ProjectionEditorRow selected={selected === 'Assumptions and scenarios'} onClick={() => setSelected('Assumptions and scenarios')} icon={<GitBranch size={16} />} title="Assumptions and scenarios" value="MYGA assumptions · Base scenario" /><ProjectionEditorRow selected={selected === 'Projection characteristics'} onClick={() => setSelected('Projection characteristics')} icon={<LineChart size={16} />} title="Projection characteristics" value="2026–2045 · Policy year · Monthly" /><ProjectionEditorRow selected={selected === 'Output capture and trace scope'} onClick={() => setSelected('Output capture and trace scope')} icon={<Workflow size={16} />} title="Output capture and trace scope" value="Net reserve · cash flows · lineage enabled" />{<div className="workspace-callout"><Check size={16} /><span><b>{selected}</b><small>Selected editor section · frontend state is ready for validation.</small></span></div>}</div><aside className="panel"><h2 className="section-title">Validation</h2><div className="preflight-list"><span><Check size={15} />All required inputs mapped</span><span><Check size={15} />Scenario subset compatible</span><span><Check size={15} />Projection keys selected</span><span><Check size={15} />Output capture configured</span></div><button className="button button-primary full-button" onClick={onSave}><Check size={15} />Save Projection Set</button><button className="button button-secondary full-button" onClick={() => notify('Projection Set duplicated')}>Duplicate version</button></aside></div></div> }
|
| 11 |
+
function ProjectionEditorRow({ icon, title, value, selected, onClick }: { icon: React.ReactNode; title: string; value: string; selected?: boolean; onClick?: () => void }) { return <button className={`definition-row ${selected ? 'definition-row-selected' : ''}`} onClick={onClick}><span>{icon}</span><span><b>{title}</b><small>{value}</small></span><ArrowRight size={15} /></button> }
|
| 12 |
function ProjectionValidation({ onBack }: { onBack: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Projection Set Library</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Projection Set validation</span><h2 className="section-title">MYGA GAAP Reserve readiness</h2><p className="muted">Confirm compatibility before attaching the Projection Set to a Run Set.</p></div><StatusBadge status="Validated" /></div><div className="validation-issues"><div className="panel-title-row"><h2 className="section-title">Validation checks</h2><StatusBadge status="Validated" /></div>{['Model v3.2 and GAAP basis are compatible','All input versions are pinned','Base scenario has a complete horizon','Trace and report capture scopes are configured'].map(check => <div className="issue-row" key={check}><Check size={18} color="#6fa426" /><span><b>{check}</b><small>Validated for current active context</small></span><StatusBadge status="Validated" /><ArrowRight size={16} /></div>)}</div></div> }
|
src/pages/ReportScreens.tsx
CHANGED
|
@@ -9,4 +9,6 @@ export function ReportLibraryPage() { const { notify } = useApp(); const [view,s
|
|
| 9 |
function ReportDefinition({ onBack, onAttach }: { onBack: () => void; onAttach: () => void }) { const { notify } = useApp(); return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Report Library</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Report Definition / Preview</span><h2 className="section-title">GAAP Reserve — Executive Summary</h2><p className="muted">Define source metrics, grouping dimensions, periods, filters, and the preview output.</p></div><StatusBadge status="Validated" /></div><div className="report-definition-grid"><div className="panel"><DefinitionRow icon={<Table2 size={16} />} title="Source metrics" value="Net reserve · change vs prior · scenario variance" /><DefinitionRow icon={<Settings2 size={16} />} title="Grouping and dimensions" value="Projection year · scenario · product" /><DefinitionRow icon={<Filter size={16} />} title="Filters and periods" value="MYGA · GAAP · 2026-12-31 · 2026–2045" /><DefinitionRow icon={<FileText size={16} />} title="Calculation fields" value="Reserve change % · top driver contribution" /></div><aside className="panel report-preview-panel"><span className="eyebrow">Preview · sample run data</span><h2 className="section-title">Executive Summary</h2><div className="report-sheet"><b>Net reserve</b><strong>$1,245.6M</strong><span>+6.99% vs prior period</span><div className="report-sheet-chart" /></div><div className="button-row"><button className="button button-primary" onClick={onAttach}><Link2 size={15} />Attach to Run Set</button><button className="button button-secondary" onClick={() => notify('Report definition saved')}><Check size={15} />Save definition</button></div></aside></div></div> }
|
| 10 |
function DefinitionRow({ icon, title, value }: { icon: React.ReactNode; title: string; value: string }) { return <button className="definition-row"><span>{icon}</span><span><b>{title}</b><small>{value}</small></span><ArrowRight size={15} /></button> }
|
| 11 |
function ReportAttachment({ onBack, onComplete }: { onBack: () => void; onComplete: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to report definition</button><div className="panel"><span className="eyebrow">Run Set attachment</span><h2 className="section-title">Attach report to GAAP Reserve Pack</h2><p className="muted">Attached definitions are captured in the immutable Run Set snapshot at submission.</p><div className="attachment-options"><label><input type="checkbox" defaultChecked />GAAP Reserve — Executive Summary <small>Post-run · v2.1</small></label><label><input type="checkbox" defaultChecked />Scenario Variance Pack <small>Runtime · v0.8</small></label><label><input type="checkbox" />Solvency Summary <small>Post-run · v1.4</small></label></div><div className="workspace-callout"><Check size={18} /><span><b>2 reports selected</b><small>They will be generated after the projection outputs complete.</small></span><button onClick={onComplete}><Send size={15} />Confirm attachment</button></div></div></div> }
|
| 12 |
-
function CompletedReport({ onBack, onNotify }: { onBack: () => void; onNotify: (message: string) => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Report Library</button><div className="panel completed-report-header"><div><span className="eyebrow">Completed report · JR-20261231-001</span><h2 className="section-title">GAAP Reserve — Executive Summary</h2><p className="muted">Generated from the pinned Run Set snapshot on 2026-12-31.</p></div><StatusBadge status="Completed" /></div><div className="completed-report-grid"><div className="panel report-sheet-large"><div><b>GAAP Reserve — Executive Summary</b><small>MYGA Reserve Model · v3.2 · Base scenario · GAAP</small></div><div className="report-sheet-metrics"><span><small>Net reserve</small><b>$1,245.6M</b></span><span><small>Change</small><b>+6.99%</b></span><span><small>Scenarios</small><b>3</b></span></div><div className="report-sheet-chart" /></div><aside className="panel"><h2 className="section-title">Export</h2><p className="muted">Choose a completed artifact while preserving its Run Set provenance.</p><button className="button button-primary full-button" onClick={() => onNotify('
|
|
|
|
|
|
|
|
|
| 9 |
function ReportDefinition({ onBack, onAttach }: { onBack: () => void; onAttach: () => void }) { const { notify } = useApp(); return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Report Library</button><div className="panel detail-workspace-head"><div><span className="eyebrow">Report Definition / Preview</span><h2 className="section-title">GAAP Reserve — Executive Summary</h2><p className="muted">Define source metrics, grouping dimensions, periods, filters, and the preview output.</p></div><StatusBadge status="Validated" /></div><div className="report-definition-grid"><div className="panel"><DefinitionRow icon={<Table2 size={16} />} title="Source metrics" value="Net reserve · change vs prior · scenario variance" /><DefinitionRow icon={<Settings2 size={16} />} title="Grouping and dimensions" value="Projection year · scenario · product" /><DefinitionRow icon={<Filter size={16} />} title="Filters and periods" value="MYGA · GAAP · 2026-12-31 · 2026–2045" /><DefinitionRow icon={<FileText size={16} />} title="Calculation fields" value="Reserve change % · top driver contribution" /></div><aside className="panel report-preview-panel"><span className="eyebrow">Preview · sample run data</span><h2 className="section-title">Executive Summary</h2><div className="report-sheet"><b>Net reserve</b><strong>$1,245.6M</strong><span>+6.99% vs prior period</span><div className="report-sheet-chart" /></div><div className="button-row"><button className="button button-primary" onClick={onAttach}><Link2 size={15} />Attach to Run Set</button><button className="button button-secondary" onClick={() => notify('Report definition saved')}><Check size={15} />Save definition</button></div></aside></div></div> }
|
| 10 |
function DefinitionRow({ icon, title, value }: { icon: React.ReactNode; title: string; value: string }) { return <button className="definition-row"><span>{icon}</span><span><b>{title}</b><small>{value}</small></span><ArrowRight size={15} /></button> }
|
| 11 |
function ReportAttachment({ onBack, onComplete }: { onBack: () => void; onComplete: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to report definition</button><div className="panel"><span className="eyebrow">Run Set attachment</span><h2 className="section-title">Attach report to GAAP Reserve Pack</h2><p className="muted">Attached definitions are captured in the immutable Run Set snapshot at submission.</p><div className="attachment-options"><label><input type="checkbox" defaultChecked />GAAP Reserve — Executive Summary <small>Post-run · v2.1</small></label><label><input type="checkbox" defaultChecked />Scenario Variance Pack <small>Runtime · v0.8</small></label><label><input type="checkbox" />Solvency Summary <small>Post-run · v1.4</small></label></div><div className="workspace-callout"><Check size={18} /><span><b>2 reports selected</b><small>They will be generated after the projection outputs complete.</small></span><button onClick={onComplete}><Send size={15} />Confirm attachment</button></div></div></div> }
|
| 12 |
+
function CompletedReport({ onBack, onNotify }: { onBack: () => void; onNotify: (message: string) => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Report Library</button><div className="panel completed-report-header"><div><span className="eyebrow">Completed report · JR-20261231-001</span><h2 className="section-title">GAAP Reserve — Executive Summary</h2><p className="muted">Generated from the pinned Run Set snapshot on 2026-12-31.</p></div><StatusBadge status="Completed" /></div><div className="completed-report-grid"><div className="panel report-sheet-large"><div><b>GAAP Reserve — Executive Summary</b><small>MYGA Reserve Model · v3.2 · Base scenario · GAAP</small></div><div className="report-sheet-metrics"><span><small>Net reserve</small><b>$1,245.6M</b></span><span><small>Change</small><b>+6.99%</b></span><span><small>Scenarios</small><b>3</b></span></div><div className="report-sheet-chart" /></div><aside className="panel"><h2 className="section-title">Export</h2><p className="muted">Choose a completed artifact while preserving its Run Set provenance.</p><button className="button button-primary full-button" onClick={() => { downloadArtifact('gaap-reserve-executive-summary.txt', 'GAAP Reserve — Executive Summary\nRun JR-20261231-001\nNet reserve: $1,245.6M\nChange: +6.99%', 'text/plain'); onNotify('Report artifact downloaded') }}><Download size={15} />Export PDF</button><button className="button button-secondary full-button" onClick={() => { downloadArtifact('gaap-reserve-executive-summary.csv', 'metric,value\nnet_reserve,1245.6M\nchange_vs_prior,6.99%', 'text/csv'); onNotify('Report CSV downloaded') }}><Download size={15} />Export CSV</button><button className="button button-secondary full-button" onClick={() => onNotify('Run results opened')}><ArrowRight size={15} />Open source results</button></aside></div></div> }
|
| 13 |
+
|
| 14 |
+
function downloadArtifact(filename: string, body: string, type: string) { const blob = new Blob([body], { type }); const url = URL.createObjectURL(blob); const anchor = document.createElement('a'); anchor.href = url; anchor.download = filename; anchor.click(); URL.revokeObjectURL(url) }
|
src/pages/ResultScreens.tsx
CHANGED
|
@@ -7,8 +7,10 @@ import { WorkspaceToolbar } from '../components/ui/WorkspaceToolbar'
|
|
| 7 |
|
| 8 |
type ResultView = 'explorer' | 'history' | 'compare' | 'variance'
|
| 9 |
export function ResultsExplorerPage() { const { notify, openInvestigation } = useApp(); const [view, setView] = useState<ResultView>('explorer'); const trace = () => openInvestigation({ kind: 'trace', runId: 'JR-20261231-001', metric: 'net_reserve' }); const scratchpad = () => openInvestigation({ kind: 'scratchpad', runId: 'JR-20261231-001', metric: 'net_reserve' }); return <div className="workspace-stack"><WorkspaceToolbar searchPlaceholder="Search runs, results, outputs, investigations..." action="Compare runs" actionIcon={<GitCompare size={15} />} onAction={() => setView('compare')} /><div className="results-subnav"><button className={view === 'explorer' ? 'results-subnav-active' : ''} onClick={() => setView('explorer')}>Results Explorer</button><button className={view === 'history' ? 'results-subnav-active' : ''} onClick={() => setView('history')}>Run History</button><button className={view === 'compare' ? 'results-subnav-active' : ''} onClick={() => setView('compare')}>Compare runs</button><button className={view === 'variance' ? 'results-subnav-active' : ''} onClick={() => setView('variance')}>Driver / Variance</button></div>{view === 'explorer' && <Explorer onNotify={notify} onCompare={() => setView('compare')} onTrace={trace} onScratchpad={scratchpad} />}{view === 'history' && <History onOpen={() => setView('explorer')} />}{view === 'compare' && <Compare onBack={() => setView('explorer')} onVariance={() => setView('variance')} />}{view === 'variance' && <Variance onBack={() => setView('compare')} />}</div> }
|
| 10 |
-
function Explorer({ onNotify, onCompare, onTrace, onScratchpad }: { onNotify: (m: string) => void; onCompare: () => void; onTrace: () => void; onScratchpad: () => void }) { return <><div className="result-metrics"><MetricCard icon={<Table2 size={22} />} label="Net reserve" value="$2.2B" sub="MYGA Base · 2026-12-31" /><MetricCard light icon={<LineChart size={22} />} label="Change vs prior" value="+6.99%" sub="Primary driver: lapse_rate" /><MetricCard icon={<Target size={22} />} label="Outputs captured" value="28" sub="5 published outputs" /><MetricCard light icon={<GitBranch size={22} />} label="Trace ready" value="100%" sub="All values have lineage" /></div><div className="results-layout"><div className="panel results-explorer"><div className="panel-title-row"><div><span className="eyebrow">Run JR-20261231-001</span><h2 className="section-title">MYGA Base · GAAP Reserve</h2></div><div className="button-row"><button className="view-all" onClick={() => onNotify('Result filters opened')}><Filter size={14} />Filters</button><button className="view-all" onClick={onCompare}><GitCompare size={14} />Compare</button><button className="view-all" onClick={() =>
|
| 11 |
function History({ onOpen }: { onOpen: () => void }) { return <div className="panel"><div className="panel-title-row"><div><span className="eyebrow">Results provenance</span><h2 className="section-title">Run History</h2><p className="muted">Every result remains linked to its model, input, scenario, formula, and compute snapshot.</p></div><button className="view-all"><Filter size={14} />Filters</button></div><div className="history-table">{[['JR-20261231-001','GAAP Reserve Pack','MYGA Base','2026-12-31','Completed'],['JR-20261230-004','Monthly VM-21 Pack','VM-21 Deterministic','2026-12-31','Completed'],['JR-20261228-002','Pricing Sensitivity Pack','Low Interest Rate','2026-12-31','Stale']].map(row => <button key={row[0]} onClick={onOpen}><span><b>{row[0]}</b><small>{row[1]}</small></span><span>{row[2]}</span><span>{row[3]}</span><StatusBadge status={row[4] as 'Completed' | 'Stale'} /><ArrowRight size={16} /></button>)}</div></div> }
|
| 12 |
function Compare({ onBack, onVariance }: { onBack: () => void; onVariance: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Results Explorer</button><div className="panel comparison-header"><div><span className="eyebrow">Run comparison</span><h2 className="section-title">Base vs Low Interest Rate</h2><p className="muted">Model v3.2 · same input snapshot · scenario-only difference</p></div><StatusBadge status="Validated" /></div><div className="comparison-grid"><CompareCard label="Base scenario" value="$1,245.6M" change="Reference" icon={<Check size={18} />} /><CompareCard label="Low Interest Rate" value="$998.1M" change="−19.9%" icon={<TrendingDown size={18} />} negative /></div><div className="panel"><div className="panel-title-row"><h2 className="section-title">Changed dimensions</h2><button className="button button-secondary" onClick={onVariance}><BarChart3 size={15} />Open driver variance</button></div><div className="changed-dimensions">{['Interest rate curve','Lapse rate','Net reserve','Expense load'].map((label,index) => <div key={label}><span>{label}</span><b>{['−125 bps','+0.12%','−247.5M','Unchanged'][index]}</b><small>{index === 2 ? 'Output difference' : 'Input / assumption'}</small></div>)}</div></div></div> }
|
| 13 |
function CompareCard({ label, value, change, icon, negative }: { label: string; value: string; change: string; icon: ReactNode; negative?: boolean }) { return <div className="panel compare-card"><span className="compare-card-icon">{icon}</span><span><small>{label}</small><strong>{value}</strong><b className={negative ? 'negative' : ''}>{change}</b></span></div> }
|
| 14 |
function Variance({ onBack }: { onBack: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to comparison</button><div className="panel variance-hero"><div><span className="eyebrow">Driver / Variance View</span><h2 className="section-title">What explains the −$247.5M change?</h2><p className="muted">Contribution analysis preserves the exact run pair and output lineage.</p></div><StatusBadge status="Validated" /></div><div className="panel"><div className="waterfall"><div className="waterfall-bar positive-bar" style={{ height: '72%' }}><span>+35M</span></div><div className="waterfall-bar negative-bar" style={{ height: '44%' }}><span>−120M</span></div><div className="waterfall-bar negative-bar" style={{ height: '31%' }}><span>−82M</span></div><div className="waterfall-bar negative-bar" style={{ height: '20%' }}><span>−80.5M</span></div><div className="waterfall-bar total-bar" style={{ height: '55%' }}><span>−247.5M</span></div></div><div className="waterfall-labels"><span>Mortality</span><span>Interest</span><span>Lapse</span><span>Expense</span><span>Net change</span></div></div></div> }
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
type ResultView = 'explorer' | 'history' | 'compare' | 'variance'
|
| 9 |
export function ResultsExplorerPage() { const { notify, openInvestigation } = useApp(); const [view, setView] = useState<ResultView>('explorer'); const trace = () => openInvestigation({ kind: 'trace', runId: 'JR-20261231-001', metric: 'net_reserve' }); const scratchpad = () => openInvestigation({ kind: 'scratchpad', runId: 'JR-20261231-001', metric: 'net_reserve' }); return <div className="workspace-stack"><WorkspaceToolbar searchPlaceholder="Search runs, results, outputs, investigations..." action="Compare runs" actionIcon={<GitCompare size={15} />} onAction={() => setView('compare')} /><div className="results-subnav"><button className={view === 'explorer' ? 'results-subnav-active' : ''} onClick={() => setView('explorer')}>Results Explorer</button><button className={view === 'history' ? 'results-subnav-active' : ''} onClick={() => setView('history')}>Run History</button><button className={view === 'compare' ? 'results-subnav-active' : ''} onClick={() => setView('compare')}>Compare runs</button><button className={view === 'variance' ? 'results-subnav-active' : ''} onClick={() => setView('variance')}>Driver / Variance</button></div>{view === 'explorer' && <Explorer onNotify={notify} onCompare={() => setView('compare')} onTrace={trace} onScratchpad={scratchpad} />}{view === 'history' && <History onOpen={() => setView('explorer')} />}{view === 'compare' && <Compare onBack={() => setView('explorer')} onVariance={() => setView('variance')} />}{view === 'variance' && <Variance onBack={() => setView('compare')} />}</div> }
|
| 10 |
+
function Explorer({ onNotify, onCompare, onTrace, onScratchpad }: { onNotify: (m: string) => void; onCompare: () => void; onTrace: () => void; onScratchpad: () => void }) { return <><div className="result-metrics"><MetricCard icon={<Table2 size={22} />} label="Net reserve" value="$2.2B" sub="MYGA Base · 2026-12-31" /><MetricCard light icon={<LineChart size={22} />} label="Change vs prior" value="+6.99%" sub="Primary driver: lapse_rate" /><MetricCard icon={<Target size={22} />} label="Outputs captured" value="28" sub="5 published outputs" /><MetricCard light icon={<GitBranch size={22} />} label="Trace ready" value="100%" sub="All values have lineage" /></div><div className="results-layout"><div className="panel results-explorer"><div className="panel-title-row"><div><span className="eyebrow">Run JR-20261231-001</span><h2 className="section-title">MYGA Base · GAAP Reserve</h2></div><div className="button-row"><button className="view-all" onClick={() => onNotify('Result filters opened')}><Filter size={14} />Filters</button><button className="view-all" onClick={onCompare}><GitCompare size={14} />Compare</button><button className="view-all" onClick={() => { downloadArtifact('myga-results-jr-20261231-001.csv', 'year,net_reserve,change\n2026,1245.6,6.2\n2027,1302.8,4.6\n2030,1452.7,4.0', 'text/csv'); onNotify('Results CSV downloaded') }}><Download size={14} />Export</button></div></div><div className="chart-large"><div className="chart-line chart-line-one" /><div className="chart-line chart-line-two" /><span>2026</span><span>2030</span><span>2035</span><span>2040</span><span>2045</span></div><div className="result-grid">{['2026','2027','2028','2029','2030'].map((year,index) => <button className="result-row" key={year} onClick={onTrace}><span>{year}</span><b>{['1,245.6M','1,302.8M','1,348.2M','1,397.1M','1,452.7M'][index]}</b><span className="positive">+{[6.2,4.6,3.5,3.6,4.0][index]}%</span><span>{(4.25 + index * .06).toFixed(2)}%</span><span className="trace-link"><GitBranch size={13} />Trace value</span></button>)}</div></div><aside className="panel result-inspector"><span className="eyebrow">Selected result</span><h2 className="section-title">Net reserve</h2><strong className="inspector-value">$1,245.6M</strong><StatusBadge status="Completed" /><div className="inspector-section"><span>Provenance</span><b>Run JR-20261231-001</b><small>Model v3.2 · Base scenario · GAAP</small></div><button className="button button-primary full-button" onClick={onTrace}><GitBranch size={15} />Trace this value</button><button className="button button-secondary full-button" onClick={onScratchpad}><Plus size={15} />Add to Scratchpad</button></aside></div></> }
|
| 11 |
function History({ onOpen }: { onOpen: () => void }) { return <div className="panel"><div className="panel-title-row"><div><span className="eyebrow">Results provenance</span><h2 className="section-title">Run History</h2><p className="muted">Every result remains linked to its model, input, scenario, formula, and compute snapshot.</p></div><button className="view-all"><Filter size={14} />Filters</button></div><div className="history-table">{[['JR-20261231-001','GAAP Reserve Pack','MYGA Base','2026-12-31','Completed'],['JR-20261230-004','Monthly VM-21 Pack','VM-21 Deterministic','2026-12-31','Completed'],['JR-20261228-002','Pricing Sensitivity Pack','Low Interest Rate','2026-12-31','Stale']].map(row => <button key={row[0]} onClick={onOpen}><span><b>{row[0]}</b><small>{row[1]}</small></span><span>{row[2]}</span><span>{row[3]}</span><StatusBadge status={row[4] as 'Completed' | 'Stale'} /><ArrowRight size={16} /></button>)}</div></div> }
|
| 12 |
function Compare({ onBack, onVariance }: { onBack: () => void; onVariance: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to Results Explorer</button><div className="panel comparison-header"><div><span className="eyebrow">Run comparison</span><h2 className="section-title">Base vs Low Interest Rate</h2><p className="muted">Model v3.2 · same input snapshot · scenario-only difference</p></div><StatusBadge status="Validated" /></div><div className="comparison-grid"><CompareCard label="Base scenario" value="$1,245.6M" change="Reference" icon={<Check size={18} />} /><CompareCard label="Low Interest Rate" value="$998.1M" change="−19.9%" icon={<TrendingDown size={18} />} negative /></div><div className="panel"><div className="panel-title-row"><h2 className="section-title">Changed dimensions</h2><button className="button button-secondary" onClick={onVariance}><BarChart3 size={15} />Open driver variance</button></div><div className="changed-dimensions">{['Interest rate curve','Lapse rate','Net reserve','Expense load'].map((label,index) => <div key={label}><span>{label}</span><b>{['−125 bps','+0.12%','−247.5M','Unchanged'][index]}</b><small>{index === 2 ? 'Output difference' : 'Input / assumption'}</small></div>)}</div></div></div> }
|
| 13 |
function CompareCard({ label, value, change, icon, negative }: { label: string; value: string; change: string; icon: ReactNode; negative?: boolean }) { return <div className="panel compare-card"><span className="compare-card-icon">{icon}</span><span><small>{label}</small><strong>{value}</strong><b className={negative ? 'negative' : ''}>{change}</b></span></div> }
|
| 14 |
function Variance({ onBack }: { onBack: () => void }) { return <div className="workspace-stack"><button className="back-link" onClick={onBack}><ArrowLeft size={15} />Back to comparison</button><div className="panel variance-hero"><div><span className="eyebrow">Driver / Variance View</span><h2 className="section-title">What explains the −$247.5M change?</h2><p className="muted">Contribution analysis preserves the exact run pair and output lineage.</p></div><StatusBadge status="Validated" /></div><div className="panel"><div className="waterfall"><div className="waterfall-bar positive-bar" style={{ height: '72%' }}><span>+35M</span></div><div className="waterfall-bar negative-bar" style={{ height: '44%' }}><span>−120M</span></div><div className="waterfall-bar negative-bar" style={{ height: '31%' }}><span>−82M</span></div><div className="waterfall-bar negative-bar" style={{ height: '20%' }}><span>−80.5M</span></div><div className="waterfall-bar total-bar" style={{ height: '55%' }}><span>−247.5M</span></div></div><div className="waterfall-labels"><span>Mortality</span><span>Interest</span><span>Lapse</span><span>Expense</span><span>Net change</span></div></div></div> }
|
| 15 |
+
|
| 16 |
+
function downloadArtifact(filename: string, body: string, type: string) { const blob = new Blob([body], { type }); const url = URL.createObjectURL(blob); const anchor = document.createElement('a'); anchor.href = url; anchor.download = filename; anchor.click(); URL.revokeObjectURL(url) }
|