"use client" // Kontosammenslåing ("Del 2", ADR-080) -- bekreftelsessiden lenken sendt til // MÅL-kontoens (taperens) e-post peker til. INGEN sesjon kreves (kan åpnes // på en annen enhet enn den som ba om sammenslåingen, samme prinsipp som // e-postbytte-bekreftelse) -- selve token-en ER beviset på eierskap. // // To steg, ALDRI automatisk: 1) hent en FERSK forhåndsvisning ved lasting // (les-only, konsumerer ikke token), 2) krev et eksplisitt "Bekreft // sammenslåing"-trykk før selve utførelsen -- en lenke skal aldri kunne // utløse en irreversibel handling bare ved å bli åpnet. import { useEffect, useState } from "react" import Link from "next/link" import { AlertTriangle, CheckCircle2, Merge, XCircle } from "lucide-react" import { Button } from "@/components/ui/button" type Status = "loading" | "ready" | "confirming" | "success" | "error" type ApiOrgMembershipPreview = { organization_id: string organization_name: string keeper_role: string | null loser_role: string | null resulting_role: string } type ApiOrgPlayerPreview = { organization_id: string organization_name: string keeper_has_player: boolean loser_has_player: boolean colliding_registrations: number } type ApiMergePreview = { keeper_id: string keeper_display_name: string keeper_email: string | null loser_id: string loser_display_name: string loser_email: string | null organization_memberships: ApiOrgMembershipPreview[] player_links: ApiOrgPlayerPreview[] friend_count: number shared_friend_count: number were_friends_with_each_other: boolean round_count: number secondary_email_count: number } const ROLE_LABELS: Record = { owner: "eier", admin: "administrator", member: "medlem" } export function AccountMergeConfirmForm({ token }: { token: string }) { const [status, setStatus] = useState("loading") const [preview, setPreview] = useState(null) const [error, setError] = useState(null) useEffect(() => { let cancelled = false async function load() { try { const res = await fetch(`/auth/account-merge/token/${token}`, { credentials: "include" }) if (!res.ok) { const body = await res.json().catch(() => null) throw new Error(body?.detail?.message ?? "Lenken er ugyldig, brukt eller utløpt.") } const data: ApiMergePreview = await res.json() if (!cancelled) { setPreview(data) setStatus("ready") } } catch (err) { if (!cancelled) { setError(err instanceof Error ? err.message : "Noe gikk galt. Prøv igjen.") setStatus("error") } } } void load() return () => { cancelled = true } }, [token]) async function handleConfirm() { setStatus("confirming") setError(null) try { const res = await fetch("/auth/account-merge/confirm", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ token }), }) if (!res.ok) { const body = await res.json().catch(() => null) throw new Error(body?.detail?.message ?? "Klarte ikke å slå sammen kontoene.") } setStatus("success") } catch (err) { setError(err instanceof Error ? err.message : "Noe gikk galt. Prøv igjen.") setStatus("ready") } } if (status === "loading") { return (
) } if (status === "success") { return (

Kontoene er slått sammen

Alt er nå flyttet over. Denne kontoen finnes ikke lenger -- logg inn med den kontoen du slo sammen til.

Til innlogging
) } if (status === "error" || !preview) { return (

Kunne ikke bekrefte

{error}

Til kontoinnstillinger
) } return (

Bekreft kontosammenslåing

{preview.keeper_display_name} {preview.keeper_email ? ` (${preview.keeper_email})` : ""} har bedt om å slå denne kontoen ( {preview.loser_display_name} {preview.loser_email ? `, ${preview.loser_email}` : ""}) sammen med sin.

    {preview.organization_memberships.map((m) => (
  • {m.organization_name}: blir {ROLE_LABELS[m.resulting_role] ?? m.resulting_role}
  • ))} {preview.player_links.map((p) => (
  • {p.organization_name}: spiller-koblingen slås sammen {p.colliding_registrations > 0 ? ` (${p.colliding_registrations} dobbeltregistrering${p.colliding_registrations === 1 ? "" : "er"} ryddes opp)` : ""}
  • ))} {preview.round_count > 0 &&
  • {preview.round_count} runde{preview.round_count === 1 ? "" : "r"} flyttes over
  • } {preview.friend_count > 0 && (
  • {preview.friend_count} venn{preview.friend_count === 1 ? "" : "er"} flyttes over {preview.shared_friend_count > 0 ? ` (${preview.shared_friend_count} felles, slås sammen)` : ""}
  • )} {preview.were_friends_with_each_other &&
  • Dere var venner med hverandre -- fjernes ved sammenslåing
  • } {preview.secondary_email_count > 0 && (
  • {preview.secondary_email_count} ekstra e-postadresse{preview.secondary_email_count === 1 ? "" : "r"} flyttes over
  • )}
{error &&

{error}

}
) }