import { Check, Copy } from "lucide-react"; import { useEffect, useState } from "react"; import { Button } from "./button"; import { Card } from "./card"; interface SecretLine { label: string; value: string; } interface SecretRevealCardProps { // Eyebrow text — typically " — copy now, this is the only time it's shown". eyebrow: string; // Lines to render as blocks; the last is the one the Copy button copies. lines: SecretLine[]; // Label for the Copy button. Defaults to copying the final line's value. copyLabel?: string; onDismiss?: () => void; } // Ceremonial reveal for a one-time secret (client_secret rotation, freshly // minted invite URL). Single primitive so the visual + Copy behaviour stays // in lockstep across admin/clients and admin/invites. export const SecretRevealCard = ({ eyebrow, lines, copyLabel = "Copy", onDismiss }: SecretRevealCardProps) => { const [copied, setCopied] = useState(false); const [copyError, setCopyError] = useState(null); const target = lines[lines.length - 1]?.value ?? ""; useEffect(() => { if (!copied) return; const t = window.setTimeout(() => setCopied(false), 1200); return () => window.clearTimeout(t); }, [copied]); const handleCopy = async () => { setCopyError(null); try { await navigator.clipboard.writeText(target); setCopied(true); } catch { // Clipboard API can reject on permission denial, focus loss, or // when the page is not in a secure context. The secret is the // ONLY-shown-once value here, so a silent failure would lose it // when the admin clicks Dismiss assuming the value is in the // clipboard. Surface the failure and tell the user how to copy // manually. setCopyError("Copy failed. Select the value above and use Ctrl/Cmd+C."); } }; return (

{eyebrow}

{lines.map((line) => ( {line.label}: {line.value} ))}
{onDismiss ? ( ) : null}
{copyError ? (

{copyError}

) : null}
); };