feat(web): P1 Vite skeleton beside Next — entry, router, guards, vitest 3
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
First increment of the approved Phase P RFC (webui-mission). Adds a Vite + React Router SPA scaffold coexisting with the Next app: index.html with the theme anti-flash script, src/main.tsx entry, the v1 parity route table under Guest/Auth guard shells, and a dev proxy (/api, /socket.io ws) to the gateway on 14242 so the SPA is same-origin in dev. vitest bumped to v3 (vite 8 pairing); existing specs pass unchanged. Next remains the served app until the P5 cutover. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01ESFAnh2t9HmLwng8oW95St
This commit is contained in:
co-authored by
Claude Fable 5
parent
24bbd40dc7
commit
068d0f9b1c
@@ -0,0 +1,12 @@
|
||||
import type { ReactElement } from 'react';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
// P1 shells: session-aware redirects arrive with the reworked auth client in P2.
|
||||
|
||||
export function GuestGuard(): ReactElement {
|
||||
return <Outlet />;
|
||||
}
|
||||
|
||||
export function AuthGuard(): ReactElement {
|
||||
return <Outlet />;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
export function Placeholder({ title }: { title: string }): ReactElement {
|
||||
return (
|
||||
<main className="flex min-h-screen items-center justify-center">
|
||||
<h1 className="text-xl font-medium">{title}</h1>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { RouteObject } from 'react-router-dom';
|
||||
import { routes } from '@/routes';
|
||||
|
||||
function collectPaths(routeObjects: RouteObject[]): string[] {
|
||||
return routeObjects.flatMap((route) => [
|
||||
...(route.path ? [route.path] : []),
|
||||
...(route.children ? collectPaths(route.children) : []),
|
||||
]);
|
||||
}
|
||||
|
||||
describe('SPA route table', () => {
|
||||
it('covers every v1 parity route from the Phase P RFC', () => {
|
||||
expect(collectPaths(routes).sort()).toEqual(
|
||||
[
|
||||
'/',
|
||||
'/admin',
|
||||
'/auth/provider/:provider',
|
||||
'/chat',
|
||||
'/login',
|
||||
'/projects',
|
||||
'/projects/:id',
|
||||
'/register',
|
||||
'/settings',
|
||||
'/tasks',
|
||||
].sort(),
|
||||
);
|
||||
});
|
||||
|
||||
it('separates guest and authenticated route groups', () => {
|
||||
const guestPaths = collectPaths(routes.at(0)?.children ?? []);
|
||||
const authPaths = collectPaths(routes.at(1)?.children ?? []);
|
||||
expect(guestPaths).toContain('/login');
|
||||
expect(guestPaths).not.toContain('/chat');
|
||||
expect(authPaths).toContain('/chat');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user