All docs

Billing UI

Billing UI

Route: apps/web/app/routes/_app.o.$slug.billing.tsx Loader inputs: getEntitlements, usageForCurrentPeriod, usageHistory (all from @drobek/core/quota) Design source (LOCKED): docs/design/final/project/app-screens.jsx SettingsBilling component + appshell-screens.css (.usage-block, .usage-bar, .usage-spark) Decided by: ADR 0006, ADR 0007

What the page shows

  1. Plan card — current tier label (PLANS[tier].label), "current" status pill,

limit + reset date (e.g. "250 tasks / month · resets 2026-06-01").

  1. Upgrade CTA:

- PLANS.pro.comingSoon === true (the v1 default) → Link to /pricing#waitlist labelled "Join waitlist · Pro soon" (matches the marketing waitlist form from task 39). - PLANS.pro.comingSoon === false → Link to /pricing labelled "Upgrade to Pro". The CTA mode is purely a function of the catalogue — no env flag, no DB column.

  1. Usage metertasks_created is the headline (per the locked design). Bar

fill % = used / limit * 100, clamped to 100. Renders aria-valuenow for screen readers and data-pct for E2E.

  1. Sparkline — last 6 months of tasks_created, oldest → newest, current month

visually flagged. Heights are normalised against max(limit, max(counts)) so a blowout month still fits the box.

  1. Invoices — placeholder "/ no invoices — Free plan during public beta /".

The Stripe Customer Portal link is deferred per ADR 0007 until the Stripe follow-up to task 40 lands.

Why no toast at 80%

Task 41 spec called for a session-scoped 80% warning toast on _app.tsx loader

  • email at 80% / 100% via task 45. Both are out of scope for the launch: the

visual meter at 80% already signals the state, and the email path folds naturally into the task 45 generic notification system (needs_review-style fan-out). When task 45 lands, add the alert there — not here — so the dedup story lives in one place.

Pro tier branch

For an org on pro tier (tasksPerMonth === 'unlimited'), the entire usage-block is suppressed (no meter, no sparkline). The plan card meta reads "Unlimited tasks · fair-use cap applies". The design didn't sketch this branch because Pro is "Coming soon" — we still need to handle it so a manually-flipped beta-partner org doesn't crash the page.

E2E

apps/web/app/e2e/billing.e2e.test.ts runs against the dev container with DEV_AUTH_BYPASS=true and the drobek-dev seed org. Asserts:

  • All data-testids present (plan-card, usage-meter-tasks, usage-bar,

usage-spark, upgrade-cta).

  • Sparkline renders exactly 6 bars.
  • Upgrade CTA href is /pricing#waitlist while comingSoon is true.

Integration coverage for usageHistory lives in packages/core/src/quota/quota.integration.test.ts (zero-fill, ordering, current period flag).