feat: add frontend app routing
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
17
frontend/src/lib/api.ts
Normal 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>
|
||||
}
|
||||
6
frontend/src/lib/format.ts
Normal file
6
frontend/src/lib/format.ts
Normal 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
1
frontend/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
Reference in New Issue
Block a user