"use client" import type React from "react" import { useEffect, useState } from "react" import { useRouter } from "next/navigation" import Link from "next/link" import { Building2, Calendar, ChevronRight, ChevronsUpDown, ClipboardList, Flag, LogOut, MessageCircle, Plus, Trophy, Users, UserCircle, X, Check, } from "lucide-react" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { Wordmark } from "@/components/wordmark" import { TournamentCard, type Tournament } from "@/components/tournament-card" import { TournamentStatusBadge, type TournamentStatus } from "@/components/tournament-status-badge" import { cn } from "@/lib/utils" type MyOrg = { organization_id: string; name: string; role: string } // ADR-031: "Mine runder" -- turneringer brukeren er SPILLER i (rostret på et // lag), på tvers av organisasjoner, uavhengig av organisasjonsmedlemskap. type MyTournament = { organization_id: string organization_name: string tournament_id: string tournament_name: string status: TournamentStatus team_id: string team_name: string team_color: string | null next_session_at: string | null // 2026-07-21 (deltaker-tilgang-runden): en av brukerens egne matcher, hvis // noen finnes -- lar kortet lenke direkte til lagets chat/scorekort. my_session_id: string | null my_match_id: string | null } type Me = { id: string email: string display_name: string preferred_locale: string profile_complete: boolean organizations: MyOrg[] my_tournaments: MyTournament[] } type ApiTournament = { id: string name: string status: TournamentStatus start_date: string | null end_date: string | null } function toTournament(t: ApiTournament): Tournament { return { id: t.id, name: t.name, status: t.status, startDate: t.start_date ?? undefined, endDate: t.end_date ?? undefined, } } // --- Component ------------------------------------------------------------- export function Dashboard() { const router = useRouter() const [me, setMe] = useState(null) const [loadingMe, setLoadingMe] = useState(true) const [activeOrgId, setActiveOrgId] = useState(null) const [tournaments, setTournaments] = useState([]) const [loadingTournaments, setLoadingTournaments] = useState(false) const [error, setError] = useState(null) // Hent innlogget bruker + organisasjonsmedlemskap. Ingen gyldig sesjon -> // tilbake til innloggingssiden (denne siden krever auth). useEffect(() => { let cancelled = false async function loadMe() { try { const res = await fetch("/auth/me", { credentials: "include" }) if (!res.ok) { router.replace("/") return } const data: Me = await res.json() if (cancelled) return // Obligatorisk profil-fullføring (2026-07-22): dekker enhver vei // INN til dashbordet (magic-link/passord/2FA-verifisering lander // her direkte) -- /account viser selv fullførings-skjemaet så // lenge profil_complete er false. if (!data.profile_complete) { router.replace("/account") return } setMe(data) setActiveOrgId(data.organizations[0]?.organization_id ?? null) } catch { if (!cancelled) router.replace("/") } finally { if (!cancelled) setLoadingMe(false) } } void loadMe() return () => { cancelled = true } }, [router]) // Turneringer hentes per valgt organisasjon, ikke i /auth/me-kallet. useEffect(() => { if (!activeOrgId) { setTournaments([]) return } let cancelled = false async function loadTournaments() { setLoadingTournaments(true) setError(null) try { const res = await fetch(`/orgs/${activeOrgId}/tournaments`, { credentials: "include" }) if (!res.ok) throw new Error(`tournaments: ${res.status}`) const data: ApiTournament[] = await res.json() if (!cancelled) setTournaments(data.map(toTournament)) } catch { if (!cancelled) setError("Klarte ikke å hente turneringer. Prøv igjen om litt.") } finally { if (!cancelled) setLoadingTournaments(false) } } void loadTournaments() return () => { cancelled = true } }, [activeOrgId]) async function handleCreateOrg(name: string) { setError(null) try { const res = await fetch("/orgs", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ name }), }) if (!res.ok) throw new Error(`orgs: ${res.status}`) const org: { id: string; name: string; role: string } = await res.json() setMe((prev) => prev ? { ...prev, organizations: [ ...prev.organizations, { organization_id: org.id, name: org.name, role: org.role }, ], } : prev, ) setActiveOrgId(org.id) } catch { setError("Klarte ikke å opprette organisasjonen. Prøv igjen.") } } async function handleCreateTournament(name: string) { if (!activeOrgId) return setError(null) try { const res = await fetch(`/orgs/${activeOrgId}/tournaments`, { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ name }), }) if (!res.ok) throw new Error(`create tournament: ${res.status}`) const created: ApiTournament = await res.json() setTournaments((prev) => [toTournament(created), ...prev]) } catch { setError("Klarte ikke å opprette turneringen. Prøv igjen.") } } async function handleLogout() { try { await fetch("/auth/logout", { method: "POST", credentials: "include" }) } finally { router.replace("/") } } if (loadingMe) { return (
) } if (!me) return null // router.replace("/") er allerede utløst const activeOrg = me.organizations.find((o) => o.organization_id === activeOrgId) ?? null const hasOrg = me.organizations.length > 0 && activeOrg !== null const hasMyTournaments = me.my_tournaments.length > 0 return (
{error && (

{error}

)}
{hasMyTournaments && } {hasOrg && activeOrg ? ( ) : ( )}
) } // --- "Egne runder" (ADR-033) ------------------------------------------------- // Frittstående rundeføring med detaljert statistikk -- eid direkte av // brukeren (app_user), uavhengig av organisasjon/turnering. Bevisst atskilt // navn fra "Mine runder" under (turnering-deltakelse) for å unngå // forveksling mellom de to konseptene. function PersonalRoundsEntry() { return (

Egne runder

Registrer en runde på egen hånd, med detaljert statistikk -- uavhengig av turnering.