Grunnmur for oversettelse (cookie-basert, ingen URL-endring), ny /hjelp-side med oversikt+FAQ på norsk og engelsk, og Deltakere+ Rundedeltakelse slått sammen til én bred tabell i Spillere-steget. Resten av appens ~2200 strenger og 479 backend-feilkoder er bevisst utsatt til senere, avgrensede runder -- se FEATURE_BACKLOG.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
125 lines
4.2 KiB
TypeScript
125 lines
4.2 KiB
TypeScript
"use client"
|
|
|
|
// "Mer"-siden (2026-08-01) -- den femte fanen i den faste bunn-
|
|
// navigasjonen (`components/teecup/bottom-nav.tsx`, ADR/V0 2026-08-08)
|
|
// trengte et reelt mål, ikke en lenke til en side som ikke fantes.
|
|
// Samler destinasjoner som allerede finnes andre steder i appen (header,
|
|
// dashbordets organisasjon-fot) på ett sted, i stedet for å bygge noe
|
|
// nytt -- en ren navigasjons-samleside.
|
|
|
|
import { useEffect, useState } from "react"
|
|
import { useRouter } from "next/navigation"
|
|
import Link from "next/link"
|
|
import { Bell, Building2, ChevronRight, CircleHelp, LogOut, MessageSquare, UserCircle, UserPlus } from "lucide-react"
|
|
import { BottomNav } from "@/components/teecup/bottom-nav"
|
|
import { LanguageSwitcher } from "@/components/language-switcher"
|
|
|
|
const C = {
|
|
card: "#ffffff",
|
|
ink: "#012c11",
|
|
muted: "#424941",
|
|
border: "#c1c9bf",
|
|
primary: "#1f6b08",
|
|
} as const
|
|
|
|
type MyOrg = { organization_id: string; name: string; role: string }
|
|
type Me = { display_name: string; first_name: string | null; organizations: MyOrg[] }
|
|
|
|
export function MoreMenu() {
|
|
const router = useRouter()
|
|
const [me, setMe] = useState<Me | null>(null)
|
|
|
|
useEffect(() => {
|
|
fetch("/auth/me", { credentials: "include" })
|
|
.then((res) => (res.ok ? res.json() : null))
|
|
.then((data: Me | null) => setMe(data))
|
|
.catch(() => {})
|
|
}, [])
|
|
|
|
async function handleLogout() {
|
|
try {
|
|
await fetch("/auth/logout", { method: "POST", credentials: "include" })
|
|
} finally {
|
|
router.replace("/logg-inn")
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="flex min-h-[100dvh] flex-col bg-background">
|
|
<header className="border-b pt-[env(safe-area-inset-top)]" style={{ borderColor: C.border }}>
|
|
<div className="mx-auto w-full max-w-2xl px-5 py-4">
|
|
<h1 className="text-xl font-extrabold tracking-tight" style={{ color: C.ink }}>
|
|
Mer
|
|
</h1>
|
|
{me && (
|
|
<p className="text-sm" style={{ color: C.muted }}>
|
|
Innlogget som {me.first_name ?? me.display_name}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</header>
|
|
|
|
<main className="mx-auto w-full max-w-2xl flex-1 px-5 pb-24 pt-4">
|
|
<ul
|
|
className="flex flex-col overflow-hidden rounded-2xl"
|
|
style={{ border: `1px solid ${C.border}`, backgroundColor: C.card }}
|
|
>
|
|
<MenuLink href="/account" icon={UserCircle} label="Konto" />
|
|
<MenuLink href="/my-friends" icon={UserPlus} label="Venner" />
|
|
<MenuLink href="/my-feed" icon={MessageSquare} label="Feed" />
|
|
<MenuLink href="/my-notifications" icon={Bell} label="Varsler" />
|
|
<MenuLink href="/hjelp" icon={CircleHelp} label="Hjelp" last={me?.organizations.length === 0} />
|
|
{me?.organizations.map((o, i) => (
|
|
<MenuLink
|
|
key={o.organization_id}
|
|
href={`/organizations/${o.organization_id}/members?name=${encodeURIComponent(o.name)}`}
|
|
icon={Building2}
|
|
label={o.name}
|
|
last={i === me.organizations.length - 1}
|
|
/>
|
|
))}
|
|
</ul>
|
|
|
|
<div className="mt-6 rounded-2xl p-4" style={{ border: `1px solid ${C.border}`, backgroundColor: C.card }}>
|
|
<LanguageSwitcher />
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={handleLogout}
|
|
className="mt-6 flex min-h-[52px] w-full items-center justify-center gap-2 rounded-2xl text-sm font-bold"
|
|
style={{ border: `1px solid ${C.border}`, backgroundColor: C.card, color: C.ink }}
|
|
>
|
|
<LogOut aria-hidden="true" className="size-4" />
|
|
Logg ut
|
|
</button>
|
|
</main>
|
|
|
|
<BottomNav />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function MenuLink({
|
|
href,
|
|
icon: Icon,
|
|
label,
|
|
last,
|
|
}: {
|
|
href: string
|
|
icon: typeof Bell
|
|
label: string
|
|
last?: boolean
|
|
}) {
|
|
return (
|
|
<li style={last ? undefined : { borderBottom: `1px solid ${C.border}` }}>
|
|
<Link href={href} className="flex min-h-[56px] items-center gap-3 px-4">
|
|
<Icon aria-hidden="true" className="size-5" color={C.primary} />
|
|
<span className="flex-1 truncate text-base font-semibold" style={{ color: C.ink }}>
|
|
{label}
|
|
</span>
|
|
<ChevronRight aria-hidden="true" className="size-5" color={C.muted} />
|
|
</Link>
|
|
</li>
|
|
)
|
|
}
|