Checkpoint: v4.0 media service, compose deploy, and verified docs

这个提交包含在:
cryptocommuniums-afk
2026-03-14 21:45:31 +08:00
父节点 27083d5af9
当前提交 d5431aee0e
修改 41 个文件,包含 4056 行新增883 行删除

查看文件

@@ -54,7 +54,7 @@ export default function Dashboard() {
{/* Welcome header */}
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div>
<h1 className="text-2xl font-bold tracking-tight">
<h1 className="text-2xl font-bold tracking-tight" data-testid="dashboard-title">
{user?.name || "球友"}
</h1>
<div className="flex items-center gap-3 mt-2">
@@ -65,7 +65,7 @@ export default function Dashboard() {
</div>
</div>
<div className="flex gap-2">
<Button onClick={() => setLocation("/training")} className="gap-2">
<Button data-testid="dashboard-training-button" onClick={() => setLocation("/training")} className="gap-2">
<Target className="h-4 w-4" />
</Button>

查看文件

@@ -341,7 +341,7 @@ export default function LiveCamera() {
<div className="absolute inset-0 flex flex-col items-center justify-center text-white/60">
<CameraOff className="h-12 w-12 mb-3" />
<p className="text-sm"></p>
<Button variant="secondary" className="mt-3 gap-2" onClick={() => setShowSetupGuide(true)}>
<Button data-testid="live-camera-start-button" variant="secondary" className="mt-3 gap-2" onClick={() => setShowSetupGuide(true)}>
<Camera className="h-4 w-4" />
</Button>
</div>
@@ -357,7 +357,7 @@ export default function LiveCamera() {
{/* Controls bar */}
<div className="flex items-center justify-center gap-3 p-3 bg-muted/30 flex-wrap">
{!cameraActive ? (
<Button onClick={() => setShowSetupGuide(true)} className="gap-2">
<Button data-testid="live-camera-toolbar-start-button" onClick={() => setShowSetupGuide(true)} className="gap-2">
<Camera className="h-4 w-4" />
</Button>
) : (

查看文件

@@ -42,13 +42,14 @@ export default function Login() {
<Card className="border-0 shadow-xl">
<CardHeader className="text-center pb-2">
<CardTitle className="text-xl"></CardTitle>
<CardTitle className="text-xl" data-testid="login-title"></CardTitle>
<CardDescription>使</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleLogin} className="space-y-4">
<div className="space-y-2">
<Input
data-testid="login-username-input"
type="text"
placeholder="请输入您的用户名"
value={username}
@@ -59,6 +60,7 @@ export default function Login() {
/>
</div>
<Button
data-testid="login-submit-button"
type="submit"
className="w-full h-12 text-base font-medium"
disabled={loginMutation.isPending || !username.trim()}

文件差异内容过多而无法显示 加载差异

查看文件

@@ -95,7 +95,7 @@ export default function Training() {
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold tracking-tight"></h1>
<h1 className="text-2xl font-bold tracking-tight" data-testid="training-title"></h1>
<p className="text-muted-foreground text-sm mt-1">AI个性化训练方案</p>
</div>
</div>
@@ -143,6 +143,7 @@ export default function Training() {
</div>
</div>
<Button
data-testid="training-generate-button"
onClick={() => generateMutation.mutate({ skillLevel, durationDays })}
disabled={generateMutation.isPending}
className="w-full sm:w-auto gap-2"

查看文件

@@ -47,12 +47,12 @@ export default function Videos() {
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold tracking-tight"></h1>
<h1 className="text-2xl font-bold tracking-tight" data-testid="videos-title"></h1>
<p className="text-muted-foreground text-sm mt-1">
· {videos?.length || 0}
</p>
</div>
<Button onClick={() => setLocation("/analysis")} className="gap-2">
<Button data-testid="videos-upload-button" onClick={() => setLocation("/analysis")} className="gap-2">
<Video className="h-4 w-4" />
</Button>
@@ -77,7 +77,7 @@ export default function Videos() {
const status = statusMap[video.analysisStatus] || statusMap.pending;
return (
<Card key={video.id} className="border-0 shadow-sm hover:shadow-md transition-shadow">
<Card key={video.id} className="border-0 shadow-sm hover:shadow-md transition-shadow" data-testid="video-card">
<CardContent className="p-4">
<div className="flex items-start gap-4">
{/* Thumbnail / icon */}