teecup/frontend/components/tournament-router.tsx
Erol Haagenrud f9ae32f4e1 Kopierbar offentlig lenke + trygg fallback for ikke-innlogget besøkende
Organisator delte ved et uhell sin egen admin-URL (/tournaments/{id}?org=...)
i stedet for den ekte offentlige (/t/{id}) -- lenken fantes fra før, men var
kun ren, ikke-kopierbar tekst. tournament-presentation.tsx fikk en ekte
kopier-knapp.

Reell, uavhengig bug: tournament-router.tsx falt ubetinget tilbake til
Cup-format-adminskjermen ved ENHVER feil i formatoppslaget, inkl. 401 for en
ikke-innlogget besøkende -- viste et ødelagt lag-oppsett-skjermbilde for en
individuell turnering. Skiller nå 401 fra andre feil og sender videre til
den ekte offentlige siden i stedet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 19:44:02 +02:00

111 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
// Velger riktig turnering-skjerm basert på tournament.format_type
// (ADR-011 lagformat vs. ADR-037 individuell) -- egen, autoritativ henting
// FØR noe rendres, i stedet for å stole på et query-param-hint (ville brutt
// for enhver inngang som ikke går via dashbordets akkurat-nå-opprettet-flyt,
// f.eks. en bokmerket lenke). Samme "hent org-ens turneringsliste og finn
// egen rad"-mønster som tournament-detail.tsx allerede bruker for join_code.
import { useEffect, useState } from "react"
import { useRouter } from "next/navigation"
import { TournamentDetail } from "@/components/tournament-detail"
import { IndividualTournamentDetail } from "@/components/individual-tournament-detail"
export function TournamentRouter({
organizationId,
tournamentId,
tournamentName,
}: {
organizationId: string
tournamentId: string
tournamentName: string
}) {
const router = useRouter()
const [formatType, setFormatType] = useState<"team" | "individual" | null>(null)
const [error, setError] = useState(false)
// Ikke-innlogget besøkende på denne organisator-URL-en (f.eks. en delt
// /tournaments/{id}?org=...-lenke) -- funnet 2026-08-21: den generiske
// feil-fallback-en under viste tidligere ALLTID Cup/lag-format-
// administrasjonsskjermen (samme "trygt mot krasj"-begrunnelse som før),
// helt uavhengig av turneringens faktiske format og helt uavhengig av
// ÅRSAKEN til feilen -- en anonym besøkende på en individuell turnering
// fikk et forvirrende, ødelagt Ryder Cup-oppsett-skjermbilde i stedet for
// en ren "logg inn"-beskjed. Skiller nå eksplisitt 401 (ikke innlogget)
// fra andre feil, og sender i stedet videre til den ekte offentlige siden.
const [notAuthenticated, setNotAuthenticated] = useState(false)
useEffect(() => {
let cancelled = false
fetch(`/orgs/${organizationId}/tournaments`, { credentials: "include" })
.then((res) => {
if (res.status === 401) {
if (!cancelled) setNotAuthenticated(true)
return Promise.reject()
}
return res.ok ? res.json() : Promise.reject()
})
.then((list: { id: string; format_type: string }[]) => {
if (cancelled) return
const mine = list.find((t) => t.id === tournamentId)
setFormatType(mine?.format_type === "individual" ? "individual" : "team")
})
.catch(() => {
if (!cancelled) setError(true)
})
return () => {
cancelled = true
}
}, [organizationId, tournamentId])
useEffect(() => {
if (notAuthenticated) router.replace(`/t/${tournamentId}`)
}, [notAuthenticated, tournamentId, router])
if (notAuthenticated) {
return (
<div className="flex min-h-[100dvh] flex-col items-center justify-center gap-4 bg-background">
<div
aria-hidden="true"
className="size-10 animate-spin rounded-full border-4 border-primary/20 border-t-primary"
/>
</div>
)
}
if (error) {
// Samme trygge fallback som resten av appen: en ukjent/util­gjengelig
// turnering skal ikke krasje -- TournamentDetail sin egen feilhåndtering
// (join_code/teams/players) tar over og viser en tydelig feilmelding.
// (Denne grenen er nå kun for EKTE feil hos en innlogget bruker --
// 401/ikke-innlogget håndteres over, IKKE lenger her.)
return (
<TournamentDetail organizationId={organizationId} tournamentId={tournamentId} tournamentName={tournamentName} />
)
}
if (formatType === null) {
return (
<div className="flex min-h-[100dvh] flex-col items-center justify-center gap-4 bg-background">
<div
aria-hidden="true"
className="size-10 animate-spin rounded-full border-4 border-primary/20 border-t-primary"
/>
</div>
)
}
if (formatType === "individual") {
return (
<IndividualTournamentDetail
organizationId={organizationId}
tournamentId={tournamentId}
tournamentName={tournamentName}
/>
)
}
return (
<TournamentDetail organizationId={organizationId} tournamentId={tournamentId} tournamentName={tournamentName} />
)
}