add jury host controls component
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
91
packages/client/src/components/jury-host.tsx
Normal file
91
packages/client/src/components/jury-host.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import { useState } from "react"
|
||||
import type { Entry, JuryRound, JuryResult } from "@celebrate-esc/shared"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
|
||||
interface JuryHostProps {
|
||||
entries: Entry[]
|
||||
currentRound: JuryRound | null
|
||||
results: JuryResult[]
|
||||
onOpenVote: (countryCode: string) => void
|
||||
onCloseVote: () => void
|
||||
}
|
||||
|
||||
export function JuryHost({ entries, currentRound, results, onOpenVote, onCloseVote }: JuryHostProps) {
|
||||
const [selectedCountry, setSelectedCountry] = useState<string | null>(null)
|
||||
const votedCountries = new Set(results.map((r) => r.countryCode))
|
||||
|
||||
if (currentRound) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
Voting: {currentRound.countryFlag} {currentRound.countryName}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Button onClick={onCloseVote} variant="destructive" className="w-full">
|
||||
Close Voting
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Jury Voting</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-3">
|
||||
{results.length > 0 && (
|
||||
<div className="mb-2">
|
||||
<p className="mb-1 text-sm font-medium text-muted-foreground">
|
||||
Completed ({results.length})
|
||||
</p>
|
||||
{results.map((r) => (
|
||||
<div key={r.countryCode} className="flex items-center justify-between text-sm">
|
||||
<span>{r.countryFlag} {r.countryName}</span>
|
||||
<span className="text-muted-foreground">avg {r.averageRating} ({r.totalVotes} votes)</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col gap-1">
|
||||
{entries.map((entry) => {
|
||||
const voted = votedCountries.has(entry.country.code)
|
||||
const isSelected = selectedCountry === entry.country.code
|
||||
return (
|
||||
<button
|
||||
key={entry.country.code}
|
||||
type="button"
|
||||
disabled={voted}
|
||||
onClick={() => setSelectedCountry(isSelected ? null : entry.country.code)}
|
||||
className={`rounded-md border px-3 py-2 text-left text-sm transition-colors ${
|
||||
voted
|
||||
? "border-transparent bg-muted/50 text-muted-foreground line-through opacity-50"
|
||||
: isSelected
|
||||
? "border-primary bg-primary/5"
|
||||
: "hover:bg-muted"
|
||||
}`}
|
||||
>
|
||||
{entry.country.flag} {entry.artist} — {entry.song}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{selectedCountry && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
onOpenVote(selectedCountry)
|
||||
setSelectedCountry(null)
|
||||
}}
|
||||
className="w-full"
|
||||
>
|
||||
Open Voting
|
||||
</Button>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user