"use client"; import { Mail, Send, Webhook } from "lucide-react"; import { startTransition, useActionState, useState, type FormEvent, type ReactNode } from "react"; import { Ankreuzfeld, Badge, Button, Feld, Feldgruppe, Formularfehler, Karte, Textbereich, } from "@/components/ui"; import { VorgangsKnopf } from "@/components/vorgang/VorgangsKnopf"; import type { BenachrichtigungAnsicht } from "@/lib/benachrichtigung/einstellungen"; import { ANLAESSE, ANLASS_TEXT } from "@/lib/benachrichtigung/regeln"; import { benachrichtigungSpeichernAktion, testSendenAktion } from "@/server/actions/einstellungen"; import type { FormularZustand } from "@/server/actions/formular"; type Zustand = FormularZustand & { gespeichert?: boolean }; /** Fester Anfangszustand: ein neues `{}` je Render liesse den Vergleich unten nie zur Ruhe kommen. */ const ANFANG: Zustand = {}; /** * Benachrichtigungskanaele und Anlaesse (#028). Geheimnisse kommen nie zurueck in den Browser: * die Felder starten leer, „gesetzt“ zeigt, dass ein Wert hinterlegt ist, und leer speichern * laesst ihn unveraendert. */ export function BenachrichtigungFormular({ ansicht, darfAendern, }: { ansicht: BenachrichtigungAnsicht; darfAendern: boolean; }) { const [zustand, ausfuehren, laeuft] = useActionState( benachrichtigungSpeichernAktion, ANFANG, ); // Nach dem Speichern neu aufbauen: Passwortfelder leeren sich, die Vorgaben kommen frisch // vom Server (revalidatePath). Ueber `key` statt Formular-Reset, damit Fehlerfaelle die // Eingaben behalten (Muster aus useFormularAktion, #008). const [fassung, setFassung] = useState(0); const [gemerkt, setGemerkt] = useState(zustand); if (zustand !== gemerkt) { setGemerkt(zustand); if (zustand.gespeichert) setFassung((f) => f + 1); } function absenden(ereignis: FormEvent) { ereignis.preventDefault(); const daten = new FormData(ereignis.currentTarget); startTransition(() => ausfuehren(daten)); } return (
{zustand.gespeichert && !laeuft && (

Einstellungen gespeichert.

)} {darfAendern && (
)} ); } function Inhalt({ ansicht, darfAendern, f, }: { ansicht: BenachrichtigungAnsicht; darfAendern: boolean; f: Record; }) { const [sicherheit, setSicherheit] = useState(ansicht.email.sicherheit); const [httpErlaubt, setHttpErlaubt] = useState(ansicht.webhook.httpErlaubt); const { email, webhook } = ansicht; return ( // Leser sehen dieselben Felder, aber gesperrt und ohne Knoepfe.
E-Mail (SMTP) } untertitel="Vorbelegt für Microsoft 365: smtp.office365.com, Port 587, STARTTLS." aktionen={} >
{( [ ["STARTTLS", "STARTTLS", "Port 587 – Microsoft 365"], ["TLS", "TLS (SMTPS)", "Port 465"], ["KEINE", "Unverschlüsselt", "nur internes Relay"], ] as const ).map(([wert, titel, erklaerung]) => ( setSicherheit(wert)} beschriftung={titel} erklaerung={erklaerung} /> ))}
{sicherheit === "KEINE" && ( Ohne Verschlüsselung gehen Zugangsdaten und Inhalt im Klartext über das Netz. Nur für ein Relay im selben, vertrauenswürdigen Netz verwenden. )}
{darfAendern && }
Webhook } untertitel="JSON per POST, optional signiert mit X-Sshkv-Signatur: sha256=." aktionen={} >
setHttpErlaubt(e.currentTarget.checked)} beschriftung="http:// für ein internes Ziel erlauben" erklaerung="Sonst nimmt sshkv nur https://-Adressen an." /> {httpErlaubt && ( Über http:// sind Nutzlast und Signatur unterwegs lesbar und veränderbar. Nur für Ziele im selben, vertrauenswürdigen Netz. )}
{darfAendern && }
{ANLAESSE.map((anlass) => ( ))}
); } function KanalStatus({ aktiv }: { aktiv: boolean }) { return aktiv ? aktiv : aus; } function Warnung({ children }: { children: ReactNode }) { return (

▲ {children}

); } /** * Passwort bzw. Geheimnis: nie vorbelegt. Ist ein Wert gesetzt, zeigt das Feld nur „gesetzt“; * leer lassen behaelt ihn, das Ankreuzfeld entfernt ihn. */ function GeheimFeld({ beschriftung, name, gesetzt, darfAendern, fehler, }: { beschriftung: string; name: string; gesetzt: boolean; darfAendern: boolean; fehler?: string | undefined; }) { if (!darfAendern) { return (
{beschriftung} {gesetzt ? gesetzt : nicht gesetzt}
); } return (
gesetzt Wird verschlüsselt gespeichert und nie wieder angezeigt. ) : ( "Wird verschlüsselt gespeichert und nie wieder angezeigt." ) } fehler={fehler} /> {gesetzt && ( )}
); } function TestKnopf({ kanal }: { kanal: "EMAIL" | "WEBHOOK" }) { return (

Die Testnachricht nutzt den gespeicherten Stand – erst speichern, dann testen. Der Versand läuft im Worker.

testSendenAktion(kanal)} beschriftung="Testnachricht senden" icon={} />
); }