teecup/frontend/app/logg-inn/page.tsx
Erol Haagenrud 2562e20ec3 13-årsgrense — håndhevet server-side (eneste vei inn i appen, ADR-051) og med umiddelbar rød feilmelding klientside i begge profil-skjemaer. Testet på nøyaktig dagsgrensen.
PWA-installasjonstekst — skiller nå Safari/Chrome på iOS, ikke lenger feilaktig Safari-only.
Beskrivelsestekst — "golfklubber" fjernet, nytt punkt om at turneringsadmin/-presentasjon fungerer godt på PC.
Dette krever både teecup_api og teecup_frontend (ingen migrasjon).
2026-08-10 07:00:11 +02:00

88 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { Metadata } from "next"
import { cookies } from "next/headers"
import { redirect } from "next/navigation"
import { TeeCupWordmark } from "@/components/teecup/wordmark"
import { TeeCupAuth } from "@/components/teecup/teecup-auth"
export const metadata: Metadata = {
title: "Logg inn TeeCup",
description: "Logg inn på TeeCup for live-scoring og golfturneringer med vennegjengen, klubben eller bedriften.",
}
// Server-side, IKKE nettleser-fetch -- går derfor IKKE gjennom
// next.config.mjs sin rewrites() (samme mønster som generateMetadata i
// app/t/[id]/page.tsx og den forrige app/page.tsx). Peker direkte på API-et.
const API_ORIGIN = process.env.TEECUP_API_ORIGIN || "http://localhost:8000"
// Må matche SESSION_COOKIE_NAME i app/auth.py -- ingen delt konstant på
// tvers av Python/TypeScript, samme mønster som andre API-kontrakt-felt
// som dupliseres bevisst i frontend-koden.
const SESSION_COOKIE_NAME = "teecup_session"
export default async function LoggInnPage() {
// Samme allerede-innlogget-sjekk som /-siden hadde (2026-08-08: /logg-inn
// overtok rollen som ekte innloggingsside, se CHANGELOG.md) -- en bruker
// med gyldig sesjonscookie skal aldri se innloggingsskjemaet på nytt.
const cookieStore = await cookies()
const session = cookieStore.get(SESSION_COOKIE_NAME)
let authenticated = false
let profileComplete = true
if (session) {
try {
const res = await fetch(`${API_ORIGIN}/auth/me`, {
headers: { Cookie: `${SESSION_COOKIE_NAME}=${session.value}` },
cache: "no-store",
})
authenticated = res.ok
if (res.ok) {
const data = await res.json()
profileComplete = Boolean(data.profile_complete)
}
} catch {
// API utilgjengelig -- vis innloggingsskjemaet i stedet for å henge.
authenticated = false
}
}
// redirect() kaster en egen NEXT_REDIRECT-kontrollflyt-exception som MÅ
// boble videre til Next.js -- må derfor stå UTENFOR try/catch-en over
// (en catch der ville slukt den stille og latt innloggingsskjemaet vises
// i stedet for å omdirigere, reell bug funnet under scratch-verifisering
// 2026-08-08, se CHANGELOG.md).
// Ikke rett til /account -- se ARCHITECTURE_DECISIONS.md-tillegg
// 2026-08-09: /velkommen viser kontekst (PWA-installasjon, låste
// hurtighandlinger, presentasjon) FØR skjemaet, i stedet for at
// skjemaet dukker opp helt uten forklaring rett etter innlogging.
if (authenticated) {
redirect(profileComplete ? "/dashboard" : "/velkommen")
}
return (
<main className="relative flex min-h-dvh flex-col items-center justify-center overflow-hidden bg-clubhouse-bg px-4 py-8 text-clubhouse-ink">
{/* Quiet turf-horizon backdrop — the one bit of atmosphere, kept subtle. */}
<div aria-hidden="true" className="pointer-events-none absolute inset-x-0 bottom-0 h-1/3 bg-gradient-to-t from-tee/15 to-transparent" />
<div className="relative z-10 w-full max-w-md">
{/* Header */}
<header className="mb-6 flex flex-col items-center text-center">
<TeeCupWordmark className="h-11" />
<p className="mt-3 text-pretty text-[0.9375rem] font-medium text-clubhouse-muted">
Live-scoring og turneringer fra første tee til pokalen.
</p>
</header>
{/* Card — the scorecard. Continuation screens (2FA, link confirm) can
reuse this exact container. */}
<section className="overflow-hidden rounded-3xl border border-clubhouse-border bg-clubhouse-card shadow-[0_1px_0_rgba(23,32,15,0.04),0_18px_40px_-24px_rgba(23,32,15,0.35)]">
{/* Signature element: a fairway-green scorecard header stripe. */}
<div className="h-2 w-full bg-gradient-to-r from-tee-strong via-tee to-cup" />
<div className="p-6 sm:p-8">
<TeeCupAuth />
</div>
</section>
<p className="mt-6 text-center text-[0.875rem] leading-relaxed text-clubhouse-muted">
Ved å logge inn godtar du vilkårene våre. Trenger du hjelp? Ta kontakt med klubben din.
</p>
</div>
</main>
)
}