"use client" import { useActionState, useState, type ReactElement } from "react" import { EyeIcon, EyeOffIcon, PlusIcon, WandSparklesIcon } from "lucide-react" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog" import { Field, FieldGroup, FieldLabel } from "@/components/ui/field" import { Input } from "@/components/ui/input" import { cn } from "@/lib/utils" import { createInternalCredential, updateInternalCredential, type InternalCredentialState, } from "@/modules/admin/actions/internal-credentials" import { BILLING_CYCLES, BILLING_LABELS, PLANS, type Plan } from "@/modules/admin/lib/internal-costs" import type { InternalCredential } from "@/modules/admin/queries/internal-credentials" import { SERVICES } from "@/modules/shared/data/services-data" import { formatAmount } from "@/modules/shared/lib/invoice-math" import { ServiceLogo } from "@/modules/shared/ui/components/service-logo" const selectClassName = "flex h-10 w-full rounded-md border border-input bg-background px-3 text-body outline-none transition-colors disabled:cursor-not-allowed disabled:opacity-50" const PLAN_LABELS: Record = { free: "Kostenlos", paid: "Bezahlt" } // 20 zufällige Zeichen (Web Crypto) ohne leicht verwechselbare Zeichen wie l/1 oder O/0 const ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789!#$%&*+-=?@_" function generatePassword(length = 20) { const values = crypto.getRandomValues(new Uint32Array(length)) return Array.from(values, (value) => ALPHABET[value % ALPHABET.length]).join("") } function DialogBody({ credential, onDone }: { credential: InternalCredential | null; onDone: () => void }) { const [serviceId, setServiceId] = useState(credential?.service ?? SERVICES[0].id) const [plan, setPlan] = useState(credential?.plan ?? "free") const [password, setPassword] = useState("") const [visible, setVisible] = useState(false) async function submit(previous: InternalCredentialState, formData: FormData) { const action = credential ? updateInternalCredential : createInternalCredential const result = await action(previous, formData) if (result && "success" in result) onDone() return result } const [state, formAction, pending] = useActionState(submit, undefined) return ( <> {credential ? "Zugang bearbeiten" : "Neuer interner Zugang"}
{credential && } Dienst *
Bezeichnung Benutzername * Passwort {!credential && *}
setPassword(event.target.value)} required={!credential} autoComplete="new-password" placeholder={credential ? "Leer lassen, um es zu behalten" : undefined} className="font-mono focus-visible:ring-0" />
Login-Adresse Kosten
{PLANS.map((option) => ( ))}
{plan === "paid" && (
Preis in € * Abrechnung
)} {state && "error" in state &&

{state.error}

}
) } export function InternalCredentialDialog({ credential, trigger }: { credential?: InternalCredential; trigger?: ReactElement }) { const [open, setOpen] = useState(false) const [instance, setInstance] = useState(0) // Beim Schließen neu aufbauen, damit das Formular beim nächsten Öffnen leer bzw. aktuell ist function handleOpenChange(next: boolean) { setOpen(next) if (!next) setInstance((key) => key + 1) } return ( }> {!trigger && ( <> Neuer Zugang )} handleOpenChange(false)} /> ) }