Add economic indicators dashboard#27
Conversation
Add /economic-indicators dashboard showing how Canada compares against G7/OECD peers across growth, productivity, incomes, inequality, and housing, backed by a new /api/economy/series proxy and economy API client. Includes a Canvas view (/economic-indicators/canvas) to overlay and compare up to three indicator series, with indexed and raw scale modes. Nav link for Economy is hidden for now while the dashboard is in progress. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Section links follow the reader below the global navbar, highlight the section currently in view, and jump forward on click. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pass selectedEntityColors so Canada renders as auburn-600 (#c43e3e) on every chart instead of a scheme-assigned color that varied per chart. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Renders york_factory's new inflation-cpi-annual measure in the Overall Economy section; the canvas picker and API proxy pick it up from the shared catalog automatically. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New gdp-monthly-canada series from StatCan table 36-10-0434, with a $M unit that converts to dollars so Grapher's magnitude labels read true. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reuse the memo signpost (scrollspy rail with progress dots) on /economic-indicators/[section], with new props for the sticky offset, mobile bar, and top border so memo pages are unchanged. Each chart section gets an anchor: headings self-link with a hover #, rail clicks write the hash, scroll margins clear the sticky nav stack, and chart placeholders match final heights so hash jumps land accurately. ScrollToTop now leaves the browser's native anchor jump alone when a hash is present. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lead with the thesis — Canada should be the most prosperous country in the world — and frame every chart as answering "are we moving in the right direction?", including how to read the red Canada line against G7/OECD peers. Align the meta description and footer CTA. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| import { getEconomicSeries } from "@/lib/api/economy"; | ||
| import { MEASURE_SLUGS } from "@/app/economic-indicators/indicators"; | ||
|
|
||
| // Same-origin proxy for york_factory's public series endpoint so client |
There was a problem hiding this comment.
Yes, for now at least — verified with curl: york_factory's CORS only allows https://buildcanada.com exactly (localhost and Vercel preview origins get no Access-Control-Allow-Origin header), and the endpoint currently 404s unauthenticated from outside, so a direct browser fetch would only ever work in prod. The proxy also shares the section pages' hourly-revalidated fetch cache, so canvas traffic doesn't stampede upstream. If york_factory opens CORS broadly we can delete this route.
| Legend, | ||
| ); | ||
|
|
||
| function formatValue(value: number, unitSymbol: string): string { |
There was a problem hiding this comment.
The way we do units is fucked, this should all probably be backend config
There was a problem hiding this comment.
Agreed. Consolidated the three parallel switches (Grapher display config, canvas axis label, canvas tooltip format) into a single table in units.ts in 80dbabb, so adding a unit is now one row. End state should be york_factory shipping display config (label, decimals, format hints) on the unit object itself — this table is written to be deleted when that lands.
| // Neutral grey for benchmark reference lines (e.g. the 2% inflation target). | ||
| export const BENCHMARK_COLOR = "#8c8880"; | ||
|
|
||
| export const SECTIONS: IndicatorSection[] = [ |
There was a problem hiding this comment.
nit: This should probably be driven by the backend, no?
Fine for now, but I think we want to get to a place where people can build their own dashboard and be alerted when indicators that they care change.
There was a problem hiding this comment.
Agreed — sections/blurbs/benchmarks as backend config is the right end state, especially for the user-built-dashboards-plus-alerts direction. Kept it a static catalog for v1 so this ships without a york_factory change.
| export function DesktopNav() { | ||
| const { activeId, activeParentId, navigateTo, shareTitle, shareUrl } = useSignpost(); | ||
| export function DesktopNav({ | ||
| topClass = "top-[90px]", |
There was a problem hiding this comment.
The indicator pages stack an extra sticky bar (SectionNav) below the global navbar, so the signpost rail needs a deeper sticky top (170px vs the memo's 90px) and no duplicate top border. Both props default to the old values, so memos render exactly as before.
| const dot = container.querySelector(`[data-dot="${activeId}"]`); | ||
| if (!dot) return; | ||
| const h = (dot as HTMLElement).offsetTop + (dot as HTMLElement).offsetHeight / 2; | ||
| const h = |
There was a problem hiding this comment.
why change this? Is this our formatter doing this?
There was a problem hiding this comment.
Yeah — that's just Prettier re-wrapping the line, no logic change.
| <nav className="hidden 2xl-memo:block" aria-label="Table of contents"> | ||
| <div className="sticky top-[90px] border-accent border-t-[2px] overflow-x-hidden [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"> | ||
| <div | ||
| className={`sticky ${topClass} ${showTopBorder ? "border-accent border-t-[2px] " : ""}overflow-x-hidden [scrollbar-width:none] [&::-webkit-scrollbar]:hidden`} |
There was a problem hiding this comment.
What is this for? Do we need mods to navigation in this?
There was a problem hiding this comment.
Oh is this for the sidenav indicators?
Why is this bespoke and not shared from the memos?
There was a problem hiding this comment.
Right — and it's not bespoke: this is the shared memo Signpost, just parameterized. The indicator pages reuse it as-is; these props only adjust the sticky offset/border so the rail sits below their SectionNav bar. Memos pass nothing and get the previous defaults.
| const y = el.getBoundingClientRect().top + window.scrollY - 120; | ||
| window.scrollTo({ top: y, behavior: "smooth" }); | ||
| }, []); | ||
| const navigateTo = useCallback( |
There was a problem hiding this comment.
No — scrollOffset defaults to the old hardcoded 120, and the mobile bar / top border default on, so memo layout and scroll behavior are unchanged. One deliberate change does reach memos: rail clicks now write #heading into the URL via replaceState so readers can share deep links (paired with the ScrollToTop change that leaves the browser's native anchor jump alone when a hash is present).
| > | ||
| <MobileBar /> | ||
| <DesktopNav /> | ||
| {showMobileBar && <MobileBar />} |
There was a problem hiding this comment.
Yes — on narrow screens the indicator pages already have their own sticky SectionNav, so the memo mobile TOC bar would double-stack sticky bars. Memos keep it via the default true.
The Grapher display config, canvas axis label, and canvas tooltip format for each unit symbol were three parallel switches that would drift as units are added. One row per unit in units.ts now feeds all three, until york_factory ships display config on the unit object. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
Adds a new Economic Indicators dashboard showing how Canada stacks up against its G7/OECD peers across growth, productivity, incomes, inequality, and housing.
/economic-indicators— sectioned dashboard of indicator charts, each with source attribution/economic-indicators/canvas— overlay & compare up to three indicator series, with indexed (first shared year = 100) and raw scale modes; shareable via URL params/api/economy/seriesproxy route + economy API client (src/lib/api/economy.ts)UI notes
Testing
npm run lint— passes (only pre-existing warnings in unrelatedbills/files)npx tsc --noEmit— passes🤖 Generated with Claude Code