- Gemeinsame FormPanel-Karte fuer Passwort- und Zwei-Faktor-Seiten - Zwei-Faktor-Code: grosses, zentriertes Ziffernfeld - Backup-Codes als nummerierte Kacheln statt langer Liste - Newsletter-Bestaetigung: Willkommensrabatt als Gutschein mit gestricheltem Rand und Kopier-Knopf (bestaetigt "Kopiert") Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
70 lines
2.5 KiB
TypeScript
70 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" label={t.twoFactorPage.backLabel} />
|
|
<SectionHeading
|
|
eyebrow={t.twoFactorPage.eyebrow}
|
|
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-forest bg-forest/5 border border-forest/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-forest">{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>
|
|
);
|
|
}
|