feat(admin-ui): minimal React+Vite+TanStack scaffolding
- Vite 7 + React 19 + TanStack Router (file-based) + TanStack Query - Tailwind v4 (CSS-first config, OKLCH brand colors) - i18next + react-i18next с RU/EN switcher в header - Axios client с Accept-Language interceptor + JWT placeholder - 2 pages: /dictionaries (list cards) + /dictionaries/$name (records table) - Dockerfile (node build + nginx serving + API proxy) - README + .gitignore Backend: - Новый GET /api/v1/dictionaries в read-api (DictionaryListController), фильтрует по доступному scope через ScopeContext - DictionaryListItem DTO без heavy schema_json routeTree.gen.ts — stub; реальное содержимое генерится Vite plugin'ом при первом 'pnpm dev'.
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { createRootRouteWithContext, Link, Outlet } from '@tanstack/react-router'
|
||||
import type { QueryClient } from '@tanstack/react-query'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
|
||||
component: RootLayout,
|
||||
})
|
||||
|
||||
function RootLayout() {
|
||||
const { t, i18n } = useTranslation()
|
||||
return (
|
||||
<div className="min-h-full flex flex-col">
|
||||
<header className="border-b border-zinc-200 bg-white">
|
||||
<div className="max-w-6xl mx-auto px-6 py-4 flex items-center gap-6">
|
||||
<Link to="/" className="font-semibold text-lg text-brand-700">
|
||||
{t('app.title')}
|
||||
</Link>
|
||||
<nav className="flex gap-4 text-sm">
|
||||
<Link
|
||||
to="/dictionaries"
|
||||
className="text-zinc-600 hover:text-brand-700"
|
||||
activeProps={{ className: 'text-brand-700 font-medium' }}
|
||||
>
|
||||
{t('nav.dictionaries')}
|
||||
</Link>
|
||||
</nav>
|
||||
<div className="ml-auto flex items-center gap-2 text-sm">
|
||||
<button
|
||||
onClick={() => i18n.changeLanguage('ru-RU')}
|
||||
className={`px-2 py-1 rounded ${i18n.language === 'ru-RU' ? 'bg-brand-50 text-brand-700' : 'text-zinc-500'}`}
|
||||
>
|
||||
RU
|
||||
</button>
|
||||
<button
|
||||
onClick={() => i18n.changeLanguage('en-US')}
|
||||
className={`px-2 py-1 rounded ${i18n.language === 'en-US' ? 'bg-brand-50 text-brand-700' : 'text-zinc-500'}`}
|
||||
>
|
||||
EN
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<main className="flex-1 max-w-6xl mx-auto w-full px-6 py-8">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user