Legge til status i TournamentUpdate + enum-cast i update_tournament Scratch-verifisere status-oppdatering (enum-cast, PATCH-semantikk, validering) Bygge frontend-UI for å sette turnering-status Ekte typesjekket frontend-build Oppdatere .md-filer + deploy etter bekreftelse
93 lines
2.8 KiB
TypeScript
93 lines
2.8 KiB
TypeScript
import { ChevronDown } from "lucide-react"
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
export type TournamentStatus = "draft" | "active" | "completed" | "archived"
|
|
|
|
const STATUS_CONFIG: Record<
|
|
TournamentStatus,
|
|
{ label: string; dot: string; badge: string }
|
|
> = {
|
|
draft: {
|
|
label: "Utkast",
|
|
dot: "bg-muted-foreground",
|
|
badge: "bg-muted text-foreground",
|
|
},
|
|
active: {
|
|
label: "Aktiv",
|
|
dot: "bg-primary",
|
|
badge: "bg-primary/15 text-foreground",
|
|
},
|
|
completed: {
|
|
label: "Fullført",
|
|
dot: "bg-brand-orange",
|
|
badge: "bg-brand-orange/12 text-foreground",
|
|
},
|
|
archived: {
|
|
label: "Arkivert",
|
|
dot: "bg-muted-foreground/50",
|
|
badge: "border border-border bg-transparent text-muted-foreground",
|
|
},
|
|
}
|
|
|
|
export function TournamentStatusBadge({ status }: { status: TournamentStatus }) {
|
|
const config = STATUS_CONFIG[status]
|
|
return (
|
|
<span
|
|
className={cn(
|
|
"inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-semibold",
|
|
config.badge,
|
|
)}
|
|
>
|
|
<span className={cn("size-1.5 rounded-full", config.dot)} aria-hidden="true" />
|
|
{config.label}
|
|
</span>
|
|
)
|
|
}
|
|
|
|
// Redigerbar variant -- lar organisator sette status via PATCH
|
|
// /orgs/{id}/tournaments/{id} (ingen egen tilstandsmaskin/overgangsregler,
|
|
// samme tillitsnivå som resten av appen). Brukt i tournament-detail.tsx sin
|
|
// header; den skrivebeskyttede TournamentStatusBadge over brukes fortsatt i
|
|
// dashbordets read-only kortliste.
|
|
export function TournamentStatusPicker({
|
|
status,
|
|
onChange,
|
|
}: {
|
|
status: TournamentStatus
|
|
onChange: (status: TournamentStatus) => void
|
|
}) {
|
|
const config = STATUS_CONFIG[status]
|
|
return (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger
|
|
aria-label="Endre turnering-status"
|
|
className={cn(
|
|
"inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-semibold transition-opacity hover:opacity-80",
|
|
config.badge,
|
|
)}
|
|
>
|
|
<span className={cn("size-1.5 rounded-full", config.dot)} aria-hidden="true" />
|
|
{config.label}
|
|
<ChevronDown aria-hidden="true" className="size-3" />
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="start" className="w-40 rounded-2xl p-1.5">
|
|
{(Object.keys(STATUS_CONFIG) as TournamentStatus[]).map((s) => (
|
|
<DropdownMenuItem
|
|
key={s}
|
|
onClick={() => onChange(s)}
|
|
className="flex cursor-pointer items-center gap-2 rounded-xl px-3 py-2.5 text-sm font-semibold"
|
|
>
|
|
<span className={cn("size-1.5 rounded-full", STATUS_CONFIG[s].dot)} aria-hidden="true" />
|
|
{STATUS_CONFIG[s].label}
|
|
</DropdownMenuItem>
|
|
))}
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
)
|
|
}
|