add two v3 invoices pages and a shared theme system - #191
Draft
tenub wants to merge 5 commits into
Draft
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
tenub
force-pushed
the
v3-invoices-demo
branch
from
August 24, 2026 18:27
de8ac0d to
6698779
Compare
tenub
force-pushed
the
v3-invoices-demo
branch
from
August 28, 2026 18:22
d70d69c to
28ca1cb
Compare
tenub
force-pushed
the
v3-invoices-demo
branch
from
September 1, 2026 18:12
28ca1cb to
8896c79
Compare
/invoices builds a billing-history card on the v3 data seam — useInvoices,
deriveInvoiceList, useResolvedLocale, useTranslator — with the app's own
markup and Tailwind classes. /invoices-element renders the same data through
the packaged <Invoices> element, styled entirely from invoices.css against
the class names the element emits.
Both pages needed somewhere to live, so the app shell comes with them. One
palette in src/styles/palette.ts feeds both globals.css, for the app's own
markup, and useEmbedSettings, for the embedded components — replacing the
hardcoded dark embedSettings object, so /pricing, /usage and /custom-checkout
follow the site. ThemeProvider resolves light/dark before first paint and a
navbar toggle switches it.
SchematicProvider now takes a fetchAccessToken function so it can re-issue a
token after a 401, and useAccessToken wraps the same route for components
that take a token as a prop.
Also: ui/{Card,Button,Badge} primitives, a rewritten README and .env.example,
simple-import-sort in place of eslint-plugin-import's ordering, and a ban on
parent-relative imports in favour of the @/ alias.
Not mergeable as-is: pnpm-workspace.yaml links schematic-components,
schematic-react and schematic-js to a sibling schematic-js checkout, since
the v3 entry point is unpublished.
tenub
force-pushed
the
v3-invoices-demo
branch
from
September 1, 2026 21:13
5dd7f34 to
9ec27da
Compare
The Schematic packages are linked to a sibling checkout while the v3 entry point is unpublished. Turbopack traces only what sits under its inferred root — this directory — so it cannot follow those symlinks and the v3 import fails to resolve. Pointing the root one level up brings the sibling checkout in scope. Comes out once the packages come from npm.
`/invoices` and `/invoices-element` named the data rather than the thing a user came for, and neither had a home in the app's own navigation. They become `/billing` — this app's markup on the v3 hooks — and `/account/billing`, the packaged element over the same data. The hand-built card moves out of the page it was declared in, to `src/components/billing/InvoiceHistory.tsx`, so the page is a page. Both routes now take their query, row limit, and copy from `src/utils/billing.ts`: the pair only demonstrates anything if the two agree, and agreeing by convention is agreeing by accident.
/account/billing is account settings, not a fifth destination in the primary nav — it belongs where a user already looks for their own things. Clerk's UserButton takes menu items, so it goes in there. Demo mode has no ClerkProvider and so no user menu to hang it on; the link stays in the header there, which is the only place left that keeps the route reachable.
<SchematicStyles /> mounts on the provider in ClientWrapper, so /account/billing now renders through the sheet the package ships rather than through this app's own copy of it. That is the configuration worth demoing: the packaged element as a host gets it out of the box, tracking the app's theme toggle through `color-scheme` with nothing to wire up. invoices.css stays on disk, unimported. Every selector in it moved onto the classes the element now emits — __column/__cell rather than th/td, __link rather than __date a, __chip rather than a descendant of .schematic-invoices — so it remains a working example of restyling the element from scratch.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Demo pages for the invoices-only v3 slice (schematic-js#1688, backed by schematic-api#7529), plus the theming and shell work the pages needed to look like anything.
Two invoices pages
/invoicesbuilds a billing-history card straight on the v3 data seam:useInvoices({ includePending })for the page of invoices,deriveInvoiceList(page, { locale })for the display rows,useResolvedLocale()for date and currency formatting, anduseTranslator()/plural()for copy. The markup and Tailwind classes are the app's own, so nothing is injected into the document's styles./invoices-elementrenders the same data through the packaged<Invoices>element and styles it entirely frominvoices.css, targeting the class names the element emits. Side by side, the two pages cover both ends of the v3 story: bring your own UI, or take the element and skin it.Each row links to the hosted invoice where one exists, renders credits parenthesised and muted, and carries a status pill. Amounts are
tabular-numsso the column aligns. Rows past the first 10 collapse behind a show-all toggle, andloadMorefetches the next page once the list is fully expanded.Three states are handled explicitly:
aria-busy, matching the row layout so the card doesn't resize on arrival.role="alert". A refetch that fails never blanks data the customer was already reading.One palette, two consumers
src/styles/palette.tsholds the colours and fonts; everything else reads from it.globals.cssmaps them onto CSS custom properties and Tailwind theme tokens for the app's own markup, anduseEmbedSettings()maps the same values ontoEmbedSettingsfor the embedded components./pricing,/usageand/custom-checkoutnow follow the site instead of the hardcoded darkembedSettingsobject, which is deleted.ThemeProviderreads the preference withuseSyncExternalStoreoverlocalStorageandprefers-color-scheme: an explicit choice wins, the system preference leads until someone makes one, and astoragelistener keeps other tabs in step. An inline script in<head>applies the resolved class before first paint, so a dark reload never flashes light; when storage throws — private browsing, blocked site data — the choice falls back to memory and still works for the session.ThemeTogglesits in the navbar.Type is set at the document level: every heading level pairs a font size with a line height, holding the same scale the embed's typography uses.
Token plumbing
SchematicProvidernow receives afetchAccessTokenfunction, which it calls lazily and re-calls after a 401, withsessionKeyset to the company.useAccessTokenwraps the same/api/accessTokenroute for the components that take a token as a prop (/usage, custom checkout), cancelling in-flight requests so a slow first response can't land after a newer one./usagedrops its hand-rolled fetch effect for it, and neither invoices page needs token plumbing or a component ID of its own.Shell and housekeeping
ui/{Card,Button,Badge}primitives over acnhelper, shared by the invoices page and the error and skeleton states..env.exampledocuments every variable, and demo mode is opt-in rather than on by default.simple-import-sortreplaceseslint-plugin-import's ordering, andno-restricted-importsbans parent-relative paths in favour of the@/alias — imports updated throughout.Trying it
Run in demo mode against a local API with the
company-context-apiflag enabled and invoices seeded fordemo-co.Not mergeable as-is
The
./v3entry point only exists in@schematichq/schematic-components3.0.0, which isn't published.pnpm-workspace.yamloverridesschematic-components,schematic-reactandschematic-jstolink:../schematic-js/*, and the lockfile records those links — so this installs only alongside a siblingschematic-jscheckout. Both need to come back out, repinned to the published 3.0.0, before this can go green and merge.