Files
craftvia/src/components/tenant-recovery-regen-form.tsx
T
msolarczekandClaude Opus 5 c8e6f30a27
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s
Basis: Certvia dev@a48c5fb als Fundament für Craftvia
Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation
und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 11:05:39 +02:00

38 lines
1.6 KiB
TypeScript

"use client";
import { useActionState } from "react";
import { regenerateOwnRecoveryCodes, type MfaEnrollState } from "@/server/actions/account";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
const initial: MfaEnrollState = { status: "idle" };
/** SEC3-c: Recovery-Codes neu erzeugen — Step-up per aktuellem TOTP-Code (F-08). */
export function TenantRecoveryRegenForm() {
const [state, action, pending] = useActionState(regenerateOwnRecoveryCodes, initial);
if (state.status === "done") {
return (
<div className="space-y-2">
<p className="text-[12px] text-[var(--ok)]">Neue Recovery-Codes — nur jetzt sichtbar, die alten sind ungültig:</p>
<ul className="grid grid-cols-2 gap-2 font-mono text-sm">
{state.recoveryCodes.map((c) => (
<li key={c} className="rounded border bg-card px-3 py-1.5 text-center tracking-wider">{c}</li>
))}
</ul>
</div>
);
}
return (
<details>
<summary className="cursor-pointer text-[12.5px] text-muted-foreground">Recovery-Codes neu erzeugen</summary>
<form action={action} className="mt-2 flex items-center gap-2">
<Input name="token" inputMode="numeric" autoComplete="one-time-code" placeholder="6-stelliger Code" className="h-8 w-36" required />
<Button type="submit" variant="outline" size="sm" disabled={pending}>{pending ? "Prüfe…" : "Neu erzeugen"}</Button>
</form>
{state.status === "error" && <p role="alert" className="mt-2 text-[12px] text-[var(--risk)]">{state.message}</p>}
</details>
);
}