Files
Wabenhain/src/app/konto/zwei-faktor/page.tsx
T
Nick SzaunigandClaude Opus 5.5 3107a02588 feat(konto): settings page for security and privacy
Adds /konto/einstellungen, which gathers two-factor authentication,
"sign out on all devices", the Erntepost consent, the data export
(Art. 15 GDPR) and account deletion (Art. 17 GDPR) in one place.
The account overview now links there through a "Settings" card
instead of listing these blocks inline. Server actions are unchanged
apart from redirecting failures and revalidating the new page.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-30 21:20:37 +02:00

69 lines
2.5 KiB
TypeScript

import type { Metadata } from "next";
import { redirect } from "next/navigation";
import { Container, SectionHeading, Button, FormPanel } from "@/components/ui";
import { BackLink } from "@/components/back-link";
import { ShieldIcon } from "@/components/account-ui";
import { PasswordField } from "@/components/password-field";
import { getCurrentCustomer } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { startTwoFactorSetupAction, disableTwoFactorAction } from "../actions";
import { getLocale } from "@/i18n/locale";
import { getDictionary } from "@/i18n/get-dictionary";
export const metadata: Metadata = { title: "Zwei-Faktor-Authentifizierung" };
export const dynamic = "force-dynamic";
export default async function ZweiFaktorPage() {
const t = getDictionary(await getLocale());
const customer = await getCurrentCustomer();
if (!customer) redirect("/konto");
const remainingBackupCodes = customer.twoFactorEnabled
? await prisma.twoFactorBackupCode.count({ where: { customerId: customer.id, usedAt: null } })
: 0;
return (
<div className="py-20">
<Container className="max-w-lg">
<FormPanel>
<BackLink href="/konto/einstellungen" label={t.twoFactorPage.backLabel} />
<SectionHeading
title={t.twoFactorPage.title}
subtitle={t.twoFactorPage.subtitle}
/>
{customer.twoFactorEnabled ? (
<div className="mt-8 space-y-6">
<p className="flex items-center gap-3 text-sm text-heading bg-forest/5 border border-heading/20 px-4 py-3">
<ShieldIcon />
<span>
{t.twoFactorPage.activeStatus(remainingBackupCodes)}
</span>
</p>
<form action={disableTwoFactorAction} className="space-y-4 border border-ink/10 p-6">
<h2 className="text-heading">{t.twoFactorPage.disableHeading}</h2>
<PasswordField
name="password"
label={t.twoFactorPage.passwordConfirmLabel}
autoComplete="current-password"
required
/>
<Button type="submit" variant="ghost">
{t.twoFactorPage.disableButton}
</Button>
</form>
</div>
) : (
<form action={startTwoFactorSetupAction} className="mt-8">
<Button type="submit">{t.twoFactorPage.setupButton}</Button>
</form>
)}
</FormPanel>
</Container>
</div>
);
}