feat: initialize zhixing-web with React, React Router, and Tailwind CSS

- Set up the main application structure with App component and routing.
- Implemented global state management for UI theme using Zustand.
- Created API utilities for fetching system status.
- Developed system status page with responsive design and theme toggle.
- Added testing setup with Vitest and initial tests for system status page.
- Configured TypeScript with appropriate settings for the application.
- Integrated Tailwind CSS for styling and responsive design.
- Established Vite configuration for development and testing environments.
This commit is contained in:
yuxuanhui
2026-08-03 13:47:24 +08:00
commit 844d8416bd
61 changed files with 4694 additions and 0 deletions
@@ -0,0 +1,7 @@
import { requestJson } from "@/shared/api/request-json"
import type { SystemStatus } from "./system.types"
export function getSystemStatus(signal?: AbortSignal) {
return requestJson<SystemStatus>("/api/v1/system/status", { signal })
}
@@ -0,0 +1,12 @@
import { useQuery } from "@tanstack/react-query"
import { getSystemStatus } from "./system.api"
export const systemStatusQueryKey = ["system", "status"] as const
export function useSystemStatus() {
return useQuery({
queryFn: ({ signal }) => getSystemStatus(signal),
queryKey: systemStatusQueryKey,
})
}
@@ -0,0 +1,5 @@
export interface SystemStatus {
environment: string
service: string
status: "ok"
}
@@ -0,0 +1,31 @@
import { render, screen } from "@testing-library/react"
import { beforeEach, describe, expect, it, vi } from "vitest"
import { SystemStatusPage } from "./system-status-page"
const useSystemStatus = vi.fn()
vi.mock("@/features/system/api/system.query", () => ({
useSystemStatus: () => useSystemStatus(),
}))
describe("SystemStatusPage", () => {
beforeEach(() => {
useSystemStatus.mockReturnValue({
data: {
environment: "test",
service: "Zhixing Server",
status: "ok",
},
isError: false,
isPending: false,
})
})
it("renders the connected backend state", () => {
render(<SystemStatusPage />)
expect(screen.getByText("运行正常")).toBeInTheDocument()
expect(screen.getByText("test")).toBeInTheDocument()
})
})
@@ -0,0 +1,76 @@
import { Activity, Moon, Sun } from "lucide-react"
import { useSystemStatus } from "@/features/system/api/system.query"
import { useUiStore } from "@/shared/config/ui-store"
import { Badge } from "@/shared/ui/badge"
import { Button } from "@/shared/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/shared/ui/card"
export function SystemStatusPage() {
const status = useSystemStatus()
const theme = useUiStore((state) => state.theme)
const toggleTheme = useUiStore((state) => state.toggleTheme)
const statusLabel = status.isPending
? "正在连接"
: status.isError
? "连接异常"
: "运行正常"
return (
<main className="flex min-h-screen items-center justify-center bg-background p-6 text-foreground">
<Card className="w-full max-w-xl">
<CardHeader className="flex-row items-start justify-between gap-4">
<div className="space-y-1.5">
<CardTitle className="flex items-center gap-2 text-2xl">
<Activity className="size-6 text-primary" aria-hidden="true" />
Zhixing System
</CardTitle>
<CardDescription>
量化系统开发环境已完成最小链路连接。
</CardDescription>
</div>
<Button
aria-label="切换主题"
onClick={toggleTheme}
size="icon"
variant="outline"
>
{theme === "dark" ? (
<Sun aria-hidden="true" />
) : (
<Moon aria-hidden="true" />
)}
</Button>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between rounded-lg border bg-muted/40 p-4">
<div>
<p className="font-medium">后端服务</p>
<p className="text-sm text-muted-foreground">
{status.data?.service ?? "Zhixing Server"}
</p>
</div>
<Badge variant={status.isError ? "destructive" : "default"}>
{statusLabel}
</Badge>
</div>
{status.data ? (
<p className="text-sm text-muted-foreground">
当前环境:
<span className="font-medium text-foreground">
{status.data.environment}
</span>
</p>
) : null}
</CardContent>
</Card>
</main>
)
}