Kleinigkeiten zuerst: "Gelaufen" heisst jetzt "Vergangen", die Kachel "Langzeitabwesend" heisst "Langzeitabwesende", "Eintritte/Austritte (Jahr)" heissen "(YTD)" -- gezaehlt wurde ohnehin seit Jahresbeginn. "Personenkreis" heisst "Grund". Der Hinweis unter dem Grad der Behinderung und der Erklaertext ueber dem Honestly-Report sind weg. "Gehaltsanpassung" steht nicht mehr im Filter des Protokolls: das Gehalt ist aus dem Funktionsumfang, keine Funktion schreibt die Art mehr, und ein Filter, der immer leer ausgeht, sieht aus wie ein Fehler. Im Fenster fuer den Notfallkontakt faellt "Wirksam ab" weg -- eine Telefonnummer fuer den Ernstfall gilt ab sofort. In "Daten aendern" wandert der Block unter die Angehoerigen, dieselbe Reihenfolge wie im Reiter "Stammdaten". Auf der Uebersicht fuehren die Namen unter "Letzte Aktivitaeten" in die Akte; vorher war die Karte eine Sackgasse. "Eintrag berichtigen" bot fuer jedes Feld ein Textfeld an, auch fuer "Notfallkontakt Verhaeltnis", wo die Erfassung sonst eine Liste fuehrt. Wer dort "Gattin" statt "Gattin/Gatte" tippte, erzeugte einen Wert, den keine Auswertung mehr findet -- die Berichtigung schreibt in dieselbe Spalte wie das Formular, nur ohne dessen Pruefung. Welche Felder eine Liste bekommen, steht in lib/historie-felder.ts; ein Test prueft jeden Schluessel gegen app_feld_karte(), damit ein Tippfehler dort nicht still auf ein Textfeld zurueckfaellt. Felder mit Aufzaehlungstyp bleiben bewusst aussen vor: dort ist der gespeicherte Wert nicht die Anzeige. Und die Antwort auf "wo sieht man die Anmerkung beim Austritt?": nirgends. Das Formular sammelte sie ein, terminate_employee liess sie fallen. Bis 20260814100000 stand sie in der Beschreibung des Ereignisses; beim Umschreiben fuer den Nichtantritt ging sie verloren, und die Migration fuer die Austrittsart reichte die verkuerzte Fassung weiter. Sie steht jetzt wieder in der Personalakte und im Protokoll, und die Selbstpruefung faengt den naechsten Verlust ab.
116 lines
4.0 KiB
TypeScript
116 lines
4.0 KiB
TypeScript
"use client";
|
|
|
|
import { useRouter } from "next/navigation";
|
|
import { useState } from "react";
|
|
import { changeEmployeeData } from "@/actions/employees";
|
|
import { Button } from "@/components/ui/Button";
|
|
import { SelectField, TextField } from "@/components/ui/Field";
|
|
import { Modal } from "@/components/ui/Modal";
|
|
import { useToast } from "@/components/ui/Toast";
|
|
import { todayIso } from "@/lib/format";
|
|
import { EMERGENCY_RELATIONS } from "@/lib/types";
|
|
|
|
// 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 [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: todayIso(),
|
|
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 (
|
|
<Modal
|
|
open={open}
|
|
onClose={onClose}
|
|
title={vorhanden ? "Notfallkontakt ändern" : "Notfallkontakt hinterlegen"}
|
|
footer={
|
|
<>
|
|
<Button variant="ghost" onClick={onClose}>
|
|
Abbrechen
|
|
</Button>
|
|
<Button onClick={handleSubmit} pending={pending}>
|
|
Speichern
|
|
</Button>
|
|
</>
|
|
}
|
|
>
|
|
{/* Kein „Wirksam ab": ein Notfallkontakt gilt ab sofort. Das Feld stand
|
|
hier, weil die Datenbankfunktion einen Stichtag kennt — bei einer
|
|
Telefonnummer, die im Ernstfall gewählt wird, ist ein Datum in der
|
|
Zukunft keine sinnvolle Angabe, sondern eine Frage zu viel. */}
|
|
<div className="flex flex-col gap-4">
|
|
<TextField label="Name" required value={name} onChange={setName} />
|
|
<TextField label="Telefonnummer" required type="tel" value={telefon} onChange={setTelefon} />
|
|
{/* Dieselbe Liste wie im Einstellungsassistenten (EMERGENCY_RELATIONS)
|
|
und kein Freitext: sonst stünden „Gattin", „Ehefrau" und „Frau"
|
|
nebeneinander und liessen sich nicht auswerten. */}
|
|
<SelectField
|
|
label="Verhältnis"
|
|
value={verhaeltnis}
|
|
onChange={setVerhaeltnis}
|
|
placeholder="Bitte wählen…"
|
|
options={EMERGENCY_RELATIONS.map((r) => ({ value: r, label: r }))}
|
|
/>
|
|
</div>
|
|
</Modal>
|
|
);
|
|
}
|