Some checks failed
Deploy to Cloudflare Workers / deploy (push) Has been cancelled
169 lines
5.2 KiB
TypeScript
169 lines
5.2 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { api } from "@/lib/api/client";
|
|
import { GlassCard } from "@/components/ui/glass-card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
export function MatchActions({
|
|
matchId,
|
|
homeTeamId,
|
|
awayTeamId,
|
|
userTeamId,
|
|
status,
|
|
resultStatus,
|
|
isLeagueManager,
|
|
proposedAt,
|
|
}: {
|
|
matchId: string;
|
|
homeTeamId: string;
|
|
awayTeamId: string;
|
|
userTeamId: string | null;
|
|
status: string;
|
|
resultStatus: string | null;
|
|
isLeagueManager: boolean;
|
|
proposedAt: string | null;
|
|
}) {
|
|
const router = useRouter();
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
async function run(fn: () => Promise<void>) {
|
|
setLoading(true);
|
|
setError(null);
|
|
try {
|
|
await fn();
|
|
router.refresh();
|
|
} catch (e) {
|
|
setError(e instanceof Error ? e.message : "Error");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
{error && <p className="text-sm text-red-400">{error}</p>}
|
|
|
|
{userTeamId && status !== "completed" && (
|
|
<GlassCard title="Schedule">
|
|
{proposedAt && (
|
|
<p className="mb-3 text-sm text-[var(--color-muted)]">
|
|
Proposed: {new Date(proposedAt).toLocaleString()}
|
|
</p>
|
|
)}
|
|
<form
|
|
className="flex flex-wrap items-end gap-3"
|
|
onSubmit={(e) => {
|
|
e.preventDefault();
|
|
const fd = new FormData(e.currentTarget);
|
|
run(async () => {
|
|
const raw = fd.get("scheduled_at") as string;
|
|
await api.matches.proposeSchedule(
|
|
matchId,
|
|
new Date(raw).toISOString()
|
|
);
|
|
});
|
|
}}
|
|
>
|
|
<div>
|
|
<Label htmlFor="scheduled_at">Propose kickoff</Label>
|
|
<Input
|
|
id="scheduled_at"
|
|
name="scheduled_at"
|
|
type="datetime-local"
|
|
className="mt-1"
|
|
required
|
|
/>
|
|
</div>
|
|
<Button type="submit" disabled={loading}>
|
|
Propose
|
|
</Button>
|
|
</form>
|
|
<Button
|
|
className="mt-3"
|
|
variant="secondary"
|
|
disabled={loading || !userTeamId}
|
|
onClick={() =>
|
|
run(() => api.matches.signSchedule(matchId, userTeamId!))
|
|
}
|
|
>
|
|
Sign schedule
|
|
</Button>
|
|
</GlassCard>
|
|
)}
|
|
|
|
{userTeamId && status !== "completed" && (
|
|
<GlassCard title="Submit result">
|
|
<form
|
|
className="flex flex-wrap items-end gap-3"
|
|
onSubmit={(e) => {
|
|
e.preventDefault();
|
|
const fd = new FormData(e.currentTarget);
|
|
run(() =>
|
|
api.matches.submitResult(matchId, {
|
|
teamId: userTeamId!,
|
|
homeScore: Number(fd.get("home_score")),
|
|
awayScore: Number(fd.get("away_score")),
|
|
})
|
|
);
|
|
}}
|
|
>
|
|
<div>
|
|
<Label>Home score</Label>
|
|
<Input name="home_score" type="number" min={0} required className="mt-1 w-20" />
|
|
</div>
|
|
<div>
|
|
<Label>Away score</Label>
|
|
<Input name="away_score" type="number" min={0} required className="mt-1 w-20" />
|
|
</div>
|
|
<Button type="submit" disabled={loading}>
|
|
Submit result
|
|
</Button>
|
|
</form>
|
|
</GlassCard>
|
|
)}
|
|
|
|
{isLeagueManager && resultStatus === "pending_approval" && (
|
|
<GlassCard title="League manager" highlight>
|
|
<Button
|
|
disabled={loading}
|
|
onClick={() => run(() => api.matches.approveResult(matchId))}
|
|
>
|
|
Approve matching result
|
|
</Button>
|
|
</GlassCard>
|
|
)}
|
|
|
|
{isLeagueManager && (
|
|
<GlassCard title="Set official result">
|
|
<form
|
|
className="flex flex-wrap items-end gap-3"
|
|
onSubmit={(e) => {
|
|
e.preventDefault();
|
|
const fd = new FormData(e.currentTarget);
|
|
run(() =>
|
|
api.matches.setResult(matchId, {
|
|
homeScore: Number(fd.get("home_score")),
|
|
awayScore: Number(fd.get("away_score")),
|
|
note: (fd.get("note") as string) || undefined,
|
|
})
|
|
);
|
|
}}
|
|
>
|
|
<Input name="home_score" type="number" min={0} placeholder="H" className="w-20" required />
|
|
<Input name="away_score" type="number" min={0} placeholder="A" className="w-20" required />
|
|
<Input name="note" placeholder="Note (optional)" className="flex-1 min-w-[120px]" />
|
|
<Button type="submit" disabled={loading}>
|
|
Set result
|
|
</Button>
|
|
</form>
|
|
</GlassCard>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|