add predictions form component

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 11:12:28 +01:00
parent 22bae2aa82
commit a26f050688

View File

@@ -0,0 +1,127 @@
import { useState } from "react"
import type { Country, Prediction } from "@celebrate-esc/shared"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
interface PredictionsFormProps {
countries: Country[]
existingPrediction: Prediction | null
locked: boolean
onSubmit: (prediction: { predictedWinner: string; top3: string[]; nulPointsPick: string }) => void
}
export function PredictionsForm({ countries, existingPrediction, locked, onSubmit }: PredictionsFormProps) {
const [winner, setWinner] = useState(existingPrediction?.predictedWinner ?? "")
const [top3, setTop3] = useState<string[]>(existingPrediction?.top3 ?? [])
const [nulPoints, setNulPoints] = useState(existingPrediction?.nulPointsPick ?? "")
if (locked) {
if (!existingPrediction) {
return (
<Card>
<CardContent className="py-6 text-center text-muted-foreground">
Predictions are locked. You didn't submit one in time.
</CardContent>
</Card>
)
}
return (
<Card>
<CardHeader>
<CardTitle>Your Predictions (locked)</CardTitle>
</CardHeader>
<CardContent className="flex flex-col gap-2 text-sm">
<p>
<span className="font-medium">Winner:</span>{" "}
{countries.find((c) => c.code === existingPrediction.predictedWinner)?.name}
</p>
<p>
<span className="font-medium">Top 3:</span>{" "}
{existingPrediction.top3.map((code) => countries.find((c) => c.code === code)?.name).join(", ")}
</p>
<p>
<span className="font-medium">Nul Points:</span>{" "}
{countries.find((c) => c.code === existingPrediction.nulPointsPick)?.name}
</p>
</CardContent>
</Card>
)
}
function toggleTop3(code: string) {
setTop3((prev) => {
if (prev.includes(code)) return prev.filter((c) => c !== code)
if (prev.length >= 3) return prev
return [...prev, code]
})
}
const canSubmit = winner && top3.length === 3 && nulPoints && !top3.includes(winner)
return (
<Card>
<CardHeader>
<CardTitle>Predictions</CardTitle>
</CardHeader>
<CardContent className="flex flex-col gap-4">
<div>
<label className="mb-1 block text-sm font-medium">Winner</label>
<select
className="w-full rounded-md border bg-background px-3 py-2 text-sm"
value={winner}
onChange={(e) => setWinner(e.target.value)}
>
<option value="">Select a country...</option>
{countries.map((c) => (
<option key={c.code} value={c.code}>
{c.name}
</option>
))}
</select>
</div>
<div>
<label className="mb-1 block text-sm font-medium">Top 3 (select 3, excl. winner)</label>
<div className="flex flex-wrap gap-1">
{countries
.filter((c) => c.code !== winner)
.map((c) => (
<button
type="button"
key={c.code}
onClick={() => toggleTop3(c.code)}
className={`rounded-full border px-2 py-0.5 text-xs transition-colors ${
top3.includes(c.code)
? "border-primary bg-primary text-primary-foreground"
: "border-border hover:bg-muted"
} ${top3.length >= 3 && !top3.includes(c.code) ? "opacity-40" : ""}`}
>
{c.name}
</button>
))}
</div>
</div>
<div>
<label className="mb-1 block text-sm font-medium">Nul Points (last place)</label>
<select
className="w-full rounded-md border bg-background px-3 py-2 text-sm"
value={nulPoints}
onChange={(e) => setNulPoints(e.target.value)}
>
<option value="">Select a country...</option>
{countries.map((c) => (
<option key={c.code} value={c.code}>
{c.name}
</option>
))}
</select>
</div>
<Button onClick={() => onSubmit({ predictedWinner: winner, top3, nulPointsPick: nulPoints })} disabled={!canSubmit}>
{existingPrediction ? "Update Prediction" : "Submit Prediction"}
</Button>
</CardContent>
</Card>
)
}