diff --git a/components/employees/NotfallkontaktModal.tsx b/components/employees/NotfallkontaktModal.tsx
new file mode 100644
index 0000000..346d257
--- /dev/null
+++ b/components/employees/NotfallkontaktModal.tsx
@@ -0,0 +1,108 @@
+"use client";
+
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { changeEmployeeData } from "@/actions/employees";
+import { Button } from "@/components/ui/Button";
+import { TextField } from "@/components/ui/Field";
+import { Modal } from "@/components/ui/Modal";
+import { useToast } from "@/components/ui/Toast";
+import { todayIso } from "@/lib/format";
+
+// Der Notfallkontakt, ohne den Umweg über „Daten ändern".
+//
+// Geschrieben wird über dieselbe Funktion wie dort: change_employee_data mit
+// **nur** den drei Feldern im Abschnitt `person`. Das ist keine Abkürzung,
+// sondern die Bedingung dafür, dass der Eintrag in der Historie und im
+// Protokoll landet wie jede andere Stammdatenänderung. Die Datenbank fasst
+// ausschliesslich die Felder an, die im Aufruf stehen — sie prüft auf das
+// Vorhandensein des Schlüssels, nicht auf seinen Wert (Migration
+// 20260917120000). Adresse, E-Mail und der Rest bleiben deshalb unberührt.
+
+export type Notfallkontakt = {
+ name: string | null;
+ telefon: string | null;
+ verhaeltnis: string | null;
+};
+
+export function NotfallkontaktModal({
+ open,
+ onClose,
+ employeeId,
+ kontakt,
+}: {
+ open: boolean;
+ onClose: () => void;
+ employeeId: string;
+ kontakt: Notfallkontakt;
+}) {
+ const { showToast } = useToast();
+ const router = useRouter();
+ const [name, setName] = useState(kontakt.name ?? "");
+ const [telefon, setTelefon] = useState(kontakt.telefon ?? "");
+ const [verhaeltnis, setVerhaeltnis] = useState(kontakt.verhaeltnis ?? "");
+ const [effectiveDate, setEffectiveDate] = useState(todayIso());
+ const [pending, setPending] = useState(false);
+
+ const vorhanden = Boolean(kontakt.name);
+
+ async function handleSubmit() {
+ // Name und Telefonnummer gehören zusammen: ein Name ohne Nummer hilft im
+ // Ernstfall niemandem, eine Nummer ohne Namen sagt nicht, wen man dran
+ // hat.
+ if (!name.trim() || !telefon.trim()) {
+ showToast("Name und Telefonnummer sind nötig.", "error");
+ return;
+ }
+ setPending(true);
+ const result = await changeEmployeeData({
+ employee_id: employeeId,
+ effective_date: effectiveDate,
+ person: {
+ emergency_contact_name: name.trim(),
+ emergency_contact_phone: telefon.trim(),
+ emergency_contact_relation: verhaeltnis.trim(),
+ },
+ contract: {},
+ role: {},
+ });
+ setPending(false);
+ if (result.success) {
+ showToast(vorhanden ? "Notfallkontakt geändert." : "Notfallkontakt hinterlegt.");
+ router.refresh();
+ onClose();
+ } else {
+ showToast(result.error ?? "Fehler beim Speichern.", "error");
+ }
+ }
+
+ return (
+
+
+
+ >
+ }
+ >
+
+
+
+
+
+
+
+ );
+}
diff --git a/components/employees/NotfallkontaktSection.tsx b/components/employees/NotfallkontaktSection.tsx
new file mode 100644
index 0000000..c94d992
--- /dev/null
+++ b/components/employees/NotfallkontaktSection.tsx
@@ -0,0 +1,127 @@
+"use client";
+
+import { Pencil, Plus, Trash2 } from "lucide-react";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { changeEmployeeData } from "@/actions/employees";
+import { Button } from "@/components/ui/Button";
+import { useToast } from "@/components/ui/Toast";
+import { todayIso } from "@/lib/format";
+import { NotfallkontaktModal, type Notfallkontakt } from "./NotfallkontaktModal";
+
+// Eigener Abschnitt statt einer Zelle im Raster, und unterhalb der
+// Angehörigen: beides sind Personen im Umfeld, und der Notfallkontakt ist die
+// Ausnahme davon — deshalb steht er zuletzt, nicht dazwischen. Im Ernstfall
+// greift jemand in Eile danach; dann muss die Nummer sofort zu finden sein
+// und wählbar.
+//
+// Der Knopf steht hier, weil er bei den Angehörigen darüber auch steht. Ohne
+// ihn führte der einzige Weg über „Daten ändern" — ein Formular über alle
+// Stammdaten, um eine Telefonnummer einzutragen.
+
+export function NotfallkontaktSection({ employeeId, kontakt }: { employeeId: string; kontakt: Notfallkontakt }) {
+ const { showToast } = useToast();
+ const router = useRouter();
+ const [modalOpen, setModalOpen] = useState(false);
+ const [entfernt, setEntfernt] = useState(false);
+
+ const vorhanden = Boolean(kontakt.name);
+
+ async function handleEntfernen() {
+ setEntfernt(true);
+ // Leere Zeichenketten, nicht ausgelassene Felder: die Datenbank macht
+ // daraus null. Würden die Schlüssel fehlen, bliebe der Kontakt stehen.
+ const result = await changeEmployeeData({
+ employee_id: employeeId,
+ effective_date: todayIso(),
+ person: { emergency_contact_name: "", emergency_contact_phone: "", emergency_contact_relation: "" },
+ contract: {},
+ role: {},
+ });
+ setEntfernt(false);
+ if (result.success) {
+ showToast("Notfallkontakt entfernt.");
+ router.refresh();
+ } else {
+ showToast(result.error ?? "Fehler beim Entfernen.", "error");
+ }
+ }
+
+ return (
+
+ )}
+
+ {/* `key` setzt die Felder auf den gespeicherten Stand zurück, sobald
+ sich der Kontakt geändert hat — das Fenster bleibt sonst montiert
+ und zeigte beim zweiten Öffnen den alten Entwurf. */}
+ setModalOpen(false)}
+ employeeId={employeeId}
+ kontakt={kontakt}
+ />
+
+ );
+}
diff --git a/components/employees/tabs/StammdatenTab.tsx b/components/employees/tabs/StammdatenTab.tsx
index a265b17..9524345 100644
--- a/components/employees/tabs/StammdatenTab.tsx
+++ b/components/employees/tabs/StammdatenTab.tsx
@@ -1,4 +1,5 @@
import { AngehoerigeSection } from "@/components/employees/AngehoerigeSection";
+import { NotfallkontaktSection } from "@/components/employees/NotfallkontaktSection";
import { brauchtAufenthaltstitel } from "@/lib/countries";
import { fmtAge, fmtDate } from "@/lib/format";
import type { Database } from "@/lib/types";
@@ -71,43 +72,14 @@ export function StammdatenTab({ employee, location, dependents }: { employee: Em
- {/* Eigener Abschnitt statt einer Zelle im Raster, und unterhalb der
- Angehörigen: beides sind Personen im Umfeld, und der Notfallkontakt
- ist die Ausnahme davon — deshalb steht er zuletzt, nicht dazwischen.
- Im Ernstfall greift jemand in Eile danach; dann muss die Nummer
- sofort zu finden sein und wählbar. */}
-