Skip to content

feat(routing): add client-side SPA routing with React Router v8 - #133

Open
b-cedrick wants to merge 1 commit into
mainfrom
feat/spa-routing
Open

feat(routing): add client-side SPA routing with React Router v8#133
b-cedrick wants to merge 1 commit into
mainfrom
feat/spa-routing

Conversation

@b-cedrick

@b-cedrick b-cedrick commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add client-side SPA routing with React Router v8 (Data mode), replacing Dashboard's conditional rendering. Every page and every selectable resource gets its own URL — deep-links, browser back/forward, and refresh keep the open view.

Route View
/projects/:projectId? Project list / selected project
/discussions/:discussionId? Discussions / open discussion
/planning/:taskId? Planning backlog / open task detail
/plugins/:configId? Plugin grid / config drawer
/workflows, /workflows/:id/runs/:runId, /workflows/qp/:qpId?, /workflows/qa/:qaId? Workflows, run detail, Quick Prompts / Quick APIs
/config Settings

Changes

  • Install react-router v8; createBrowserRouter + RouterProvider in main.tsx; router.tsx stays a **pure routlive in routes/lazyRoutes.tsx(valid fast-refresh boundary, per-route code splitting: Dashboard chunk 949 KB → ~430 KB- Route wrappers (src/routes/,Route) bridge outlet context +URL params to page props; Dashboard becomes a layout shell rendering `
  • Typed useKronnNavigate() hooncy; one-shot intents (auto-run,batch focus, workflow presets) travel as location state so refresh never re-triggers
  • **Planning integrated into themain mid-branch):/planning/:taskId is the single source of truth for the open task — selection navigateduplicated state
  • Every selection path syncs the URL: discussions (sidebar, batch triage, contact chat, join-by-code, plan panel), proje
  • Legacy CLI hash #project-<id> redirects to the canonical /projects/:id (validated + scrolled); the kronn CLI now ely
  • ESLint warning budget restored: 161/162, pnpm lint --max-warnings 162 exits 0
  • Docs aligned (architecture, gltesting notes, decisions), broken [src:] citation repointed, CHANGELOG [Unreleased] entry added

Test plan

  • npx tsc --noEmit — 0 errors
  • pnpm test --run — **2772harness rewrites, URL assertionsvia router.state.location.pathname, hash-redirect pins, Planning URL-driven selection)
  • pnpm build — per-route cRoute`
  • SPA fallback verified: frontend/nginx.conf try_files (Docker) + Vite default (dev)
  • Manual: nav tabs, back/foruided tour (17 steps)
  • E2E suite (pnpm test:e2e) — specs unchanged, URL assertions to add in a follow-up

Checklist

  • Commits are signed off (DCO)
  • Tests pass locally for the
  • Documentation updated
  • No secrets, real names, or
  • No breaking changes (legacy hash deep-link kept as redirect)

@b-cedrick b-cedrick self-assigned this Jul 24, 2026
@b-cedrick b-cedrick added the ci-test Start all the CI Test steps label Jul 24, 2026
Replace Dashboard conditional rendering with React Router v8 (Data
mode). Every page and every selectable resource gets its own URL —
/projects/:id, /discussions/:id, /planning/:taskId, /plugins/:configId,
/workflows/:id/runs/:runId, /workflows/qp/:qpId, /workflows/qa/:qaId,
/config — enabling deep-links, browser back/forward, and refresh that
keeps the open view.

- router.tsx is a pure route table; route components live in
  routes/lazyRoutes.tsx (fast-refresh boundary, per-route code
  splitting: Dashboard chunk 949 KB → ~430 KB) and routes/* wrappers
  bridge outlet context + URL params to page props.
- Navigation goes through the typed useKronnNavigate() hook; one-shot
  intents (auto-run, batch focus, workflow presets) travel as location
  state so a refresh never re-triggers them.
- Planning task selection is URL-driven (/planning/:taskId is the
  single source of truth for the open detail).
- Every discussion selection path syncs the URL (sidebar, batch
  triage, contact chat, join-by-code, plan panel).
- The CLI's legacy #project-<id> hash deep-link redirects to the
  canonical /projects/:id route; the kronn CLI now emits the canonical
  URL directly.
- ESLint warning budget restored (161/162, exit 0).
- Docs, glossary, AGENTS §9, repo-map, testing notes and CHANGELOG
  aligned with the routing model; stale comments fixed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Cédrick BARO <baro.cedrick@gmail.com>
@b-cedrick b-cedrick added ci-test Start all the CI Test steps and removed ci-test Start all the CI Test steps labels Jul 27, 2026
@b-cedrick
b-cedrick marked this pull request as ready for review July 27, 2026 22:07
@barberemi

Copy link
Copy Markdown
Collaborator

🔍 Review — SPA routing (React Router v8)

Migration propre et cohérente : useKronnNavigate() isole bien la dépendance, les wrappers routes/ gardent les pages testables, les intents one-shot passent par le location state et sont bien nettoyés (replace, state:{}) donc un refresh ne les rejoue pas. Le fallback history (try_files … /index.html) est présent dans nginx.conf → les deep-links survivent au refresh en Docker. Bonne couverture de tests. 👍

Rien de bloquant. Quelques points mineurs :

Sévérité Fichier Point
🟡 Perf hooks/useKronnNavigate.ts L'objet retourné est recréé à chaque render (pas de useMemo). Les composants qui mettent nav dans les deps d'un useCallback/useEffect (ex. ProjectCard.tsx L495/L508) recréent donc ces callbacks à chaque render → mémoïsation cassée. → envelopper le retour dans useMemo (navigate est déjà stable).
🟡 Régression UX MessageBubble.tsx / routes/SettingsRoute.tsx nav.toConfig('settings-server') (bouton « Modifier le timeout ») pousse /config#settings-server, mais aucun effet ne lit location.hash au mount → la page ne scrolle plus vers la section serveur (l'ancien onNavigate('settings',{scrollTo}) le faisait). → ajouter un effet de scroll-vers-hash. toConfig() sans section reste OK.
🔵 Nit lib/__tests__/routeConstants.test.ts Le test « covers all 5 pages » en couvre 5 alors qu'il y en a 6 (planning ajouté mais non asserté). Nom/couverture à jour.

ℹ️ CI : les 2 checks non-« passed » sont skipped (label-gated), pas des échecs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-test Start all the CI Test steps

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants