feat: nKode React + TypeScript SPA scaffold

- React + Vite + Tailwind CSS v4 + React Router
- Keypad component: 10-digit pad with dot indicators, keyboard support, haptic feel
- Auth pages: Login (email → keypad), Signup (email → method → keypad/key)
- Home page: session info, practice keypad
- Admin dashboard: placeholder stats + user management
- Developer dashboard: OIDC client registration UI placeholder
- WASM client wrapper: lazy loads from /wasm/, falls back to mock client
- TypeScript type declarations for nkode-client-wasm package
- Dark mode with system preference detection
- Auth state management with localStorage persistence + auto-expiry
- Code splitting: lazy-loaded route pages (~236KB main bundle)
- Inter font, clean indigo/slate design system
This commit is contained in:
2026-01-29 14:39:27 +00:00
commit a4de830313
25 changed files with 2004 additions and 0 deletions

41
src/App.tsx Normal file
View File

@@ -0,0 +1,41 @@
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { Layout } from '@/components/Layout';
import { AuthContext, useAuthState } from '@/hooks/useAuth';
import { ROUTES } from '@/lib/types';
const HomePage = lazy(() => import('@/pages/HomePage').then((m) => ({ default: m.HomePage })));
const LoginPage = lazy(() => import('@/pages/LoginPage').then((m) => ({ default: m.LoginPage })));
const SignupPage = lazy(() => import('@/pages/SignupPage').then((m) => ({ default: m.SignupPage })));
const AdminPage = lazy(() => import('@/pages/AdminPage').then((m) => ({ default: m.AdminPage })));
const DeveloperPage = lazy(() => import('@/pages/DeveloperPage').then((m) => ({ default: m.DeveloperPage })));
function Loading() {
return (
<div className="flex items-center justify-center min-h-[50vh]">
<div className="w-6 h-6 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" />
</div>
);
}
export default function App() {
const auth = useAuthState();
return (
<AuthContext.Provider value={auth}>
<BrowserRouter>
<Suspense fallback={<Loading />}>
<Routes>
<Route element={<Layout />}>
<Route path={ROUTES.HOME} element={<HomePage />} />
<Route path={ROUTES.LOGIN} element={<LoginPage />} />
<Route path={ROUTES.SIGNUP} element={<SignupPage />} />
<Route path={ROUTES.ADMIN} element={<AdminPage />} />
<Route path={ROUTES.DEVELOPER} element={<DeveloperPage />} />
</Route>
</Routes>
</Suspense>
</BrowserRouter>
</AuthContext.Provider>
);
}