@retiregolden/planner-ui 0.1.0 → 0.3.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/README.md +177 -6
- package/package.json +4 -1
- package/src/App.tsx +24 -76
- package/src/data/PlanStoreProvider.tsx +13 -0
- package/src/data/planFormat.ts +89 -0
- package/src/data/planStore.ts +65 -16
- package/src/data/planStoreContext.ts +138 -0
- package/src/data/v2Backup.ts +21 -70
- package/src/import/ImportPage.tsx +3 -2
- package/src/index.ts +23 -0
- package/src/planner/ComparePlansPage.tsx +6 -5
- package/src/planner/PlanContext.tsx +7 -6
- package/src/planner/PlanWorkspace.tsx +3 -2
- package/src/planner/ResultsPage.tsx +25 -31
- package/src/planner/SurvivalPercentileModal.tsx +1 -1
- package/src/planner/chartTooltip.tsx +18 -0
- package/src/planner/examples/ExampleLibrary.tsx +5 -1
- package/src/planner/examples/ExamplePreviewBanner.tsx +3 -1
- package/src/planner/examples/loadExample.ts +35 -3
- package/src/planner/home/DataAndPrivacyCard.tsx +1 -1
- package/src/planner/home/YourPlans.tsx +1 -1
- package/src/planner/home/useHomeData.ts +29 -18
- package/src/planner/home/useHomeMode.ts +1 -1
- package/src/planner/insights/InsightCardView.tsx +5 -5
- package/src/planner/insights/InsightsPage.tsx +6 -5
- package/src/planner/marketModelPicker.ts +1 -1
- package/src/report/ReportBrandingProvider.tsx +14 -0
- package/src/report/reportHtml.ts +174 -226
- package/src/report/reportModel.ts +715 -0
- package/src/routes/groups.tsx +75 -0
- package/src/routes/lazyPages.tsx +18 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route-level exports: the planner's route table split into host-mountable
|
|
3
|
+
* groups. `<PlannerApp/>` composes all three; a host that owns its own
|
|
4
|
+
* plans-management chrome mounts the workspace (and usually content) groups
|
|
5
|
+
* under its router and omits the home group entirely.
|
|
6
|
+
*
|
|
7
|
+
* The groups are react-router v7 `RouteObject[]` arrays — the shape that
|
|
8
|
+
* spreads into `useRoutes` or feeds `createBrowserRouter` — rather than
|
|
9
|
+
* `<Route>` fragments, which can't cross a component boundary (`<Routes>`
|
|
10
|
+
* only reads `<Route>` children it can see literally).
|
|
11
|
+
*
|
|
12
|
+
* **Mount the groups at the host router's root.** To serve the planner under
|
|
13
|
+
* a URL prefix, put the prefix in the router's `basename` — the planner's
|
|
14
|
+
* pages navigate with root-absolute paths (`/plan/:id/…`, `/compare`,
|
|
15
|
+
* `/learn/…`), which react-router resolves against the basename. Nesting the
|
|
16
|
+
* groups under a parent route path (e.g. `path: 'planner/*'`) is NOT
|
|
17
|
+
* supported: the initial deep link would render, but the first in-app
|
|
18
|
+
* navigation would escape the prefix.
|
|
19
|
+
*
|
|
20
|
+
* Everything here stays chrome-free: pages only, no header/nav/footer and no
|
|
21
|
+
* document.title management for non-plan routes (plan routes retitle
|
|
22
|
+
* themselves in PlanWorkspace). The host owns that, as `<PlannerApp/>` does
|
|
23
|
+
* for the web app.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { Suspense, type ReactNode } from 'react'
|
|
27
|
+
import { Navigate, type RouteObject } from 'react-router-dom'
|
|
28
|
+
|
|
29
|
+
import { PlanPickerPage } from '../planner/PlanPickerPage'
|
|
30
|
+
import { DisclaimerPage } from '../planner/DisclaimerPage'
|
|
31
|
+
import { RouteFallback } from './RouteFallback'
|
|
32
|
+
import { ComparePlansPage, ExamplesPage, HowTestedPage, ImportPage, LearnRoutes, PlanRoutes } from './lazyPages'
|
|
33
|
+
|
|
34
|
+
function suspended(children: ReactNode) {
|
|
35
|
+
return <Suspense fallback={<RouteFallback />}>{children}</Suspense>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The plan workspace: everything under `/plan/:planId/*` (entry sections,
|
|
40
|
+
* results, Monte Carlo, scenarios, survivor, relocation, the optimizers, the
|
|
41
|
+
* report) plus cross-plan Compare. Deep links work when this group is
|
|
42
|
+
* mounted alone. Workspace pages link to `/` ("Your plans") and into the
|
|
43
|
+
* content group (`/disclaimer`, `/how-tested`, Learn) — a host mounts or
|
|
44
|
+
* redirects those paths as it sees fit.
|
|
45
|
+
*/
|
|
46
|
+
export const plannerWorkspaceRoutes: RouteObject[] = [
|
|
47
|
+
{ path: 'plan/*', element: suspended(<PlanRoutes />) },
|
|
48
|
+
{ path: 'compare', element: suspended(<ComparePlansPage />) },
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Storage-independent content: the example library, the Learning Center,
|
|
53
|
+
* the disclaimer, and the "How RetireGolden is tested" trust page (linked
|
|
54
|
+
* from Results and the Disclaimer, so hosts mounting the workspace want it).
|
|
55
|
+
*/
|
|
56
|
+
export const plannerContentRoutes: RouteObject[] = [
|
|
57
|
+
{ path: 'examples', element: suspended(<ExamplesPage />) },
|
|
58
|
+
{ path: 'learn/*', element: suspended(<LearnRoutes />) },
|
|
59
|
+
{ path: 'disclaimer', element: <DisclaimerPage /> },
|
|
60
|
+
{ path: 'how-tested', element: suspended(<HowTestedPage />) },
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The web app's plans-management surfaces: the home page (plan list, backup
|
|
65
|
+
* download/import, clear-all) and the file-import wizard, plus redirects for
|
|
66
|
+
* retired v1 routes. Hosts with their own library chrome omit this group.
|
|
67
|
+
*/
|
|
68
|
+
export const plannerHomeRoutes: RouteObject[] = [
|
|
69
|
+
{ index: true, element: <PlanPickerPage /> },
|
|
70
|
+
{ path: 'import', element: suspended(<ImportPage />) },
|
|
71
|
+
// Retired v1 routes now redirect into the planner.
|
|
72
|
+
{ path: 'legacy', element: <Navigate to="/" replace /> },
|
|
73
|
+
{ path: 'longevity', element: <Navigate to="/" replace /> },
|
|
74
|
+
{ path: 'social-security', element: <Navigate to="/" replace /> },
|
|
75
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy page bindings for the route groups (routes/groups.tsx), split out so
|
|
3
|
+
* each file exports only components (react-refresh boundary rule).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { lazy } from 'react'
|
|
7
|
+
|
|
8
|
+
export const PlanRoutes = lazy(() => import('./PlanRoutes'))
|
|
9
|
+
export const LearnRoutes = lazy(() => import('./LearnRoutes'))
|
|
10
|
+
// Lazy like /plan and /learn: Examples pulls in all example builders + the Zod
|
|
11
|
+
// schema, Compare pulls in the whole engine via projectPlan — eager imports
|
|
12
|
+
// here would drag both into the landing entry chunk.
|
|
13
|
+
export const ExamplesPage = lazy(() => import('../planner/examples/ExamplesPage').then((m) => ({ default: m.ExamplesPage })))
|
|
14
|
+
export const ComparePlansPage = lazy(() => import('../planner/ComparePlansPage').then((m) => ({ default: m.ComparePlansPage })))
|
|
15
|
+
// Lazy so the glob-derived test-suite manifests it embeds stay out of the landing chunk.
|
|
16
|
+
export const HowTestedPage = lazy(() => import('../planner/HowTestedPage').then((m) => ({ default: m.HowTestedPage })))
|
|
17
|
+
// Lazy: the import wizard pulls in the per-source mappers and the Zod schema.
|
|
18
|
+
export const ImportPage = lazy(() => import('../import/ImportPage').then((m) => ({ default: m.ImportPage })))
|