@retiregolden/planner-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +181 -0
- package/package.json +77 -0
- package/src/App.tsx +246 -0
- package/src/RouteErrorBoundary.tsx +45 -0
- package/src/assets/hero.png +0 -0
- package/src/assets/react.svg +1 -0
- package/src/assets/vite.svg +1 -0
- package/src/data/fedInvestClient.ts +113 -0
- package/src/data/localStore.ts +42 -0
- package/src/data/planOrigin.ts +24 -0
- package/src/data/planStore.ts +165 -0
- package/src/data/v2Backup.ts +101 -0
- package/src/import/ImportPage.tsx +347 -0
- package/src/import/ReviewChecklistView.tsx +38 -0
- package/src/import/brokerCsv.ts +395 -0
- package/src/import/csv.ts +133 -0
- package/src/import/genericCsv.ts +224 -0
- package/src/import/projectionLab.ts +350 -0
- package/src/import/reviewChecklist.ts +33 -0
- package/src/import/tenForty.ts +275 -0
- package/src/index.css +630 -0
- package/src/index.ts +16 -0
- package/src/learn/ArticleBody.tsx +78 -0
- package/src/learn/ArticlePage.tsx +57 -0
- package/src/learn/GlossaryPage.tsx +33 -0
- package/src/learn/LearnAboutScreen.tsx +41 -0
- package/src/learn/LearnCards.tsx +41 -0
- package/src/learn/LearnLink.tsx +91 -0
- package/src/learn/LearningCenterPage.tsx +114 -0
- package/src/learn/SourcesPage.tsx +98 -0
- package/src/learn/components/ArticleFigure.tsx +34 -0
- package/src/learn/components/ArticleShell.tsx +86 -0
- package/src/learn/components/ComparisonTable.tsx +42 -0
- package/src/learn/components/FormulaBlock.tsx +34 -0
- package/src/learn/components/PurchasingPowerChart.tsx +41 -0
- package/src/learn/components/RelatedArticles.tsx +27 -0
- package/src/learn/components/ScenarioCard.tsx +24 -0
- package/src/learn/components/SourceList.tsx +23 -0
- package/src/learn/components/charts.tsx +21 -0
- package/src/learn/content/about-retiregolden.ts +100 -0
- package/src/learn/content/aca-premium-tax-credits-and-magi.ts +103 -0
- package/src/learn/content/account-types-overview.ts +106 -0
- package/src/learn/content/after-tax-estate.ts +111 -0
- package/src/learn/content/agi-magi-and-taxable-income.ts +112 -0
- package/src/learn/content/appealing-irmaa-ssa-44.ts +95 -0
- package/src/learn/content/assumption-general-inflation.ts +82 -0
- package/src/learn/content/assumption-healthcare-inflation.ts +85 -0
- package/src/learn/content/assumption-heir-tax-rate.ts +79 -0
- package/src/learn/content/assumption-investment-returns.ts +90 -0
- package/src/learn/content/assumption-longevity-planning-age.ts +78 -0
- package/src/learn/content/assumption-recent-magi.ts +83 -0
- package/src/learn/content/assumption-social-security-cola.ts +89 -0
- package/src/learn/content/assumption-social-security-trust-fund.ts +83 -0
- package/src/learn/content/assumption-state-tax-override.ts +79 -0
- package/src/learn/content/beneficiaries-and-account-titling.ts +99 -0
- package/src/learn/content/break-even-useful-lens.ts +94 -0
- package/src/learn/content/building-a-retirement-spending-budget.ts +100 -0
- package/src/learn/content/cola-and-inflation-protection.ts +102 -0
- package/src/learn/content/divorced-spousal-and-survivor-records.ts +104 -0
- package/src/learn/content/dynamic-spending-guardrails.ts +90 -0
- package/src/learn/content/earnings-test-before-fra.ts +100 -0
- package/src/learn/content/employer-match-and-contribution-order.ts +104 -0
- package/src/learn/content/examplePlanArticles.ts +525 -0
- package/src/learn/content/fees-expense-ratios-and-compounding-drag.ts +98 -0
- package/src/learn/content/fi-number-and-four-percent-rule.ts +64 -0
- package/src/learn/content/filling-a-tax-bracket-with-roth-conversions.ts +98 -0
- package/src/learn/content/funded-ratio.ts +70 -0
- package/src/learn/content/healthcare-after-65.ts +103 -0
- package/src/learn/content/healthcare-before-65.ts +104 -0
- package/src/learn/content/historical-vs-random-return-models.ts +101 -0
- package/src/learn/content/how-assumptions-change-the-answer.ts +105 -0
- package/src/learn/content/how-much-can-i-spend.ts +105 -0
- package/src/learn/content/how-social-security-is-taxed.ts +95 -0
- package/src/learn/content/how-the-optimizer-thinks.ts +102 -0
- package/src/learn/content/how-the-optimizer-values-after-tax-estate.ts +97 -0
- package/src/learn/content/how-to-model-accumulation.ts +67 -0
- package/src/learn/content/how-to-read-a-retirement-projection.ts +115 -0
- package/src/learn/content/hsas-and-qualified-medical-expenses.ts +108 -0
- package/src/learn/content/hsas-as-retirement-accounts.ts +101 -0
- package/src/learn/content/inflation-risk.ts +98 -0
- package/src/learn/content/inherited-ira-10-year-rule.ts +105 -0
- package/src/learn/content/insurance-in-your-retirement-plan.ts +103 -0
- package/src/learn/content/irmaa-two-year-lookback.ts +99 -0
- package/src/learn/content/long-term-care-costs-and-insurance.ts +103 -0
- package/src/learn/content/long-term-care-insurance-as-risk-transfer.ts +98 -0
- package/src/learn/content/longevity-risk.ts +99 -0
- package/src/learn/content/marginal-vs-effective-tax-rate.ts +98 -0
- package/src/learn/content/medicare-part-b-vs-part-d-irmaa.ts +102 -0
- package/src/learn/content/mortality-weighted-social-security.ts +113 -0
- package/src/learn/content/moving-to-retiregolden.ts +86 -0
- package/src/learn/content/niit-high-income-investment-tax.ts +98 -0
- package/src/learn/content/ordinary-income-vs-capital-gains.ts +103 -0
- package/src/learn/content/paying-conversion-taxes-taxable-vs-ira.ts +102 -0
- package/src/learn/content/pensions-and-annuities.ts +101 -0
- package/src/learn/content/permanent-life-insurance-in-a-plan.ts +106 -0
- package/src/learn/content/pia-aime-and-bend-points.ts +103 -0
- package/src/learn/content/planner-overview.ts +106 -0
- package/src/learn/content/planning-for-couples-and-survivor-years.ts +108 -0
- package/src/learn/content/privacy-what-stays-in-your-browser.ts +99 -0
- package/src/learn/content/qcds-qualified-charitable-distributions.ts +101 -0
- package/src/learn/content/reading-the-results-page.ts +96 -0
- package/src/learn/content/reading-the-social-security-analysis-page.ts +106 -0
- package/src/learn/content/real-estate-home-equity-and-debt.ts +100 -0
- package/src/learn/content/reports-csv-exports-and-sharing.ts +101 -0
- package/src/learn/content/risk-based-guardrails.ts +100 -0
- package/src/learn/content/rmds-required-minimum-distributions.ts +100 -0
- package/src/learn/content/roth-conversion-basics.ts +104 -0
- package/src/learn/content/rsus-and-espp.ts +101 -0
- package/src/learn/content/rule-of-55-and-72t.ts +107 -0
- package/src/learn/content/savings-rate-biggest-lever.ts +66 -0
- package/src/learn/content/seed-your-plan-from-your-tax-return.ts +93 -0
- package/src/learn/content/sensitivity-testing-what-changes-the-answer.ts +104 -0
- package/src/learn/content/sequence-of-returns-risk.ts +98 -0
- package/src/learn/content/social-security-bridge.ts +67 -0
- package/src/learn/content/social-security-claiming-age-basics.ts +113 -0
- package/src/learn/content/social-security-taxes-vs-benefits.ts +76 -0
- package/src/learn/content/spending-profiles-and-the-retirement-smile.ts +92 -0
- package/src/learn/content/spousal-and-survivor-benefits.ts +120 -0
- package/src/learn/content/ssdi-and-retirement-planning.ts +72 -0
- package/src/learn/content/standard-deduction-senior-deduction-and-itemizing.ts +97 -0
- package/src/learn/content/state-income-taxes-in-retirement.ts +97 -0
- package/src/learn/content/step-up-in-basis.ts +102 -0
- package/src/learn/content/survivor-planning-for-couples.ts +110 -0
- package/src/learn/content/survivor-spending-in-couple-plans.ts +98 -0
- package/src/learn/content/tax-cliffs-and-bracket-edges.ts +105 -0
- package/src/learn/content/tax-loss-and-gain-harvesting.ts +99 -0
- package/src/learn/content/taxable-brokerage-basis-and-capital-gains.ts +99 -0
- package/src/learn/content/three-big-questions-spending-time-risk.ts +103 -0
- package/src/learn/content/tips-ladders.ts +92 -0
- package/src/learn/content/todays-dollars-vs-future-dollars.ts +107 -0
- package/src/learn/content/traditional-vs-roth-contributions.ts +113 -0
- package/src/learn/content/troubleshooting-surprising-results.ts +105 -0
- package/src/learn/content/trust-fund-haircut-scenarios.ts +101 -0
- package/src/learn/content/understanding-monte-carlo-success-rate.ts +118 -0
- package/src/learn/content/understanding-your-plan-assumptions.ts +134 -0
- package/src/learn/content/using-assumptions-and-provenance.ts +98 -0
- package/src/learn/content/using-scenarios-to-compare-choices.ts +99 -0
- package/src/learn/content/what-changes-when-you-move-states.ts +141 -0
- package/src/learn/content/what-is-fire.ts +65 -0
- package/src/learn/content/what-monte-carlo-proves.ts +98 -0
- package/src/learn/content/what-retiregolden-models.ts +103 -0
- package/src/learn/content/what-retirement-healthcare-really-costs.ts +117 -0
- package/src/learn/content/why-95-percent-is-not-a-guarantee.ts +98 -0
- package/src/learn/content/why-roth-conversions-raise-other-costs.ts +106 -0
- package/src/learn/content/why-small-tax-cliffs-can-matter.ts +109 -0
- package/src/learn/content/widows-penalty-and-survivor-brackets.ts +106 -0
- package/src/learn/content/withdrawal-order-basics.ts +105 -0
- package/src/learn/glossary.ts +191 -0
- package/src/learn/inlineMarkdown.tsx +54 -0
- package/src/learn/learn.css +537 -0
- package/src/learn/learningRegistry.ts +502 -0
- package/src/longevity/LongevityResults.tsx +85 -0
- package/src/longevity/LongevityWizard.tsx +305 -0
- package/src/longevity/constants.ts +15 -0
- package/src/longevity/factors.ts +125 -0
- package/src/longevity/model.ts +31 -0
- package/src/longevity/persistedGuard.ts +129 -0
- package/src/longevity/storage.ts +40 -0
- package/src/mc/messages.ts +118 -0
- package/src/mc/monteCarlo.worker.ts +44 -0
- package/src/mc/pool.ts +267 -0
- package/src/mc/runRequest.ts +125 -0
- package/src/optimize/messages.ts +84 -0
- package/src/optimize/optimize.worker.ts +29 -0
- package/src/optimize/runOptimize.ts +92 -0
- package/src/optimize/runSpendingSolve.ts +47 -0
- package/src/optimize/runner.ts +21 -0
- package/src/optimize/spendingMessages.ts +44 -0
- package/src/optimize/spendingRunner.ts +21 -0
- package/src/optimize/spendingSolve.worker.ts +18 -0
- package/src/planner/AssumptionsCardPage.tsx +136 -0
- package/src/planner/BucketLensCard.tsx +114 -0
- package/src/planner/ComparePlansPage.tsx +219 -0
- package/src/planner/DisclaimerPage.tsx +88 -0
- package/src/planner/HowTestedPage.tsx +159 -0
- package/src/planner/LiveStatus.tsx +15 -0
- package/src/planner/LongevityModal.tsx +55 -0
- package/src/planner/Modal.tsx +97 -0
- package/src/planner/MonteCarloPage.tsx +907 -0
- package/src/planner/OptimizePage.tsx +611 -0
- package/src/planner/PlanContext.tsx +198 -0
- package/src/planner/PlanPickerPage.tsx +124 -0
- package/src/planner/PlanWorkspace.tsx +290 -0
- package/src/planner/ProvenancePanel.tsx +45 -0
- package/src/planner/RelocationComparePage.tsx +485 -0
- package/src/planner/ReportPage.tsx +375 -0
- package/src/planner/ResultsPage.tsx +817 -0
- package/src/planner/ScenariosPage.tsx +285 -0
- package/src/planner/SocialSecuritySection.tsx +556 -0
- package/src/planner/SpendingSolverPage.tsx +512 -0
- package/src/planner/SsAnalysisPage.tsx +1134 -0
- package/src/planner/SurvivalPercentileModal.tsx +161 -0
- package/src/planner/SurvivorTransitionPage.tsx +286 -0
- package/src/planner/assumptionsExport.ts +371 -0
- package/src/planner/bucketLens.ts +89 -0
- package/src/planner/chartFrame.ts +8 -0
- package/src/planner/chartStyle.ts +11 -0
- package/src/planner/dialogViews.tsx +184 -0
- package/src/planner/dialogs.tsx +133 -0
- package/src/planner/examples/ExampleLibrary.tsx +189 -0
- package/src/planner/examples/ExamplePreviewBanner.tsx +55 -0
- package/src/planner/examples/ExamplesPage.tsx +25 -0
- package/src/planner/examples/OpenExampleButton.tsx +61 -0
- package/src/planner/examples/buildAggressiveSaver.ts +102 -0
- package/src/planner/examples/buildAnnuityEstate.ts +137 -0
- package/src/planner/examples/buildBaristaFire.ts +115 -0
- package/src/planner/examples/buildBracketFillRoth.ts +65 -0
- package/src/planner/examples/buildBridgeEarlyRetirement.ts +94 -0
- package/src/planner/examples/buildBrokerageNoHsa.ts +109 -0
- package/src/planner/examples/buildCoastFire.ts +88 -0
- package/src/planner/examples/buildContext.ts +20 -0
- package/src/planner/examples/buildEarlyCareerMatch.ts +93 -0
- package/src/planner/examples/buildEarlyRetireeAca.ts +61 -0
- package/src/planner/examples/buildExampleCouple.ts +103 -0
- package/src/planner/examples/buildFixedTargetSpending.ts +74 -0
- package/src/planner/examples/buildGlidepathAllocation.ts +131 -0
- package/src/planner/examples/buildGuardrailsFlex.ts +120 -0
- package/src/planner/examples/buildHsaPropertyDepth.ts +109 -0
- package/src/planner/examples/buildHsaStealthRetirement.ts +97 -0
- package/src/planner/examples/buildLeanFatFire.ts +109 -0
- package/src/planner/examples/buildLtcShock.ts +62 -0
- package/src/planner/examples/buildMovingStateTax.ts +53 -0
- package/src/planner/examples/buildNoAnnuityBrokerage.ts +92 -0
- package/src/planner/examples/buildRmdIrmaa.ts +55 -0
- package/src/planner/examples/buildSalaryGrowthEscalation.ts +96 -0
- package/src/planner/examples/buildStaticAllocationControl.ts +96 -0
- package/src/planner/examples/buildSurvivorYears.ts +62 -0
- package/src/planner/examples/buildUnderSavedSingle.ts +51 -0
- package/src/planner/examples/exampleCopy.ts +23 -0
- package/src/planner/examples/loadExample.ts +90 -0
- package/src/planner/examples/registry.ts +313 -0
- package/src/planner/explainPanels.tsx +233 -0
- package/src/planner/fields.tsx +381 -0
- package/src/planner/format.ts +33 -0
- package/src/planner/home/DataAndPrivacyCard.tsx +56 -0
- package/src/planner/home/GettingStartedPaths.tsx +46 -0
- package/src/planner/home/GettingStartedReopener.tsx +32 -0
- package/src/planner/home/StartHereLinks.tsx +22 -0
- package/src/planner/home/WelcomeHero.tsx +39 -0
- package/src/planner/home/YourPlans.tsx +72 -0
- package/src/planner/home/importErrorMessage.ts +22 -0
- package/src/planner/home/startHereSlugs.ts +7 -0
- package/src/planner/home/useHomeData.ts +190 -0
- package/src/planner/home/useHomeMode.ts +47 -0
- package/src/planner/householdActions.ts +22 -0
- package/src/planner/insights/InsightCardView.tsx +340 -0
- package/src/planner/insights/InsightsPage.tsx +204 -0
- package/src/planner/insights/categoryLabels.ts +11 -0
- package/src/planner/learnLinks.ts +85 -0
- package/src/planner/marketModelPicker.ts +172 -0
- package/src/planner/optimizePageChart.ts +40 -0
- package/src/planner/optimizePageClaim.ts +64 -0
- package/src/planner/planCompleteness.ts +27 -0
- package/src/planner/planContextCore.ts +26 -0
- package/src/planner/planner.css +2304 -0
- package/src/planner/provenanceLinks.ts +25 -0
- package/src/planner/sections/AccountFields.tsx +872 -0
- package/src/planner/sections/AccountsSection.tsx +89 -0
- package/src/planner/sections/AllocationPanel.tsx +261 -0
- package/src/planner/sections/AssumptionsSection.tsx +256 -0
- package/src/planner/sections/HouseholdSection.tsx +243 -0
- package/src/planner/sections/IncomeFloorSection.tsx +418 -0
- package/src/planner/sections/IncomeSection.tsx +170 -0
- package/src/planner/sections/InsuranceSection.tsx +362 -0
- package/src/planner/sections/SpendingSection.tsx +904 -0
- package/src/planner/sections/StrategySection.tsx +349 -0
- package/src/planner/sections/UpdateBalancesPanel.tsx +182 -0
- package/src/planner/sections/sectionHelpers.ts +48 -0
- package/src/planner/sections/shared.tsx +15 -0
- package/src/planner/sections.tsx +15 -0
- package/src/planner/ssAnalysis.ts +325 -0
- package/src/planner/successBand.ts +20 -0
- package/src/planner/survivorAnalysis.ts +277 -0
- package/src/planner/usStates.ts +19 -0
- package/src/planner/useMcSuccessRate.ts +77 -0
- package/src/planner/useProjection.ts +63 -0
- package/src/relocation/messages.ts +21 -0
- package/src/relocation/relocation.worker.ts +18 -0
- package/src/relocation/runRelocation.ts +17 -0
- package/src/relocation/runner.ts +22 -0
- package/src/report/brandingContext.ts +15 -0
- package/src/report/downloadReport.ts +34 -0
- package/src/report/reportHtml.ts +547 -0
- package/src/routes/LearnRoutes.tsx +46 -0
- package/src/routes/PlanRoutes.tsx +55 -0
- package/src/routes/RouteFallback.tsx +9 -0
- package/src/socialSecurity/breakEven.ts +107 -0
- package/src/socialSecurity/expectedPv.ts +164 -0
- package/src/socialSecurity/explain.ts +92 -0
- package/src/socialSecurity/ficaReturn.ts +81 -0
- package/src/socialSecurity/persistedSsGuard.ts +138 -0
- package/src/socialSecurity/ssFormUtils.ts +48 -0
- package/src/socialSecurity/ssaStatementXml.ts +156 -0
- package/src/socialSecurity/storage.ts +69 -0
- package/src/socialSecurity/survivorSwitching.ts +153 -0
- package/src/testSupport/samplePlan.ts +2 -0
- package/src/workers/run.ts +45 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learning Center registry (V9 PR1).
|
|
3
|
+
*
|
|
4
|
+
* Articles are authored as structured TypeScript so prose, visuals (added in
|
|
5
|
+
* PR2), and metadata live together, bundle for offline use, and stay type-safe.
|
|
6
|
+
* This module owns the article metadata model, the category set, the topic
|
|
7
|
+
* inventory, and small selectors used by the Learning Center pages.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type LearningCategoryId =
|
|
11
|
+
| 'start-here'
|
|
12
|
+
| 'using-retiregolden'
|
|
13
|
+
| 'example-plans'
|
|
14
|
+
| 'accounts-saving'
|
|
15
|
+
| 'taxes'
|
|
16
|
+
| 'social-security'
|
|
17
|
+
| 'healthcare'
|
|
18
|
+
| 'withdrawals-roth'
|
|
19
|
+
| 'risk-uncertainty'
|
|
20
|
+
| 'insurance-estate'
|
|
21
|
+
| 'early-investing-fire'
|
|
22
|
+
| 'assumptions'
|
|
23
|
+
| 'glossary'
|
|
24
|
+
|
|
25
|
+
export type ArticleStatus = 'stub' | 'draft' | 'ready' | 'needs-review'
|
|
26
|
+
export type ReviewCadence = 'annual' | 'rule-change' | 'stable'
|
|
27
|
+
export type Audience = 'beginner' | 'intermediate'
|
|
28
|
+
export type Priority = 'P0' | 'P1' | 'P2'
|
|
29
|
+
|
|
30
|
+
/** One variable in a {@link FormulaBlockData}: the symbol and what it means. */
|
|
31
|
+
export type FormulaVariable = { symbol: string; meaning: string }
|
|
32
|
+
|
|
33
|
+
/** One labelled assumption row in a {@link ScenarioBlockData}. */
|
|
34
|
+
export type ScenarioAssumption = { label: string; value: string }
|
|
35
|
+
|
|
36
|
+
/** A teaching figure: a registered chart or an image, with a text equivalent. */
|
|
37
|
+
export type FigureBlockData = {
|
|
38
|
+
type: 'figure'
|
|
39
|
+
/** Key into the chart registry (learn/components/charts). */
|
|
40
|
+
chartId?: string
|
|
41
|
+
/** Static image alternative to a chart. */
|
|
42
|
+
image?: { src: string }
|
|
43
|
+
/** Short descriptive caption shown under the figure. */
|
|
44
|
+
caption: string
|
|
45
|
+
/** Text equivalent of the figure for screen readers and no-image fallback. */
|
|
46
|
+
alt: string
|
|
47
|
+
/** Optional note when the figure is based on rules, data, or an assumption. */
|
|
48
|
+
sourceNote?: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type FormulaBlockData = {
|
|
52
|
+
type: 'formula'
|
|
53
|
+
/** The formula itself, e.g. "real = nominal ÷ (1 + inflation)^years". */
|
|
54
|
+
expression: string
|
|
55
|
+
/** Each symbol used in the expression and its plain-language meaning. */
|
|
56
|
+
where?: FormulaVariable[]
|
|
57
|
+
/** Whether dollars in the formula are nominal or in today's dollars. */
|
|
58
|
+
basis?: 'nominal' | 'today'
|
|
59
|
+
/** What the simple formula leaves out. */
|
|
60
|
+
note?: string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type ScenarioBlockData = {
|
|
64
|
+
type: 'scenario'
|
|
65
|
+
/** A short name for the example household, e.g. "The Reyes household". */
|
|
66
|
+
name: string
|
|
67
|
+
assumptions: ScenarioAssumption[]
|
|
68
|
+
/** Optional plain-language wrap-up of what the scenario shows. */
|
|
69
|
+
summary?: string
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type TableBlockData = {
|
|
73
|
+
type: 'table'
|
|
74
|
+
caption?: string
|
|
75
|
+
/** Column headers; the first column is treated as a row header. */
|
|
76
|
+
columns: string[]
|
|
77
|
+
/** Rows of cells, aligned to `columns`. Cells support inline markdown. */
|
|
78
|
+
rows: string[][]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Content blocks for a `ready` article. Text blocks render plain; the richer
|
|
83
|
+
* blocks (figure, formula, scenario, table) render through dedicated components
|
|
84
|
+
* in learn/components.
|
|
85
|
+
*/
|
|
86
|
+
export type ArticleBlock =
|
|
87
|
+
| { type: 'prose'; md: string }
|
|
88
|
+
| { type: 'heading'; text: string; level?: 2 | 3 }
|
|
89
|
+
| { type: 'list'; ordered?: boolean; items: string[] }
|
|
90
|
+
| { type: 'callout'; tone?: 'note' | 'warn'; md: string }
|
|
91
|
+
| FigureBlockData
|
|
92
|
+
| FormulaBlockData
|
|
93
|
+
| ScenarioBlockData
|
|
94
|
+
| TableBlockData
|
|
95
|
+
|
|
96
|
+
export type LearningArticle = {
|
|
97
|
+
slug: string
|
|
98
|
+
title: string
|
|
99
|
+
description: string
|
|
100
|
+
category: LearningCategoryId
|
|
101
|
+
tags: string[]
|
|
102
|
+
audience: Audience
|
|
103
|
+
status: ArticleStatus
|
|
104
|
+
/** ISO date (YYYY-MM-DD) the content was last reviewed. */
|
|
105
|
+
lastReviewed: string
|
|
106
|
+
reviewCadence: ReviewCadence
|
|
107
|
+
sourceUrls: string[]
|
|
108
|
+
/** Slugs of related articles. */
|
|
109
|
+
relatedArticles: string[]
|
|
110
|
+
/** Planner route patterns this article relates to (see KNOWN_PLANNER_ROUTES). */
|
|
111
|
+
relatedPlannerRoutes: string[]
|
|
112
|
+
/** True when the article leans on current-year dollar figures or rules. */
|
|
113
|
+
currentYearSensitive: boolean
|
|
114
|
+
priority?: Priority
|
|
115
|
+
/** Surfaced in the Learning Center home "Featured" strip. */
|
|
116
|
+
featured?: boolean
|
|
117
|
+
/** Present once an article reaches `draft`/`ready`/`needs-review`. */
|
|
118
|
+
blocks?: ArticleBlock[]
|
|
119
|
+
/** When set, ArticleShell offers "Open this example in the planner". */
|
|
120
|
+
exampleId?: string
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export type LearningCategory = {
|
|
124
|
+
id: LearningCategoryId
|
|
125
|
+
label: string
|
|
126
|
+
blurb: string
|
|
127
|
+
/** Glossary is browsed via /learn/glossary rather than per-article pages. */
|
|
128
|
+
isGlossary?: boolean
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export const LEARNING_CATEGORIES: LearningCategory[] = [
|
|
132
|
+
{ id: 'start-here', label: 'Start Here', blurb: 'Basics, vocabulary, and how to read a retirement plan.' },
|
|
133
|
+
{ id: 'using-retiregolden', label: 'Using RetireGolden', blurb: 'Tool-specific workflows and screen explanations.' },
|
|
134
|
+
{
|
|
135
|
+
id: 'assumptions',
|
|
136
|
+
label: 'Assumptions',
|
|
137
|
+
blurb: 'The forward-looking defaults behind your plan — inflation, returns, longevity — and the sources for each.',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'example-plans',
|
|
141
|
+
label: 'Example Plans',
|
|
142
|
+
blurb: 'Worked example households you can open in the planner.',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: 'accounts-saving',
|
|
146
|
+
label: 'Accounts and Saving',
|
|
147
|
+
blurb: 'Account types, contributions, savings order, and employer match.',
|
|
148
|
+
},
|
|
149
|
+
{ id: 'taxes', label: 'Taxes', blurb: 'Brackets, deductions, MAGI, capital gains, state tax, and cliffs.' },
|
|
150
|
+
{
|
|
151
|
+
id: 'social-security',
|
|
152
|
+
label: 'Social Security',
|
|
153
|
+
blurb: 'PIA, claiming age, spousal and survivor benefits, and break-even.',
|
|
154
|
+
},
|
|
155
|
+
{ id: 'healthcare', label: 'Healthcare', blurb: 'ACA, Medicare, IRMAA, HSAs, and long-term-care costs.' },
|
|
156
|
+
{
|
|
157
|
+
id: 'withdrawals-roth',
|
|
158
|
+
label: 'Withdrawals and Roth',
|
|
159
|
+
blurb: 'Withdrawal order, Roth conversions, the optimizer, RMDs, and QCDs.',
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: 'risk-uncertainty',
|
|
163
|
+
label: 'Risk and Uncertainty',
|
|
164
|
+
blurb: 'Monte Carlo, sequence risk, inflation, and longevity.',
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: 'insurance-estate',
|
|
168
|
+
label: 'Insurance and Estate',
|
|
169
|
+
blurb: 'LTC insurance, permanent life, survivor planning, and heirs.',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: 'early-investing-fire',
|
|
173
|
+
label: 'Early Investing & FIRE',
|
|
174
|
+
blurb: 'Saving in your 20s–40s, savings rate, the FI number, and early-retirement strategies.',
|
|
175
|
+
},
|
|
176
|
+
{ id: 'glossary', label: 'Glossary', blurb: 'Plain-language definitions of terms used across the app.', isGlossary: true },
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Planner route patterns an article may reference. Used by the registry-integrity
|
|
181
|
+
* test so `relatedPlannerRoutes` cannot drift from real app routes.
|
|
182
|
+
*/
|
|
183
|
+
export const KNOWN_PLANNER_ROUTES = [
|
|
184
|
+
'/',
|
|
185
|
+
'/compare',
|
|
186
|
+
'/import',
|
|
187
|
+
'/plan/:planId/household',
|
|
188
|
+
'/plan/:planId/social-security',
|
|
189
|
+
'/plan/:planId/accounts',
|
|
190
|
+
'/plan/:planId/insurance',
|
|
191
|
+
'/plan/:planId/income',
|
|
192
|
+
'/plan/:planId/income-floor',
|
|
193
|
+
'/plan/:planId/spending',
|
|
194
|
+
'/plan/:planId/strategy',
|
|
195
|
+
'/plan/:planId/assumptions',
|
|
196
|
+
'/plan/:planId/insights',
|
|
197
|
+
'/plan/:planId/social-security-analysis',
|
|
198
|
+
'/plan/:planId/survivor',
|
|
199
|
+
'/plan/:planId/results',
|
|
200
|
+
'/plan/:planId/monte-carlo',
|
|
201
|
+
'/plan/:planId/scenarios',
|
|
202
|
+
'/plan/:planId/relocation',
|
|
203
|
+
'/plan/:planId/optimize',
|
|
204
|
+
'/plan/:planId/spending-solver',
|
|
205
|
+
'/plan/:planId/report',
|
|
206
|
+
] as const
|
|
207
|
+
|
|
208
|
+
// Articles live in ./content as pure data, keeping this module free of
|
|
209
|
+
// JSX/content concerns.
|
|
210
|
+
import { aboutRetireGoldenArticle } from './content/about-retiregolden'
|
|
211
|
+
import { accountTypesOverviewArticle } from './content/account-types-overview'
|
|
212
|
+
import { acaPremiumTaxCreditsAndMagiArticle } from './content/aca-premium-tax-credits-and-magi'
|
|
213
|
+
import { appealingIrmaaSsa44Article } from './content/appealing-irmaa-ssa-44'
|
|
214
|
+
import { afterTaxEstateArticle } from './content/after-tax-estate'
|
|
215
|
+
import { agiMagiAndTaxableIncomeArticle } from './content/agi-magi-and-taxable-income'
|
|
216
|
+
import { beneficiariesAccountTitlingArticle } from './content/beneficiaries-and-account-titling'
|
|
217
|
+
import { breakEvenUsefulLensArticle } from './content/break-even-useful-lens'
|
|
218
|
+
import { buildingRetirementSpendingBudgetArticle } from './content/building-a-retirement-spending-budget'
|
|
219
|
+
import { colaInflationProtectionArticle } from './content/cola-and-inflation-protection'
|
|
220
|
+
import { divorcedSpousalSurvivorRecordsArticle } from './content/divorced-spousal-and-survivor-records'
|
|
221
|
+
import { dynamicSpendingGuardrailsArticle } from './content/dynamic-spending-guardrails'
|
|
222
|
+
import { employerMatchAndContributionOrderArticle } from './content/employer-match-and-contribution-order'
|
|
223
|
+
import { earningsTestBeforeFraArticle } from './content/earnings-test-before-fra'
|
|
224
|
+
import { EXAMPLE_PLAN_ARTICLES } from './content/examplePlanArticles'
|
|
225
|
+
import { feesExpenseRatiosCompoundingDragArticle } from './content/fees-expense-ratios-and-compounding-drag'
|
|
226
|
+
import { fillingTaxBracketArticle } from './content/filling-a-tax-bracket-with-roth-conversions'
|
|
227
|
+
import { healthcareAfter65Article } from './content/healthcare-after-65'
|
|
228
|
+
import { healthcareBefore65Article } from './content/healthcare-before-65'
|
|
229
|
+
import { historicalVsRandomReturnModelsArticle } from './content/historical-vs-random-return-models'
|
|
230
|
+
import { howAssumptionsChangeAnswerArticle } from './content/how-assumptions-change-the-answer'
|
|
231
|
+
import { howMuchCanISpendArticle } from './content/how-much-can-i-spend'
|
|
232
|
+
import { howSocialSecurityIsTaxedArticle } from './content/how-social-security-is-taxed'
|
|
233
|
+
import { howTheOptimizerThinksArticle } from './content/how-the-optimizer-thinks'
|
|
234
|
+
import { hsasQualifiedMedicalExpensesArticle } from './content/hsas-and-qualified-medical-expenses'
|
|
235
|
+
import { hsasAsRetirementAccountsArticle } from './content/hsas-as-retirement-accounts'
|
|
236
|
+
import { optimizerAfterTaxEstateArticle } from './content/how-the-optimizer-values-after-tax-estate'
|
|
237
|
+
import { howToReadProjectionArticle } from './content/how-to-read-a-retirement-projection'
|
|
238
|
+
import { inflationRiskArticle } from './content/inflation-risk'
|
|
239
|
+
import { inheritedIraTenYearRuleArticle } from './content/inherited-ira-10-year-rule'
|
|
240
|
+
import { insuranceInYourRetirementPlanArticle } from './content/insurance-in-your-retirement-plan'
|
|
241
|
+
import { irmaaTwoYearLookbackArticle } from './content/irmaa-two-year-lookback'
|
|
242
|
+
import { longevityRiskArticle } from './content/longevity-risk'
|
|
243
|
+
import { longTermCareCostsInsuranceArticle } from './content/long-term-care-costs-and-insurance'
|
|
244
|
+
import { longTermCareInsuranceRiskTransferArticle } from './content/long-term-care-insurance-as-risk-transfer'
|
|
245
|
+
import { medicarePartBVsPartDIrmaaArticle } from './content/medicare-part-b-vs-part-d-irmaa'
|
|
246
|
+
import { mortalityWeightedSocialSecurityArticle } from './content/mortality-weighted-social-security'
|
|
247
|
+
import { movingToRetireGoldenArticle } from './content/moving-to-retiregolden'
|
|
248
|
+
import { seedYourPlanFromYourTaxReturnArticle } from './content/seed-your-plan-from-your-tax-return'
|
|
249
|
+
import { marginalVsEffectiveTaxRateArticle } from './content/marginal-vs-effective-tax-rate'
|
|
250
|
+
import { niitHighIncomeInvestmentTaxArticle } from './content/niit-high-income-investment-tax'
|
|
251
|
+
import { ordinaryIncomeVsCapitalGainsArticle } from './content/ordinary-income-vs-capital-gains'
|
|
252
|
+
import { payingConversionTaxesTaxableVsIraArticle } from './content/paying-conversion-taxes-taxable-vs-ira'
|
|
253
|
+
import { piaAimeBendPointsArticle } from './content/pia-aime-and-bend-points'
|
|
254
|
+
import { planningForCouplesAndSurvivorYearsArticle } from './content/planning-for-couples-and-survivor-years'
|
|
255
|
+
import { plannerOverviewArticle } from './content/planner-overview'
|
|
256
|
+
import { pensionsAndAnnuitiesArticle } from './content/pensions-and-annuities'
|
|
257
|
+
import { permanentLifeInsurancePlanArticle } from './content/permanent-life-insurance-in-a-plan'
|
|
258
|
+
import { privacyWhatStaysInYourBrowserArticle } from './content/privacy-what-stays-in-your-browser'
|
|
259
|
+
import { qcdsQualifiedCharitableDistributionsArticle } from './content/qcds-qualified-charitable-distributions'
|
|
260
|
+
import { readingResultsPageArticle } from './content/reading-the-results-page'
|
|
261
|
+
import { readingSocialSecurityAnalysisPageArticle } from './content/reading-the-social-security-analysis-page'
|
|
262
|
+
import { realEstateHomeEquityDebtArticle } from './content/real-estate-home-equity-and-debt'
|
|
263
|
+
import { reportsCsvExportsAndSharingArticle } from './content/reports-csv-exports-and-sharing'
|
|
264
|
+
import { fundedRatioArticle } from './content/funded-ratio'
|
|
265
|
+
import { riskBasedGuardrailsArticle } from './content/risk-based-guardrails'
|
|
266
|
+
import { socialSecurityBridgeArticle } from './content/social-security-bridge'
|
|
267
|
+
import { tipsLaddersArticle } from './content/tips-ladders'
|
|
268
|
+
import { rmdsRequiredMinimumDistributionsArticle } from './content/rmds-required-minimum-distributions'
|
|
269
|
+
import { rothConversionBasicsArticle } from './content/roth-conversion-basics'
|
|
270
|
+
import { rsusAndEsppArticle } from './content/rsus-and-espp'
|
|
271
|
+
import { ruleOf55And72tArticle } from './content/rule-of-55-and-72t'
|
|
272
|
+
import { sensitivityTestingWhatChangesAnswerArticle } from './content/sensitivity-testing-what-changes-the-answer'
|
|
273
|
+
import { sequenceOfReturnsRiskArticle } from './content/sequence-of-returns-risk'
|
|
274
|
+
import { socialSecurityClaimingAgeArticle } from './content/social-security-claiming-age-basics'
|
|
275
|
+
import { socialSecurityTaxesVsBenefitsArticle } from './content/social-security-taxes-vs-benefits'
|
|
276
|
+
import { ssdiAndRetirementPlanningArticle } from './content/ssdi-and-retirement-planning'
|
|
277
|
+
import { spousalSurvivorBenefitsArticle } from './content/spousal-and-survivor-benefits'
|
|
278
|
+
import { spendingProfilesAndRetirementSmileArticle } from './content/spending-profiles-and-the-retirement-smile'
|
|
279
|
+
import { standardDeductionSeniorDeductionItemizingArticle } from './content/standard-deduction-senior-deduction-and-itemizing'
|
|
280
|
+
import { stateIncomeTaxesRetirementArticle } from './content/state-income-taxes-in-retirement'
|
|
281
|
+
import { stepUpInBasisArticle } from './content/step-up-in-basis'
|
|
282
|
+
import { survivorPlanningForCouplesArticle } from './content/survivor-planning-for-couples'
|
|
283
|
+
import { survivorSpendingInCouplePlansArticle } from './content/survivor-spending-in-couple-plans'
|
|
284
|
+
import { taxableBrokerageBasisAndCapitalGainsArticle } from './content/taxable-brokerage-basis-and-capital-gains'
|
|
285
|
+
import { taxCliffsBracketEdgesArticle } from './content/tax-cliffs-and-bracket-edges'
|
|
286
|
+
import { taxLossGainHarvestingArticle } from './content/tax-loss-and-gain-harvesting'
|
|
287
|
+
import { threeBigQuestionsSpendingTimeRiskArticle } from './content/three-big-questions-spending-time-risk'
|
|
288
|
+
import { todaysDollarsArticle } from './content/todays-dollars-vs-future-dollars'
|
|
289
|
+
import { traditionalVsRothContributionsArticle } from './content/traditional-vs-roth-contributions'
|
|
290
|
+
import { troubleshootingSurprisingResultsArticle } from './content/troubleshooting-surprising-results'
|
|
291
|
+
import { trustFundHaircutScenariosArticle } from './content/trust-fund-haircut-scenarios'
|
|
292
|
+
import { understandingMonteCarloArticle } from './content/understanding-monte-carlo-success-rate'
|
|
293
|
+
import { usingAssumptionsAndProvenanceArticle } from './content/using-assumptions-and-provenance'
|
|
294
|
+
import { usingScenariosToCompareChoicesArticle } from './content/using-scenarios-to-compare-choices'
|
|
295
|
+
import { whatChangesWhenYouMoveStatesArticle } from './content/what-changes-when-you-move-states'
|
|
296
|
+
import { widowsPenaltyAndSurvivorBracketsArticle } from './content/widows-penalty-and-survivor-brackets'
|
|
297
|
+
import { whatRetireGoldenModelsArticle } from './content/what-retiregolden-models'
|
|
298
|
+
import { whatMonteCarloProvesArticle } from './content/what-monte-carlo-proves'
|
|
299
|
+
import { whatRetirementHealthcareReallyCostsArticle } from './content/what-retirement-healthcare-really-costs'
|
|
300
|
+
import { withdrawalOrderBasicsArticle } from './content/withdrawal-order-basics'
|
|
301
|
+
import { why95PercentIsNotGuaranteeArticle } from './content/why-95-percent-is-not-a-guarantee'
|
|
302
|
+
import { rothConversionsRaiseOtherCostsArticle } from './content/why-roth-conversions-raise-other-costs'
|
|
303
|
+
import { whySmallTaxCliffsCanMatterArticle } from './content/why-small-tax-cliffs-can-matter'
|
|
304
|
+
import { whatIsFireArticle } from './content/what-is-fire'
|
|
305
|
+
import { savingsRateBiggestLeverArticle } from './content/savings-rate-biggest-lever'
|
|
306
|
+
import { fiNumberAndFourPercentRuleArticle } from './content/fi-number-and-four-percent-rule'
|
|
307
|
+
import { howToModelAccumulationArticle } from './content/how-to-model-accumulation'
|
|
308
|
+
import { understandingYourPlanAssumptionsArticle } from './content/understanding-your-plan-assumptions'
|
|
309
|
+
import { assumptionGeneralInflationArticle } from './content/assumption-general-inflation'
|
|
310
|
+
import { assumptionHealthcareInflationArticle } from './content/assumption-healthcare-inflation'
|
|
311
|
+
import { assumptionInvestmentReturnsArticle } from './content/assumption-investment-returns'
|
|
312
|
+
import { assumptionSocialSecurityColaArticle } from './content/assumption-social-security-cola'
|
|
313
|
+
import { assumptionSocialSecurityTrustFundArticle } from './content/assumption-social-security-trust-fund'
|
|
314
|
+
import { assumptionLongevityPlanningAgeArticle } from './content/assumption-longevity-planning-age'
|
|
315
|
+
import { assumptionStateTaxOverrideArticle } from './content/assumption-state-tax-override'
|
|
316
|
+
import { assumptionRecentMagiArticle } from './content/assumption-recent-magi'
|
|
317
|
+
import { assumptionHeirTaxRateArticle } from './content/assumption-heir-tax-rate'
|
|
318
|
+
|
|
319
|
+
/** Every article in registry order. */
|
|
320
|
+
export const LEARNING_ARTICLES: LearningArticle[] = [
|
|
321
|
+
aboutRetireGoldenArticle,
|
|
322
|
+
howToReadProjectionArticle,
|
|
323
|
+
todaysDollarsArticle,
|
|
324
|
+
whatRetireGoldenModelsArticle,
|
|
325
|
+
readingResultsPageArticle,
|
|
326
|
+
understandingMonteCarloArticle,
|
|
327
|
+
rothConversionBasicsArticle,
|
|
328
|
+
fillingTaxBracketArticle,
|
|
329
|
+
optimizerAfterTaxEstateArticle,
|
|
330
|
+
marginalVsEffectiveTaxRateArticle,
|
|
331
|
+
rothConversionsRaiseOtherCostsArticle,
|
|
332
|
+
socialSecurityClaimingAgeArticle,
|
|
333
|
+
piaAimeBendPointsArticle,
|
|
334
|
+
breakEvenUsefulLensArticle,
|
|
335
|
+
spousalSurvivorBenefitsArticle,
|
|
336
|
+
ssdiAndRetirementPlanningArticle,
|
|
337
|
+
socialSecurityTaxesVsBenefitsArticle,
|
|
338
|
+
earningsTestBeforeFraArticle,
|
|
339
|
+
colaInflationProtectionArticle,
|
|
340
|
+
divorcedSpousalSurvivorRecordsArticle,
|
|
341
|
+
dynamicSpendingGuardrailsArticle,
|
|
342
|
+
riskBasedGuardrailsArticle,
|
|
343
|
+
tipsLaddersArticle,
|
|
344
|
+
socialSecurityBridgeArticle,
|
|
345
|
+
fundedRatioArticle,
|
|
346
|
+
buildingRetirementSpendingBudgetArticle,
|
|
347
|
+
spendingProfilesAndRetirementSmileArticle,
|
|
348
|
+
survivorSpendingInCouplePlansArticle,
|
|
349
|
+
howMuchCanISpendArticle,
|
|
350
|
+
trustFundHaircutScenariosArticle,
|
|
351
|
+
mortalityWeightedSocialSecurityArticle,
|
|
352
|
+
irmaaTwoYearLookbackArticle,
|
|
353
|
+
appealingIrmaaSsa44Article,
|
|
354
|
+
acaPremiumTaxCreditsAndMagiArticle,
|
|
355
|
+
agiMagiAndTaxableIncomeArticle,
|
|
356
|
+
healthcareBefore65Article,
|
|
357
|
+
healthcareAfter65Article,
|
|
358
|
+
whatRetirementHealthcareReallyCostsArticle,
|
|
359
|
+
hsasQualifiedMedicalExpensesArticle,
|
|
360
|
+
longTermCareCostsInsuranceArticle,
|
|
361
|
+
medicarePartBVsPartDIrmaaArticle,
|
|
362
|
+
withdrawalOrderBasicsArticle,
|
|
363
|
+
rmdsRequiredMinimumDistributionsArticle,
|
|
364
|
+
qcdsQualifiedCharitableDistributionsArticle,
|
|
365
|
+
widowsPenaltyAndSurvivorBracketsArticle,
|
|
366
|
+
payingConversionTaxesTaxableVsIraArticle,
|
|
367
|
+
ruleOf55And72tArticle,
|
|
368
|
+
inheritedIraTenYearRuleArticle,
|
|
369
|
+
accountTypesOverviewArticle,
|
|
370
|
+
traditionalVsRothContributionsArticle,
|
|
371
|
+
employerMatchAndContributionOrderArticle,
|
|
372
|
+
hsasAsRetirementAccountsArticle,
|
|
373
|
+
taxableBrokerageBasisAndCapitalGainsArticle,
|
|
374
|
+
pensionsAndAnnuitiesArticle,
|
|
375
|
+
realEstateHomeEquityDebtArticle,
|
|
376
|
+
rsusAndEsppArticle,
|
|
377
|
+
feesExpenseRatiosCompoundingDragArticle,
|
|
378
|
+
longTermCareInsuranceRiskTransferArticle,
|
|
379
|
+
permanentLifeInsurancePlanArticle,
|
|
380
|
+
insuranceInYourRetirementPlanArticle,
|
|
381
|
+
survivorPlanningForCouplesArticle,
|
|
382
|
+
afterTaxEstateArticle,
|
|
383
|
+
stepUpInBasisArticle,
|
|
384
|
+
beneficiariesAccountTitlingArticle,
|
|
385
|
+
sequenceOfReturnsRiskArticle,
|
|
386
|
+
whatMonteCarloProvesArticle,
|
|
387
|
+
historicalVsRandomReturnModelsArticle,
|
|
388
|
+
inflationRiskArticle,
|
|
389
|
+
longevityRiskArticle,
|
|
390
|
+
threeBigQuestionsSpendingTimeRiskArticle,
|
|
391
|
+
howAssumptionsChangeAnswerArticle,
|
|
392
|
+
planningForCouplesAndSurvivorYearsArticle,
|
|
393
|
+
whySmallTaxCliffsCanMatterArticle,
|
|
394
|
+
plannerOverviewArticle,
|
|
395
|
+
usingScenariosToCompareChoicesArticle,
|
|
396
|
+
readingSocialSecurityAnalysisPageArticle,
|
|
397
|
+
usingAssumptionsAndProvenanceArticle,
|
|
398
|
+
reportsCsvExportsAndSharingArticle,
|
|
399
|
+
privacyWhatStaysInYourBrowserArticle,
|
|
400
|
+
movingToRetireGoldenArticle,
|
|
401
|
+
seedYourPlanFromYourTaxReturnArticle,
|
|
402
|
+
troubleshootingSurprisingResultsArticle,
|
|
403
|
+
howTheOptimizerThinksArticle,
|
|
404
|
+
ordinaryIncomeVsCapitalGainsArticle,
|
|
405
|
+
standardDeductionSeniorDeductionItemizingArticle,
|
|
406
|
+
howSocialSecurityIsTaxedArticle,
|
|
407
|
+
niitHighIncomeInvestmentTaxArticle,
|
|
408
|
+
stateIncomeTaxesRetirementArticle,
|
|
409
|
+
whatChangesWhenYouMoveStatesArticle,
|
|
410
|
+
taxCliffsBracketEdgesArticle,
|
|
411
|
+
taxLossGainHarvestingArticle,
|
|
412
|
+
why95PercentIsNotGuaranteeArticle,
|
|
413
|
+
sensitivityTestingWhatChangesAnswerArticle,
|
|
414
|
+
whatIsFireArticle,
|
|
415
|
+
savingsRateBiggestLeverArticle,
|
|
416
|
+
fiNumberAndFourPercentRuleArticle,
|
|
417
|
+
howToModelAccumulationArticle,
|
|
418
|
+
understandingYourPlanAssumptionsArticle,
|
|
419
|
+
assumptionGeneralInflationArticle,
|
|
420
|
+
assumptionHealthcareInflationArticle,
|
|
421
|
+
assumptionInvestmentReturnsArticle,
|
|
422
|
+
assumptionSocialSecurityColaArticle,
|
|
423
|
+
assumptionSocialSecurityTrustFundArticle,
|
|
424
|
+
assumptionLongevityPlanningAgeArticle,
|
|
425
|
+
assumptionStateTaxOverrideArticle,
|
|
426
|
+
assumptionRecentMagiArticle,
|
|
427
|
+
assumptionHeirTaxRateArticle,
|
|
428
|
+
...EXAMPLE_PLAN_ARTICLES,
|
|
429
|
+
]
|
|
430
|
+
|
|
431
|
+
const ARTICLES_BY_SLUG = new Map(LEARNING_ARTICLES.map((a) => [a.slug, a]))
|
|
432
|
+
|
|
433
|
+
export function getArticle(slug: string): LearningArticle | undefined {
|
|
434
|
+
return ARTICLES_BY_SLUG.get(slug)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** An article is browsable as its own page only once it has real content. */
|
|
438
|
+
export function isReadable(article: LearningArticle): boolean {
|
|
439
|
+
return article.status === 'ready' && (article.blocks?.length ?? 0) > 0
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
export function getCategory(id: LearningCategoryId): LearningCategory | undefined {
|
|
443
|
+
return LEARNING_CATEGORIES.find((c) => c.id === id)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
export function articlesInCategory(id: LearningCategoryId): LearningArticle[] {
|
|
447
|
+
return LEARNING_ARTICLES.filter((a) => a.category === id)
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Readable articles that relate to a planner route, for contextual "Learn about
|
|
452
|
+
* this screen" links. Derived from each article's `relatedPlannerRoutes` so the
|
|
453
|
+
* planner↔article wiring has a single source of truth and a screen's links
|
|
454
|
+
* light up automatically as content lands. Stubs are excluded so a contextual
|
|
455
|
+
* link never dead-ends on a "Planned article" placeholder.
|
|
456
|
+
*/
|
|
457
|
+
export function articlesForRoute(routePattern: string): LearningArticle[] {
|
|
458
|
+
return LEARNING_ARTICLES.filter((a) => isReadable(a) && a.relatedPlannerRoutes.includes(routePattern))
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/** Non-glossary categories paired with their article counts, for the home grid. */
|
|
462
|
+
export function categorySummaries(): Array<{ category: LearningCategory; count: number }> {
|
|
463
|
+
return LEARNING_CATEGORIES.filter((c) => !c.isGlossary).map((category) => ({
|
|
464
|
+
category,
|
|
465
|
+
count: articlesInCategory(category.id).length,
|
|
466
|
+
}))
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
export function featuredArticles(): LearningArticle[] {
|
|
470
|
+
return LEARNING_ARTICLES.filter((a) => a.featured)
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/** Readable articles, most recently reviewed first. */
|
|
474
|
+
export function recentlyReviewed(limit = 5): LearningArticle[] {
|
|
475
|
+
return LEARNING_ARTICLES.filter(isReadable)
|
|
476
|
+
.slice()
|
|
477
|
+
.sort((a, b) => b.lastReviewed.localeCompare(a.lastReviewed))
|
|
478
|
+
.slice(0, limit)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const PRIORITY_ORDER: Record<Priority, number> = { P0: 0, P1: 1, P2: 2 }
|
|
482
|
+
|
|
483
|
+
/** Sort for listing: readable first, then by priority, then alphabetically. */
|
|
484
|
+
export function byListingOrder(a: LearningArticle, b: LearningArticle): number {
|
|
485
|
+
const ra = isReadable(a) ? 0 : 1
|
|
486
|
+
const rb = isReadable(b) ? 0 : 1
|
|
487
|
+
if (ra !== rb) return ra - rb
|
|
488
|
+
const pa = a.priority ? PRIORITY_ORDER[a.priority] : 3
|
|
489
|
+
const pb = b.priority ? PRIORITY_ORDER[b.priority] : 3
|
|
490
|
+
if (pa !== pb) return pa - pb
|
|
491
|
+
return a.title.localeCompare(b.title)
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/** Case-insensitive search across title, description, and tags. */
|
|
495
|
+
export function searchArticles(query: string): LearningArticle[] {
|
|
496
|
+
const q = query.trim().toLowerCase()
|
|
497
|
+
if (!q) return []
|
|
498
|
+
return LEARNING_ARTICLES.filter((a) => {
|
|
499
|
+
const haystack = [a.title, a.description, ...a.tags].join(' ').toLowerCase()
|
|
500
|
+
return haystack.includes(q)
|
|
501
|
+
}).sort(byListingOrder)
|
|
502
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { BASELINE_CITATION } from './constants'
|
|
2
|
+
import type { LongevityPersisted } from '@retiregolden/engine/longevity/types'
|
|
3
|
+
|
|
4
|
+
export interface LongevityResultsProps {
|
|
5
|
+
data: LongevityPersisted
|
|
6
|
+
onEdit: () => void
|
|
7
|
+
/** Clear persisted storage for this profile and reset parent UI. */
|
|
8
|
+
onClear: () => void
|
|
9
|
+
/** Overrides the main results `<h2>` text */
|
|
10
|
+
resultsHeading?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function LongevityResults({ data, onEdit, onClear, resultsHeading }: LongevityResultsProps) {
|
|
14
|
+
const { answers, result, updatedAt } = data
|
|
15
|
+
const updated = new Date(updatedAt).toLocaleString()
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div className="results">
|
|
19
|
+
<section className="disclaimer-box" aria-label="Disclaimer">
|
|
20
|
+
<p>
|
|
21
|
+
<strong>Educational estimate only.</strong> Not medical advice. This tool applies simple
|
|
22
|
+
adjustments to a population life table; it cannot predict your individual lifespan.
|
|
23
|
+
</p>
|
|
24
|
+
</section>
|
|
25
|
+
|
|
26
|
+
<section className="results-hero">
|
|
27
|
+
<h2>{resultsHeading ?? 'Estimated remaining years'}</h2>
|
|
28
|
+
<p className="results-central" aria-live="polite">
|
|
29
|
+
About <strong>{result.centralRemainingYears.toFixed(1)}</strong> years
|
|
30
|
+
</p>
|
|
31
|
+
<p className="muted">
|
|
32
|
+
Illustrative band (not a statistical confidence interval):{' '}
|
|
33
|
+
<strong>
|
|
34
|
+
{result.bandLowRemainingYears.toFixed(1)} – {result.bandHighRemainingYears.toFixed(1)}
|
|
35
|
+
</strong>{' '}
|
|
36
|
+
years remaining
|
|
37
|
+
</p>
|
|
38
|
+
<p className="muted">
|
|
39
|
+
For planning visuals only: living to roughly age{' '}
|
|
40
|
+
<strong>{result.illustrativePlanningAge}</strong> aligns with the central estimate above
|
|
41
|
+
(rounded).
|
|
42
|
+
</p>
|
|
43
|
+
</section>
|
|
44
|
+
|
|
45
|
+
<section className="results-detail">
|
|
46
|
+
<h3>How we got this</h3>
|
|
47
|
+
<ul className="results-list">
|
|
48
|
+
<li>
|
|
49
|
+
<strong>Population baseline:</strong> {result.baselineRemainingYears.toFixed(2)} remaining
|
|
50
|
+
years at age {answers.age} ({answers.sex === 'average' ? 'average of male/female' : `${answers.sex} table`}).
|
|
51
|
+
</li>
|
|
52
|
+
<li>
|
|
53
|
+
<strong>Lifestyle / health factor (combined):</strong>{' '}
|
|
54
|
+
{(result.appliedMultiplier * 100).toFixed(1)}% of baseline (raw {(result.rawMultiplier * 100).toFixed(1)}%,
|
|
55
|
+
clamped for stability).
|
|
56
|
+
</li>
|
|
57
|
+
<li>
|
|
58
|
+
<strong>Source:</strong>{' '}
|
|
59
|
+
<a href={BASELINE_CITATION.url} target="_blank" rel="noreferrer">
|
|
60
|
+
{BASELINE_CITATION.label}
|
|
61
|
+
</a>
|
|
62
|
+
. {BASELINE_CITATION.note}
|
|
63
|
+
</li>
|
|
64
|
+
</ul>
|
|
65
|
+
</section>
|
|
66
|
+
|
|
67
|
+
<p className="muted small">Last saved locally: {updated}</p>
|
|
68
|
+
|
|
69
|
+
<div className="wizard-actions">
|
|
70
|
+
<button type="button" className="btn btn-secondary" onClick={onEdit}>
|
|
71
|
+
Edit answers
|
|
72
|
+
</button>
|
|
73
|
+
<button
|
|
74
|
+
type="button"
|
|
75
|
+
className="btn btn-secondary"
|
|
76
|
+
onClick={() => {
|
|
77
|
+
onClear()
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
Clear saved data
|
|
81
|
+
</button>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
)
|
|
85
|
+
}
|