feat(web): scaffold client-side rendered SPA foundation

- feat: initialize Vite project with React 19, React Router 8, TanStack
  Query, Ant Design 6, and Tailwind CSS 4
- feat: add Oxlint with React/TypeScript rules, strict TypeScript
  config, and layered CSS import order
- docs: add decision record for client-rendered Web foundation with
  React, Ant Design, and Tailwind CSS
- docs: create web roadmap defining architecture boundaries and deferred
  dependencies
- build: pin Node.js 24 in mise.toml
This commit is contained in:
2026-07-14 16:50:13 +08:00
parent a62a5bc0e2
commit b49bf648be
20 changed files with 3093 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
import { RouterProvider } from 'react-router'
import { router } from './app/router.tsx'
function App() {
return <RouterProvider router={router} />
}
export default App
+15
View File
@@ -0,0 +1,15 @@
import type { PropsWithChildren } from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { App as AntDesignApp, ConfigProvider } from 'antd'
const queryClient = new QueryClient()
export function AppProviders({ children }: PropsWithChildren) {
return (
<QueryClientProvider client={queryClient}>
<ConfigProvider>
<AntDesignApp>{children}</AntDesignApp>
</ConfigProvider>
</QueryClientProvider>
)
}
+15
View File
@@ -0,0 +1,15 @@
import { createBrowserRouter } from 'react-router'
import { HomePage } from '../pages/HomePage.tsx'
import { NotFoundPage } from '../pages/NotFoundPage.tsx'
export const router = createBrowserRouter([
{
path: '/',
element: <HomePage />,
},
{
path: '*',
element: <NotFoundPage />,
},
])
+13
View File
@@ -0,0 +1,13 @@
@layer theme, base, antd, components, utilities;
@import 'tailwindcss';
html,
body,
#root {
min-width: 320px;
min-height: 100%;
}
body {
margin: 0;
}
+13
View File
@@ -0,0 +1,13 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import { AppProviders } from './app/providers.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<AppProviders>
<App />
</AppProviders>
</StrictMode>,
)
+19
View File
@@ -0,0 +1,19 @@
import { Card, Space, Tag, Typography } from 'antd'
const { Paragraph, Title } = Typography
export function HomePage() {
return (
<main className="grid min-h-screen place-items-center bg-slate-50 p-6">
<Card className="w-full max-w-xl">
<Space orientation="vertical" size="middle">
<Tag color="blue">MyGO Web</Tag>
<Title level={1}>MyGO</Title>
<Paragraph>
The client-side web application foundation is ready.
</Paragraph>
</Space>
</Card>
</main>
)
}
+16
View File
@@ -0,0 +1,16 @@
import { Button, Result } from 'antd'
import { Link } from 'react-router'
export function NotFoundPage() {
return (
<Result
status="404"
title="Page not found"
extra={
<Button type="primary">
<Link to="/">Back home</Link>
</Button>
}
/>
)
}