teecup/frontend/app/t/[id]/page.tsx
Erol Haagenrud 6d84aafef8 ADR-018 er nå helt ferdig — backend, begge frontend-skjermer, og nå Open Graph-metadata, alt live samme dag. Oppsummert:
Én reell driftsfeil funnet før den nådde produksjon: generateMetadata() kjører server-side ved forespørselstid — går derfor ikke gjennom next.config.mjs sin rewrites() (som bare gjelder nettleser-trafikk). Den trengte TEECUP_API_ORIGIN direkte, men den variabelen fantes kun i Dockerfile sitt build-steg — ENV fra ett steg arves ikke til det neste i en multi-stage build. Rettet ved å sette den på nytt i runner-steget.

Verifisert presist, ikke bare at det kompilerte: bygget det ekte produksjonsimaget (ikke dev-server), pekte det mot en scratch-backend med ekte data, og hentet den faktiske server-rendrede HTML-en — bekreftet ekte og:title/og:description, og en trygg fallback-tittel for en ukjent turnering.

teeoff.no upåvirket gjennom hele runden.
2026-07-18 10:58:36 +02:00

50 lines
1.6 KiB
TypeScript

import type { Metadata } from "next"
import { PublicTournament } from "@/components/public-tournament"
// Server-side -- IKKE nettleser-fetch, går derfor IKKE gjennom
// next.config.mjs sin rewrites() (kun for trafikk inn til Next.js-serveren
// fra en klient). Peker direkte på API-et.
const API_ORIGIN = process.env.TEECUP_API_ORIGIN || "http://localhost:8000"
type PublicTournamentInfo = {
name: string
organization_name: string
description: string | null
}
export async function generateMetadata({
params,
}: {
params: Promise<{ id: string }>
}): Promise<Metadata> {
const { id } = await params
try {
const res = await fetch(`${API_ORIGIN}/public/tournaments/${id}`, { cache: "no-store" })
if (!res.ok) {
return { title: "Turnering | TeeCup" }
}
const data: PublicTournamentInfo = await res.json()
const title = `${data.name} | ${data.organization_name}`
const description =
data.description?.trim() || `${data.name} arrangeres av ${data.organization_name} på TeeCup.`
return {
title,
description,
openGraph: { title, description, siteName: "TeeCup", type: "website" },
twitter: { card: "summary", title, description },
}
} catch {
// Feil her skal ALDRI hindre selve siden i å laste -- kun forringe
// delingsforhåndsvisningen, som fortsatt får en trygg standardtittel.
return { title: "TeeCup" }
}
}
export default async function PublicTournamentPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
return <PublicTournament tournamentId={id} />
}