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>
99 lines
4.5 KiB
TypeScript
99 lines
4.5 KiB
TypeScript
"use client"
|
|
|
|
// Hjelpeside (ADR-094) -- oversiktside for turneringsflyten + søkbar FAQ.
|
|
// Innholdet ligger i messages/{nb,en}.json under "help" (t.raw(), siden
|
|
// dette er strukturert innhold -- arrays av {title,body}/{question,answer}
|
|
// -- ikke enkeltstrenger). Startet med to konkrete spørsmål brukeren
|
|
// faktisk stilte i en samtale ("hvordan fungerer turneringsmodulen" /
|
|
// "hvordan legger jeg til spillere i spillerpoolen") -- ment å vokse
|
|
// organisk etter hvert som flere spørsmål dukker opp, samme mønster som
|
|
// resten av appen er bygget etter denne økten.
|
|
import { useState } from "react"
|
|
import Link from "next/link"
|
|
import { useTranslations } from "next-intl"
|
|
import { ArrowLeft, Search } from "lucide-react"
|
|
|
|
type OverviewSection = { title: string; body: string }
|
|
type FaqEntry = { question: string; answer: string }
|
|
|
|
export function HelpPage() {
|
|
const t = useTranslations("help")
|
|
const [query, setQuery] = useState("")
|
|
|
|
const sections = t.raw("overview.sections") as OverviewSection[]
|
|
const faq = t.raw("faq") as FaqEntry[]
|
|
|
|
const filteredFaq =
|
|
query.trim() === ""
|
|
? faq
|
|
: faq.filter(
|
|
(entry) =>
|
|
entry.question.toLowerCase().includes(query.toLowerCase()) ||
|
|
entry.answer.toLowerCase().includes(query.toLowerCase()),
|
|
)
|
|
|
|
return (
|
|
<div className="flex min-h-[100dvh] flex-col bg-background">
|
|
<header className="sticky top-0 z-10 border-b border-border bg-background/90 pt-[env(safe-area-inset-top)] backdrop-blur">
|
|
<div className="mx-auto flex w-full max-w-2xl items-center gap-3 px-5 py-4">
|
|
<Link
|
|
href="/more"
|
|
aria-label={t("backLabel")}
|
|
className="flex size-11 shrink-0 items-center justify-center rounded-xl border border-border bg-card text-muted-foreground transition-colors hover:bg-accent/50 hover:text-foreground"
|
|
>
|
|
<ArrowLeft aria-hidden="true" className="size-5" />
|
|
</Link>
|
|
<h1 className="text-xl font-extrabold tracking-tight text-foreground">{t("pageTitle")}</h1>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="mx-auto w-full max-w-2xl flex-1 px-5 py-6 sm:py-8">
|
|
<section className="flex flex-col gap-4 rounded-2xl border border-border bg-card p-5 shadow-md shadow-black/8 sm:p-6">
|
|
<div className="flex flex-col gap-1">
|
|
<h2 className="text-lg font-bold text-foreground">{t("overview.title")}</h2>
|
|
<p className="text-sm leading-relaxed text-muted-foreground text-pretty">{t("overview.intro")}</p>
|
|
</div>
|
|
<div className="flex flex-col gap-4">
|
|
{sections.map((section, i) => (
|
|
<div key={i} className="flex flex-col gap-1">
|
|
<h3 className="text-base font-bold text-foreground">{section.title}</h3>
|
|
<p className="text-sm leading-relaxed text-muted-foreground text-pretty">{section.body}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</section>
|
|
|
|
<section className="mt-6 flex flex-col gap-3">
|
|
<div className="relative">
|
|
<Search
|
|
aria-hidden="true"
|
|
className="pointer-events-none absolute left-3 top-1/2 size-5 -translate-y-1/2 text-muted-foreground"
|
|
/>
|
|
<input
|
|
type="search"
|
|
value={query}
|
|
onChange={(e) => setQuery(e.target.value)}
|
|
placeholder={t("searchPlaceholder")}
|
|
aria-label={t("searchLabel")}
|
|
className="h-11 w-full rounded-xl border border-border bg-card pl-11 pr-3 text-base text-foreground shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex flex-col divide-y divide-border overflow-hidden rounded-2xl border border-border bg-card shadow-md shadow-black/8">
|
|
{filteredFaq.length === 0 && (
|
|
<p className="p-5 text-center text-sm text-muted-foreground">{t("noResults")}</p>
|
|
)}
|
|
{filteredFaq.map((entry, i) => (
|
|
<details key={i} className="group p-4 sm:p-5">
|
|
<summary className="cursor-pointer list-none text-base font-bold text-foreground marker:content-none">
|
|
{entry.question}
|
|
</summary>
|
|
<p className="mt-2 text-sm leading-relaxed text-muted-foreground text-pretty">{entry.answer}</p>
|
|
</details>
|
|
))}
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</div>
|
|
)
|
|
}
|