feat(web): SPA foundation — tailwind, shadcn, i18n, file-based routing (Slice 2c) - #7
Merged
Conversation
…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>
avetisk
commented
Jul 9, 2026
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>
Owner
Author
|
LGTM. |
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.
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
@tailwindcss/vite, no PostCSS) + shadcn/ui initialized (components.json,cn, theme vars). One component pulled:Button(the locale toggle uses it → proves the pipeline).react-i18next+ browser detection (localStorage-cached), EN default + FR, no locale URL prefixes. Catalogs keyed on the sharedURL_ERRORcodes (ADR-0007).@tanstack/react-router(src/routes/, generated tree gitignored + regenerated bytsr generatebefore typecheck / by the vite plugin on build). Single/route now; scales to more pages without a refactor.@tanstack/react-queryprovider + a typedhc<AppType>client (tsc-proven against the API, not called yet).@tanstack/react-forminstalled (ADR-0008); the actual form is 2d.Exercisable, not inert (no dead code, honest gate)
home.spec.ts) — the page renders through i18n keys; the toggle flips the tagline EN → FR and the test asserts it.URL_ERRORcode set (no gaps, no orphans); plus acntest.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 invitest.config.ts.Docs
@tanstack/react-form(rhf maintenance waning; TanStack fresher + standard-schema fit).docs/spec.mdstack line + roadmap updated (react-hook-form→@tanstack/react-form,react-i18nextadded).Verification (full gate, local)
vite buildgreen (tailwind CSS emitted, router codegen clean).pnpm preview), incl. the EN→FR toggle.Note:
@jailu/apineeded noexportschange —AppTypeis a type-only import (erased at build), resolved via the tsconfig@jailu/api/src/*alias.🤖 Generated with Claude Code