Files
alpenwerk-hr/components/employees/NotfallkontaktSection.tsx
Andrei Laas 93cb1c1699
Some checks failed
CI / Lint, Typen, Tests, Build (push) Successful in 11m39s
CI / Migrationen auf leerer Datenbank (push) Has been cancelled
Den Notfallkontakt wie die Angehoerigen darstellen
Beide Abschnitte stehen im selben Reiter und zeigen dasselbe: Personen im
Umfeld. Die Angehoerigen standen in einer Tabelle, der Notfallkontakt in
einer Beschreibungsliste -- zwei Baustile untereinander, ohne dass ein
Unterschied in der Sache dahintersteht.

Jetzt dieselbe Tabelle mit denselben Spaltenkoepfen, und Aendern und
Entfernen als Symbole in der Zeile, wie dort das Entfernen. Der Knopf
"+ Hinzufuegen" steht nur, solange kein Kontakt hinterlegt ist: es gibt
genau einen, und ein Knopf daneben verspraeche eine Liste. Die
Telefonnummer bleibt waehlbar.
2026-09-23 18:29:53 +02:00

142 lines
5.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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 (
<div className="border-t border-border pt-6">
<div className="mb-3 flex items-center justify-between">
<h3 className="text-xs font-bold uppercase tracking-wide text-brand-700">Notfallkontakt</h3>
{/* Nur, solange keiner hinterlegt ist: es gibt genau einen. Ein
zweites „Hinzufügen" daneben verspräche eine Liste, die es hier
nicht gibt — geändert und entfernt wird in der Zeile selbst. */}
{!vorhanden && (
<Button
variant="ghost"
size="sm"
onClick={() => setModalOpen(true)}
className="!px-1 text-brand-700 hover:!bg-transparent hover:underline"
>
<Plus className="h-3.5 w-3.5" /> Hinzufügen
</Button>
)}
</div>
{/* Dieselbe Tabelle wie bei den Angehörigen darüber, Zeile für Zeile
derselbe Aufbau. Vorher stand hier eine Beschreibungsliste — fachlich
dasselbe, optisch ein zweiter Baustil im selben Reiter. Zwei Listen
von Personen im Umfeld sollen gleich aussehen. */}
{vorhanden ? (
<div className="overflow-x-auto rounded border border-border">
<table className="w-full min-w-[600px] text-sm">
<thead>
<tr className="border-b border-border bg-accent-50 text-left text-xs font-semibold uppercase tracking-wide text-ink-muted">
<th className="px-4 py-2.5">Name</th>
<th className="px-4 py-2.5">Telefon</th>
<th className="px-4 py-2.5">Verhältnis</th>
<th className="px-4 py-2.5" />
</tr>
</thead>
<tbody>
<tr className="border-b border-border last:border-0">
<td className="px-4 py-2.5 font-semibold text-ink">{kontakt.name}</td>
<td className="px-4 py-2.5 text-ink-body">
{kontakt.telefon ? (
// Wählbar: im Ernstfall greift jemand in Eile danach.
<a
href={`tel:${kontakt.telefon.replace(/\s/g, "")}`}
className="rounded font-semibold hover:text-brand-700 hover:underline
focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-500"
>
{kontakt.telefon}
</a>
) : (
"–"
)}
</td>
<td className="px-4 py-2.5 text-ink-body">{kontakt.verhaeltnis || "–"}</td>
<td className="px-4 py-2.5 text-right">
<Button
variant="icon"
onClick={() => setModalOpen(true)}
aria-label="Notfallkontakt ändern"
className="hover:!text-brand-700"
>
<Pencil className="h-3.5 w-3.5" />
</Button>
<Button
variant="icon"
onClick={handleEntfernen}
pending={entfernt}
aria-label="Notfallkontakt entfernen"
className="hover:!text-danger-solid"
>
<Trash2 className="h-3.5 w-3.5" />
</Button>
</td>
</tr>
</tbody>
</table>
</div>
) : (
<p className="text-sm text-ink-muted">Kein Notfallkontakt hinterlegt.</p>
)}
{/* `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. */}
<NotfallkontaktModal
key={`${kontakt.name ?? ""}|${kontakt.telefon ?? ""}|${kontakt.verhaeltnis ?? ""}`}
open={modalOpen}
onClose={() => setModalOpen(false)}
employeeId={employeeId}
kontakt={kontakt}
/>
</div>
);
}