Scaffold und Core-Infrastruktur 1:1 nach enconf-Pattern (netcell- webpanel/management-ui), reduziert auf EdgeGuard-Scope (kein reseller/ customer-Roles, keine codemirror/extensions). Stack: React 19 + AntD 6 + TS strict + Vite + TanStack-Query + zustand + react-i18next. Layout: AppLayout (Sider+Header+Content), Sidebar (Dashboard/Domains), Header (User-Dropdown + Logout). i18n mit de/en common.json. Pages: Login (POST /auth/login), Setup-Wizard (POST /setup/complete), Dashboard (Health-Polling + Statistics), Domains (volles CRUD via TanStack-Query gegen /domains-API). UpdateBanner-Komponente (/system/package-versions, alle 5 min poll, /system/upgrade trigger) ist von Tag 1 wie vom User gefordert eingebaut. API-Wiring: cmd/edgeguard-api/main.go mountUI() — gin StaticFS für /usr/share/edgeguard/ui/ (overridebar via EDGEGUARD_UI_DIR), echte Files werden direkt geserved, alle nicht-API-Pfade fallen via NoRoute auf index.html für React-Router-SPA. Wenn dist/ fehlt: HTML-Placeholder mit Build-Hinweis. Verifiziert: bun install + npx tsc -b strict (0 errors) + bun run build (12 chunks). End-to-end gegen /tmp/eg-api: / serviert echte React-index.html, /domains SPA-Fallback, /api/v1/* JSON, /assets/* direkt, /api/v1/nonexistent korrekt 404. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
31 lines
884 B
TypeScript
31 lines
884 B
TypeScript
import { DashboardOutlined, GlobalOutlined } from '@ant-design/icons'
|
|
import { Menu, Typography } from 'antd'
|
|
import { useNavigate, useLocation } from 'react-router-dom'
|
|
import { useTranslation } from 'react-i18next'
|
|
|
|
export default function Sidebar() {
|
|
const { t } = useTranslation()
|
|
const navigate = useNavigate()
|
|
const location = useLocation()
|
|
|
|
const items = [
|
|
{ key: '/dashboard', icon: <DashboardOutlined />, label: t('nav.dashboard') },
|
|
{ key: '/domains', icon: <GlobalOutlined />, label: t('nav.domains') },
|
|
]
|
|
|
|
return (
|
|
<div>
|
|
<Typography.Title level={4} style={{ color: '#fff', padding: '16px', margin: 0 }}>
|
|
{t('app.title')}
|
|
</Typography.Title>
|
|
<Menu
|
|
theme="dark"
|
|
mode="inline"
|
|
selectedKeys={[location.pathname]}
|
|
items={items}
|
|
onClick={(e) => navigate(e.key)}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|