first commit
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Sync to CNB / sync (push) Has been cancelled
Delete old workflow runs / del_runs (push) Has been cancelled
Upstream Sync / Sync latest commits from upstream repo (push) Has been cancelled

This commit is contained in:
2026-01-30 16:57:44 +08:00
commit 3d175d75af
119 changed files with 35834 additions and 0 deletions

130
app/scoreboard/page.tsx Normal file
View File

@@ -0,0 +1,130 @@
"use client";
import { useState } from "react";
import { Tally5, Plus, Minus, RotateCcw, Edit2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { cn } from "@/lib/utils";
export default function ScoreboardPage() {
const [teamA, setTeamA] = useState({ name: "红队", score: 0, color: "bg-rose-500", border: "border-rose-500" });
const [teamB, setTeamB] = useState({ name: "蓝队", score: 0, color: "bg-blue-500", border: "border-blue-500" });
const [editTeam, setEditTeam] = useState<"A" | "B" | null>(null);
const [tempName, setTempName] = useState("");
const handleReset = () => {
setTeamA({ ...teamA, score: 0 });
setTeamB({ ...teamB, score: 0 });
};
const handleSwap = () => {
const temp = { ...teamA };
setTeamA({ ...teamB });
setTeamB(temp);
};
const saveName = () => {
if (editTeam === "A") setTeamA({ ...teamA, name: tempName || teamA.name });
else if (editTeam === "B") setTeamB({ ...teamB, name: tempName || teamB.name });
setEditTeam(null);
};
return (
<div className="space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500">
<div className="flex items-center justify-between border-b pb-4">
<div className="flex items-center space-x-4">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-linear-to-br from-rose-500 to-blue-600 shadow-lg">
<Tally5 className="h-6 w-6 text-white" />
</div>
<div>
<h1 className="text-2xl font-bold tracking-tight"></h1>
<p className="text-muted-foreground"></p>
</div>
</div>
<div className="flex gap-2">
<Button variant="outline" size="sm" onClick={handleSwap}></Button>
<Button variant="outline" size="sm" onClick={handleReset} className="text-destructive hover:text-destructive">
<RotateCcw className="h-4 w-4 mr-2" />
</Button>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 py-4">
<TeamDisplay
team={teamA}
onScoreChange={(val) => setTeamA({ ...teamA, score: Math.max(0, teamA.score + val) })}
onNameEdit={() => {
setEditTeam("A");
setTempName(teamA.name);
}}
/>
<TeamDisplay
team={teamB}
onScoreChange={(val) => setTeamB({ ...teamB, score: Math.max(0, teamB.score + val) })}
onNameEdit={() => {
setEditTeam("B");
setTempName(teamB.name);
}}
/>
</div>
{/* Manual Modal */}
{editTeam && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4 animate-in fade-in duration-200">
<Card className="w-full max-w-sm shadow-2xl">
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Input
value={tempName}
onChange={(e) => setTempName(e.target.value)}
autoFocus
placeholder="输入名称..."
onKeyDown={(e) => e.key === "Enter" && saveName()}
/>
<div className="flex justify-end gap-2">
<Button variant="ghost" onClick={() => setEditTeam(null)}></Button>
<Button onClick={saveName}></Button>
</div>
</CardContent>
</Card>
</div>
)}
</div>
);
}
function TeamDisplay({ team, onScoreChange, onNameEdit }: { team: any, onScoreChange: (v: number) => void, onNameEdit: () => void }) {
return (
<Card className="flex flex-col items-center justify-between py-12 space-y-10 relative overflow-hidden bg-card/50">
<div className={cn("absolute top-0 left-0 w-full h-3", team.color)} />
<div className="flex items-center gap-3 group cursor-pointer hover:opacity-80 transition-all" onClick={onNameEdit}>
<h2 className="text-4xl font-black tracking-tight">{team.name}</h2>
<Edit2 className="h-5 w-5 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" />
</div>
<div className="text-[14rem] font-black tracking-tighter tabular-nums leading-none select-none drop-shadow-sm">
{team.score}
</div>
<div className="flex gap-8">
<button
className="h-24 w-24 rounded-3xl border-4 border-muted hover:bg-muted transition-colors flex items-center justify-center group"
onClick={() => onScoreChange(-1)}
>
<Minus className="h-12 w-12 text-muted-foreground group-hover:text-foreground" />
</button>
<button
className={cn("h-24 w-24 rounded-3xl shadow-xl hover:scale-105 active:scale-95 transition-all flex items-center justify-center", team.color)}
onClick={() => onScoreChange(1)}
>
<Plus className="h-12 w-12 text-white fill-current" />
</button>
</div>
</Card>
);
}