feat: add frontend app routing

This commit is contained in:
hailin
2026-06-30 18:47:35 +08:00
parent 333dc76f18
commit c80c286227
7 changed files with 77 additions and 5 deletions

View File

@@ -1,5 +1,10 @@
import { DesignSystemPreview } from '@/features/design-system/DesignSystemPreview'
import { AppShell } from './layout/AppShell'
import { Router } from './router'
export function App() {
return <DesignSystemPreview />
return (
<AppShell>
<Router />
</AppShell>
)
}

View File

@@ -13,7 +13,7 @@ export function AppShell({ children }: AppShellProps) {
<Sidebar />
<div className="min-h-screen lg:pl-64">
<Topbar />
<main>{children}</main>
{children}
</div>
</div>
)

View File

@@ -1,9 +1,27 @@
export function Sidebar() {
const navItems = [
{ href: '#/tickets', label: 'Tickets' },
{ href: '#/tickets/new', label: 'Create Ticket' },
{ href: '#/solutions', label: 'Solutions' },
{ href: '#/design-system', label: 'Design System' }
]
return (
<aside className="fixed inset-y-0 left-0 hidden w-64 border-r border-border bg-surface lg:block">
<div className="border-b border-border px-4 py-3">
<p className="text-sm font-semibold text-foreground">AI Ticket Platform</p>
</div>
<nav className="flex flex-col gap-1 p-3">
{navItems.map((item) => (
<a
className="rounded-md px-3 py-2 text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
href={item.href}
key={item.href}
>
{item.label}
</a>
))}
</nav>
</aside>
)
}

View File

@@ -1,5 +1,30 @@
import { App } from './App'
import * as React from 'react'
import { DesignSystemPreview } from '@/features/design-system/DesignSystemPreview'
function getRoute() {
return window.location.hash.replace(/^#/, '') || '/tickets'
}
export function Router() {
return <App />
const [route, setRoute] = React.useState(getRoute)
React.useEffect(() => {
const handleHashChange = () => setRoute(getRoute())
window.addEventListener('hashchange', handleHashChange)
return () => window.removeEventListener('hashchange', handleHashChange)
}, [])
if (route === '/design-system') {
return <DesignSystemPreview />
}
return (
<main className="min-h-screen bg-background p-6 text-foreground">
<div className="mx-auto max-w-6xl">
<p className="text-sm text-muted-foreground">Route</p>
<h1 className="mt-2 text-xl font-semibold">{route}</h1>
</div>
</main>
)
}

17
frontend/src/lib/api.ts Normal file
View File

@@ -0,0 +1,17 @@
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:8000/api'
export async function apiRequest<T>(path: string, init?: RequestInit): Promise<T> {
const response = await fetch(`${API_BASE_URL}${path}`, {
headers: {
'content-type': 'application/json',
...init?.headers
},
...init
})
if (!response.ok) {
throw new Error(`API request failed: ${response.status}`)
}
return response.json() as Promise<T>
}

View File

@@ -0,0 +1,6 @@
export function formatDateTime(value: string) {
return new Intl.DateTimeFormat('zh-CN', {
dateStyle: 'medium',
timeStyle: 'short'
}).format(new Date(value))
}

1
frontend/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />