Skip to content

Add economic indicators dashboard#27

Open
mikaalnaik wants to merge 11 commits into
mainfrom
mikaal/economic-dashboard
Open

Add economic indicators dashboard#27
mikaalnaik wants to merge 11 commits into
mainfrom
mikaal/economic-dashboard

Conversation

@mikaalnaik

Copy link
Copy Markdown
Contributor

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
  • New /api/economy/series proxy route + economy API client (src/lib/api/economy.ts)
  • Sitemap updated to include the new pages

UI notes

  • On the canvas, the chart is the primary content with subtle selectors tucked beneath it
  • Individual chart titles/subheaders are sized down to read as subordinate to their section
  • A "Want to see another indicator?" email CTA sits at the bottom of the dashboard
  • The Economy nav link and the Canvas nav link are hidden for now while the dashboard is in progress (pages remain live at their URLs)

Testing

  • npm run lint — passes (only pre-existing warnings in unrelated bills/ files)
  • npx tsc --noEmit — passes

🤖 Generated with Claude Code

mikaalnaik and others added 2 commits July 8, 2026 14:54
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>
@mikaalnaik mikaalnaik self-assigned this Jul 8, 2026
@mikaalnaik mikaalnaik requested a review from xrendan July 8, 2026 19:16
mikaalnaik and others added 8 commits July 8, 2026 15:31
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>
@xrendan xrendan self-requested a review July 10, 2026 14:41
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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way we do units is fucked, this should all probably be backend config

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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[] = [

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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]",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why change this? Is this our formatter doing this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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`}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this for? Do we need mods to navigation in this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh is this for the sidenav indicators?

Why is this bespoke and not shared from the memos?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do any of these break memos?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 />}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this guard?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants