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>
This commit is contained in:
parent
6644447b3e
commit
f9ae32f4e1
2 changed files with 65 additions and 2 deletions
|
|
@ -15,6 +15,7 @@ import {
|
|||
ArrowLeft,
|
||||
Camera,
|
||||
Check,
|
||||
Copy,
|
||||
ExternalLink,
|
||||
Globe,
|
||||
Lock,
|
||||
|
|
@ -105,6 +106,21 @@ export function TournamentPresentationPanel({
|
|||
const [sponsorName, setSponsorName] = useState("")
|
||||
const [sponsorUrl, setSponsorUrl] = useState("")
|
||||
const [addingSponsor, setAddingSponsor] = useState(false)
|
||||
|
||||
// Kopier-lenke (2026-08-21) -- fant reell forvirring: organisator delte
|
||||
// sin egen administrator-URL (/tournaments/{id}?org=...) i stedet for
|
||||
// denne, siden lenken under kun var ren, ikke-kopierbar tekst. Samme
|
||||
// kopier-mønster som JoinCodeChip i individual-tournament-detail.tsx.
|
||||
const [linkCopied, setLinkCopied] = useState(false)
|
||||
async function copyPublicLink() {
|
||||
try {
|
||||
await navigator.clipboard.writeText(`${window.location.origin}/t/${tournamentId}`)
|
||||
setLinkCopied(true)
|
||||
setTimeout(() => setLinkCopied(false), 1500)
|
||||
} catch {
|
||||
// stille -- ikke kritisk
|
||||
}
|
||||
}
|
||||
const [sponsorError, setSponsorError] = useState<string | null>(null)
|
||||
const [logoTargetId, setLogoTargetId] = useState<string | null>(null)
|
||||
const [uploadingLogoFor, setUploadingLogoFor] = useState<string | null>(null)
|
||||
|
|
@ -282,7 +298,7 @@ export function TournamentPresentationPanel({
|
|||
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="mb-4 flex flex-col gap-1">
|
||||
<div className="mb-4 flex flex-col gap-2">
|
||||
<p className="text-sm leading-relaxed text-muted-foreground text-pretty">
|
||||
Hvordan turneringen vises på sin offentlige side (
|
||||
<Link
|
||||
|
|
@ -295,6 +311,18 @@ export function TournamentPresentationPanel({
|
|||
</Link>
|
||||
) og klubbsiden.
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={copyPublicLink}
|
||||
className="inline-flex min-h-11 w-fit items-center gap-1.5 rounded-xl border border-border bg-card px-3 py-2 text-sm font-bold text-foreground transition-colors hover:bg-accent/50"
|
||||
>
|
||||
{linkCopied ? (
|
||||
<Check aria-hidden="true" className="size-4 text-primary" />
|
||||
) : (
|
||||
<Copy aria-hidden="true" className="size-4 text-muted-foreground" />
|
||||
)}
|
||||
{linkCopied ? "Kopiert!" : "Kopier offentlig lenke"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
// 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"
|
||||
|
||||
|
|
@ -20,13 +21,30 @@ export function TournamentRouter({
|
|||
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) => (res.ok ? res.json() : Promise.reject()))
|
||||
.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)
|
||||
|
|
@ -40,10 +58,27 @@ export function TournamentRouter({
|
|||
}
|
||||
}, [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/utilgjengelig
|
||||
// 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} />
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue