|
| 1 | +# Vue 3 → React Migration · Roadmap |
| 2 | + |
| 3 | +**Date**: 2026-05-06 |
| 4 | +**Scope**: New greenfield repo `admin-react`. Source repo (`admin-vue3/apps/admin`, v7.0.1) is frozen during migration and replaced at cutover. |
| 5 | +**Goal**: 1:1 functional parity + design redesign (Linear dark-canvas system) on a modern React stack. |
| 6 | + |
| 7 | +This document is the **index**. It carries decisions and milestones, not implementation detail. Each sub-spec under this folder owns one slice of the migration. |
| 8 | + |
| 9 | +--- |
| 10 | + |
| 11 | +## Sub-spec layout |
| 12 | + |
| 13 | +``` |
| 14 | +docs/superpowers/specs/2026-05-06-react-migration/ |
| 15 | +├── 00-roadmap.md ← this file |
| 16 | +├── 01-repo-skeleton.md ← Vite 8, tsconfig, oxlint, env, html, deps, dir layout |
| 17 | +├── 02-design-tokens.md ← Linear DESIGN.md → css.ts tokens, theme switching |
| 18 | +├── 03-ui-primitives.md ← Base UI wrappers, API + variant + a11y contracts |
| 19 | +├── 04-state-layer.md ← Zustand stores, Jotai atoms, event-bus removal, persistence |
| 20 | +├── 05-data-layer.md ← request.ts port, TanStack Query patterns, socket.io hook |
| 21 | +├── 06-routing-auth.md ← react-router tree, ProtectedRoute, better-auth + passkey |
| 22 | +├── 07-layouts-patterns.md ← AppShell, Sidebar, MasterDetailLayout, header-action injection |
| 23 | +├── 08-form-system.md ← react-hook-form + zod, ConfigForm DSL port |
| 24 | +├── 09-editors.md ← haklex direct-mount, monaco, codemirror, xterm, draft system |
| 25 | +├── 10-charts-misc.md ← G2 hook, kbar swap, shiki/marked, diffs, excalidraw, confetti |
| 26 | +├── 11-views-migration.md ← 21 views in batches; preconditions + acceptance per batch |
| 27 | +└── 12-table-effort.md ← dedicated table effort (TanStack Table or hand-built) |
| 28 | +``` |
| 29 | + |
| 30 | +**Writing order**: this file + 01–06 first (P0 + P1 ammunition). 07–10 and 12 are written when P1 calibrates tokens and primitives. 11 is written when P2 settles patterns. |
| 31 | + |
| 32 | +--- |
| 33 | + |
| 34 | +## Tech stack decisions |
| 35 | + |
| 36 | +| Concern | Choice | Reason | |
| 37 | +|---|---|---| |
| 38 | +| Framework | React 19 + Vite 8 + `@vitejs/plugin-react-swc` | Vite 8 already on source repo; SWC for speed | |
| 39 | +| Routing | `react-router` v7 (classic `<Routes>/<Route>`) | User-specified; data router (loaders) explicitly rejected | |
| 40 | +| Server state | `@tanstack/react-query` 5.x + `query-persist-client-core` | 1:1 port; `['ai']` prefix persisted to localStorage | |
| 41 | +| Session state | Zustand | auth, theme, layout backbone, ui — singletons that span the session | |
| 42 | +| Page-local state | Jotai | drawer toggles, form drafts, selections, AI task queue, layout slots (header actions, page title) | |
| 43 | +| UI primitives | Base UI (`@base-ui-components/react`) | headless; pairs with css.ts | |
| 44 | +| Styling | css.ts (`@vanilla-extract/css` + vite plugin) | Compile-time CSS; tokens live as TS exports | |
| 45 | +| Design system | Linear dark-canvas (per pasted DESIGN.md) | Lavender accent, four-step surface ladder, no second chromatic accent | |
| 46 | +| Forms | `react-hook-form` + `zod` | `zod` already in source repo | |
| 47 | +| Animation | `motion` | User-specified | |
| 48 | +| Icons | `lucide-react` | 1:1 swap from `lucide-vue-next` | |
| 49 | +| Toast | `sonner` | 1:1 swap from `vue-sonner` | |
| 50 | +| Command palette | `kbar` | Original React lib; replaces `@bytebase/vue-kbar` | |
| 51 | +| Code editors | `@monaco-editor/react`, CodeMirror 6 hand-rolled | Slash menu / WYSIWYG depth justifies hand-roll for CM6 | |
| 52 | +| Rich editor | `@haklex/*` mounted directly (no Vue bridge) | `@mx-admin/rich-react` already React-native | |
| 53 | +| Charts | `@antv/g2` via `useG2Chart` hook | Library is framework-agnostic | |
| 54 | +| Terminal | `xterm` via `useEffect` mount | Library is framework-agnostic | |
| 55 | +| Realtime | `socket.io-client` via `useSocketIO` hook | Replaces `window.bus` event bus | |
| 56 | +| Auth | `better-auth` + `@better-auth/passkey` | Framework-agnostic; same flow | |
| 57 | +| API client | `ofetch` + existing `@mx-space/api-client` types | Both framework-agnostic; port unchanged | |
| 58 | +| Lint | `oxlint` | Sticks with current toolchain | |
| 59 | + |
| 60 | +### Explicitly rejected |
| 61 | + |
| 62 | +- TanStack Router data router (loaders/actions). Component routing only. |
| 63 | +- Tailwind / UnoCSS for the new repo (css.ts wins). |
| 64 | +- Naive UI compatibility shim. Full primitive rewrite. |
| 65 | +- SSR. SPA only, same as source. |
| 66 | + |
| 67 | +--- |
| 68 | + |
| 69 | +## State allocation rule (canonical) |
| 70 | + |
| 71 | +| Bucket | Goes to | |
| 72 | +|---|---| |
| 73 | +| Server data (anything fetched from API) | TanStack Query | |
| 74 | +| Session-level singletons (auth, theme, layout backbone, app config) | Zustand | |
| 75 | +| Page-local ephemeral (drawer open, form draft, selected id, AI task queue, header-action slot) | Jotai | |
| 76 | +| Truly component-private | `useState` | |
| 77 | + |
| 78 | +Cross-component fan-out without server fetch → Jotai by default. Promote to Zustand only when state outlives the route. |
| 79 | + |
| 80 | +--- |
| 81 | + |
| 82 | +## Dependency graph |
| 83 | + |
| 84 | +``` |
| 85 | +00-roadmap (index) |
| 86 | +01-repo ──┬─→ 02-tokens ──→ 03-primitives ──┐ |
| 87 | + │ ├─→ 07-layouts ──┐ |
| 88 | + ├─→ 04-state ──────────────────────┤ │ |
| 89 | + ├─→ 05-data ───────────────────────┤ ├─→ 11-views |
| 90 | + └─→ 06-routing-auth ───────────────┘ │ |
| 91 | + │ |
| 92 | + 08-form ──────────────────────┤ |
| 93 | + 09-editors ───────────────────┤ |
| 94 | + 10-charts-misc ───────────────┤ |
| 95 | + 12-table ─────────────────────┘ |
| 96 | +``` |
| 97 | + |
| 98 | +A spec may be **drafted** before its upstream dependencies stabilize, but must be **finalized** in graph order. |
| 99 | + |
| 100 | +--- |
| 101 | + |
| 102 | +## Milestones |
| 103 | + |
| 104 | +| Phase | Window | Deliverable | Spec dependency | |
| 105 | +|---|---|---|---| |
| 106 | +| **P0 thin-foundation** | ~1 wk | repo scaffolding, theme tokens, 5 primitives (Button/Input/Card/Modal/Toast), request layer, query client, router shell, auth client | 01, 02, 03 (subset), 04 (subset), 05 (subset), 06 (subset) | |
| 107 | +| **P1 vertical slice** | ~1.5 wk | `/login`, `/setup-api`, `/setup`, AppShell + Sidebar, `/dashboard` (incl. G2 charts, socket, kbar). Tokens **calibrated** against real surface. | 06, 07 (initial), 10 (G2 + kbar partial) | |
| 108 | +| **P2 primitive backfill + patterns** | ~1.5 wk | Drawer, Tabs, Tooltip, Popover, Select, Switch, Checkbox, Tag, Skeleton, Empty, Pagination, Avatar, Badge, Progress, Spinner, Ellipsis, Scroll. MasterDetailLayout, ConfigForm DSL, placeholder Table API. | 03 (full), 07 (full), 08 | |
| 109 | +| **P3 horizontal sweep** | ~5 wk | 16 views in 6 batches (3a list-read, 3b config, 3c analyze, 3d files, 3e AI, 3f extras). Batches parallelizable. | 11 | |
| 110 | +| **P4 rich-write closure** | ~3 wk | `/posts/edit`, `/notes/edit`, `/pages/edit`, `/posts/category`. haklex direct-mount, draft system, agent sidebar, metadata drawer. | 09 | |
| 111 | +| **P5 wrap-up** | ~1 wk | debug routes, dev tools, first-paint optimization, bundle split, smoke E2E. | — | |
| 112 | +| **Table track** | parallel from end of P2 | Real Table replaces placeholder. | 12 | |
| 113 | + |
| 114 | +**Total**: ~13 weeks for the main track + Table track running parallel from week 4. |
| 115 | + |
| 116 | +--- |
| 117 | + |
| 118 | +## Critical-path risks |
| 119 | + |
| 120 | +1. **Table is a blocker for ~50% of views.** P2 ships a placeholder `useDataTableState` API + minimal renderer so P3 can proceed; the Table track replaces internals later without view-side rewrites. |
| 121 | +2. **haklex bridge removal** is mechanically simple but the **draft system, agent chat state, and Lexical theme injection** need careful porting. Pin haklex package versions across the migration window. |
| 122 | +3. **socket.io currently dispatches via `window.bus`.** Migration moves to `useSocketIO` hook + Jotai/context fan-out. Audit every `window.bus` consumer before removing the bus. |
| 123 | +4. **css.ts is compile-time.** Dynamic per-instance styling (e.g. `motion` interpolations) must use inline style or `style` props, not generated css.ts variants. 02 + 03 nail down the contract. |
| 124 | +5. **Linear tokens** will get hammered during P1. Treat 02 as "v0 → calibrated v1 at end of P1". Do not freeze before that. |
| 125 | +6. **Auth cookies need same-origin (or properly configured CORS).** Confirm during 06 implementation; affects local dev proxy config in 01. |
| 126 | + |
| 127 | +--- |
| 128 | + |
| 129 | +## Out of scope |
| 130 | + |
| 131 | +- Backend changes. The migration consumes the existing mx-core API surface unchanged. |
| 132 | +- E2E test framework choice. Smoke E2E in P5 uses whatever is light (Playwright suggested but not mandated here). |
| 133 | +- i18n. Source repo is Chinese-only; new repo inherits the same posture. |
| 134 | +- Mobile app. Responsive web only. |
| 135 | + |
| 136 | +--- |
| 137 | + |
| 138 | +## Cross-references to source surveys |
| 139 | + |
| 140 | +The six parallel surveys feeding this roadmap are summarized inline across 01–12. The raw survey output is preserved in agent transcripts and not re-pasted here. |
| 141 | + |
| 142 | +- Routes & views — feeds 11 |
| 143 | +- State stores — feeds 04 |
| 144 | +- API & data — feeds 05 |
| 145 | +- UI components — feeds 03, 08 |
| 146 | +- Heavy assets — feeds 09, 10 |
| 147 | +- Auth & build — feeds 01, 06 |
0 commit comments