Skip to content

feat(web): SPA foundation — tailwind, shadcn, i18n, file-based routing (Slice 2c) - #7

Merged
avetisk merged 6 commits into
mainfrom
slice/2c-web-foundation
Jul 9, 2026
Merged

feat(web): SPA foundation — tailwind, shadcn, i18n, file-based routing (Slice 2c)#7
avetisk merged 6 commits into
mainfrom
slice/2c-web-foundation

Conversation

@avetisk

@avetisk avetisk commented Jul 8, 2026

Copy link
Copy Markdown
Owner

The web foundation for Slice 2 — the app skeleton, exercisable, no feature yet. The shorten form is the next PR (2d). Built on the design we agreed (file-based routing chosen for scale, per your "right approach now" steer).

What's in

  1. Styling — tailwind v4 (@tailwindcss/vite, no PostCSS) + shadcn/ui initialized (components.json, cn, theme vars). One component pulled: Button (the locale toggle uses it → proves the pipeline).
  2. i18nreact-i18next + browser detection (localStorage-cached), EN default + FR, no locale URL prefixes. Catalogs keyed on the shared URL_ERROR codes (ADR-0007).
  3. Routingfile-based @tanstack/react-router (src/routes/, generated tree gitignored + regenerated by tsr generate before typecheck / by the vite plugin on build). Single / route now; scales to more pages without a refactor.
  4. Data/RPC@tanstack/react-query provider + a typed hc<AppType> client (tsc-proven against the API, not called yet).
  5. Forms@tanstack/react-form installed (ADR-0008); the actual form is 2d.

Exercisable, not inert (no dead code, honest gate)

  • e2e (home.spec.ts) — the page renders through i18n keys; the toggle flips the tagline EN → FR and the test asserts it.
  • unit (new web vitest) — i18n-completeness: both catalogs cover exactly the URL_ERROR code set (no gaps, no orphans); plus a cn test.
  • tsc proves the RPC client is typed against AppType.

Coverage philosophy (mirrors apps/api)

Web vitest keeps pure logic at 100% (i18n catalogs, cn); UI / routing / RPC / wiring are excluded from coverage and covered by Playwright instead — justified in vitest.config.ts.

Docs

  • ADR-0008 — Forms via @tanstack/react-form (rhf maintenance waning; TanStack fresher + standard-schema fit).
  • docs/spec.md stack line + roadmap updated (react-hook-form@tanstack/react-form, react-i18next added).

Verification (full gate, local)

  • lint / fmt:check / typecheck (all packages) green.
  • vitest: shared 7/7, api 16/16, web 5/5 — all 100%.
  • vite build green (tailwind CSS emitted, router codegen clean).
  • e2e 2/2 against the production build (pnpm preview), incl. the EN→FR toggle.

Note: @jailu/api needed no exports change — AppType is a type-only import (erased at build), resolved via the tsconfig @jailu/api/src/* alias.

🤖 Generated with Claude Code

avetisk and others added 3 commits July 8, 2026 23:58
…test)

Adds the web foundation deps and their wiring: tailwind v4 (@tailwindcss/vite),
@tanstack/react-router + router-plugin/CLI (file-based routes, generated tree
gitignored + regenerated via `tsr generate` before typecheck), react-query,
react-form, react-i18next, shadcn primitives (clsx/cva/tailwind-merge), the
hono RPC client, and vitest (+ coverage script wired into CI's `pnpm -r`).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pp shell

The exercisable foundation: a shadcn Button, react-i18next (EN default + FR,
browser-detected, localStorage-cached) with catalogs keyed on the shared
URL_ERROR codes, file-based @tanstack/react-router (single index route), the
react-query provider, and a typed hono RPC client (tsc-proven, not yet called).

Proof, not scaffolding: the home page renders through i18n keys and a language
toggle flips every string (e2e asserts EN->FR), and an i18n-completeness unit
test enforces both catalogs cover exactly the URL_ERROR code set. Replaces the
static App.tsx.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread apps/web/src/i18n/index.ts
avetisk and others added 3 commits July 9, 2026 00:34
The TanStack route generator writes to a temp file then renames it onto
routeTree.gen.ts. Its default tmpDir (<cwd>/.tanstack/tmp) is on the container
overlay fs while the output lives under the src/ bind mount, so the rename is
cross-device and fails with EXDEV in `docker compose up` — leaving the route
tree unwritten and router.tsx's import unresolved. Point tmpDir inside src so
temp and output share one filesystem (host + docker); temp dir is gitignored.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A parallel CI job boots the real dev stack (`docker compose up --build --wait`)
and asserts the API health endpoint, the web app shell, and that the router
route tree was actually generated in the container — so container-only breakage
(cross-workspace module resolution, the generator's cross-device EXDEV rename)
fails CI instead of a laptop. Adds a healthcheck to the web service so `--wait`
gates it too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename the app title jailu -> jai.lu and set the tagline to "shorter is better"
(FR: "plus court, c'est mieux"). e2e assertions updated to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@avetisk

avetisk commented Jul 9, 2026

Copy link
Copy Markdown
Owner Author

LGTM.

@avetisk
avetisk merged commit a58bbe3 into main Jul 9, 2026
2 checks passed
@avetisk
avetisk deleted the slice/2c-web-foundation branch July 9, 2026 00:44
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.

1 participant