Component Showcase
Readout
Component Showcase
A demo readout exercising every component in the catalog — including the new Diff, rich Diagram, Checklist, Timeline, StatTiles, and FileTree blocks — over a fictional auth-and-onboarding session.
01Summary
The login exchange is stable; routing and the candidate unlock path still have gaps.
- OAuth provider token is exchanged at
POST /auth/oauth; the backend verifies it and issues JWT access + refresh tokens. - The response field
nextScreenroutes the user to eitheraccount_completionoroverview. - Guards:
JwtAuthGuardvalidates the Bearer token,RolesGuardenforces the@AuthRoles()decorator.
A KPI row for a recap-style glance at the session:
Plain paragraphs, bold, italic, inline code, and links
all render normally inside a section.
A third-level heading
- Ordered lists work too.
- As do plain bullet lists outside
KeyPoints.
Blockquotes render with the theme's blockquote styling.
02What changed
Four moving parts shifted this session — one landed, two are watch-items, one is a hard blocker.
Account-completion endpoint now accepts role + profile in a single request.
nextScreen is computed server-side — the client should never infer it.
Refresh-token rotation isn't covered by tests yet.
Candidate identity unlock still depends on the credits module, which is unimplemented.
A lone callout with a custom label works standalone (no wrapper needed):
Single callouts wrap themselves in a .callouts row automatically.
03How the session went
The order we uncovered things — login first, routing next, then the wall.
- 09:15Traced the login exchangeRead
handleOAuthend to end and confirmed the access + refresh tokens are issued in one pass. - 10:40Found the routing gap
nextScreenwas being inferred on the client. Moved the decision server-side so the two tiers can never disagree. - 11:30Hit the credits blockerCandidate identity unlock reads a balance from the credits module, which isn't implemented — hard stop for that path.
04Flow
From provider token to landing screen, the backend decides everything in one pass.
flowchart TD
A[OAuth provider token] --> B[POST /auth/oauth]
B --> C{User exists?}
C -->|no| D[Create user]
C -->|yes| E[Issue JWT access + refresh]
D --> E
E --> F{nextScreen}
F -->|account_completion| G[Onboarding: role + profile]
F -->|overview| H[Main app]For the request path itself, a mermaid flow is too flat — the html variant draws the
layers each hop passes through, as swimlanes with labeled arrows:
05The change
Routing moved off the client: the resolver now returns nextScreen from the server.
1234556789export async function handleOAuth(dto: OAuthDto) {const profile = await verifyProviderToken(dto.provider, dto.token);const user = await users.upsertFromProvider(profile);const tokens = issueTokens(user);return { ...tokens };return { ...tokens, nextScreen: user.profileComplete ? "overview" : "account_completion",};}No newline at end of fileNo newline at end of fileThe block also accepts a unified patch string (a full git diff, file headers and all)
instead of oldText/newText, plus a split prop for side-by-side.
06Key code
Enums are const objects with inferred union types, never the enum keyword.
A plain fenced code block renders through the same .codewrap structure:
// enums are const objects with inferred union types, never the `enum` keyword
export const UserRoleEnum = {
Candidate: "candidate",
Company: "company",
} as const;
export type UserRole = (typeof UserRoleEnum)[keyof typeof UserRoleEnum];The explicit <Code> component is equivalent, useful when the source contains
characters awkward to fence:
curl -s -X POST https://api.example.com/auth/oauth \
-H "Content-Type: application/json" \
-d '{"provider":"google","token":"..."}'07Touched files
What moved this session, nested by directory and tagged by change kind.
- src
- auth
- oauth-handler.tsnextScreen resolved server-side
- tokens.tsrefresh rotation stub
- onboarding
- account-completion.controller.tsrole + profile in one request
- account-completion.dto.tscombined payload
- credits
- placeholder.tsdead stub deleted
- auth
08Plan support
Artifact generation is gated to the workspace tiers — not the consumer plans.
A props-driven table with sortable headers, a live filter, and mark cells:
| Plan | Artifacts | Notes |
|---|---|---|
| Free | ✕ | not available |
| Pro | ✕ | consumer tier, excluded |
| Max | ✕ | consumer tier, excluded |
| Team | ✓ | on by default |
| Enterprise | ✓ | admin-enabled |
A markdown GFM table also works — pass it as children:
| Guard | Purpose | Tested |
|---|---|---|
| JwtAuthGuard | validates the Bearer token | yes |
| RolesGuard | enforces @AuthRoles() | partial |
09Verification
What we actually checked before calling the login path stable.
- OAuth token exchange returns access + refresh tokens
nextScreenis computed server-side, never inferred by the client- Both guards run before the handler (verified with a forged Bearer token)
- Refresh-token rotation covered by an integration test
- Candidate unlock path wired to the credits module
A GFM task list gets re-tagged into the same checklist styling:
- Manual login round-trip against staging
- Load test the token endpoint at 100 rps
10Open questions
Three decisions still need an owner before the candidate path can ship.
- Should refresh-token rotation invalidate the old token immediately or after a grace window?
- Where does the credits balance get checked — guard or use-case?
- Does
account_completionneed to be idempotent if the client retries?