"use client"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { terminateEmployee } from "@/actions/employees"; import { Button } from "@/components/ui/Button"; import { SelectField, TextField, TextareaField } from "@/components/ui/Field"; import { SlideOver } from "@/components/ui/SlideOver"; import { useToast } from "@/components/ui/Toast"; import type { Database } from "@/lib/types"; import { fmtDate, fmtName } from "@/lib/format"; type EmployeeRow = Database["public"]["Tables"]["employees"]["Row"]; // „No Show" steht am Ende und getrennt: es ist kein Austritt im gewohnten // Sinn, sondern der Fall, dass jemand nie angetreten ist. const NO_SHOW = "No Show"; const EXIT_REASONS = [ "Einvernehmliche Auflösung", "Kündigung AN", "Kündigung AG", "Befristungsablauf", "Pensionierung", "Entlassung", NO_SHOW, ]; type TerminatePanelProps = { open: boolean; onClose: () => void; employee: EmployeeRow; directReportCount: number; }; export function TerminatePanel({ open, onClose, employee, directReportCount }: TerminatePanelProps) { const { showToast } = useToast(); const router = useRouter(); const [exitDate, setExitDate] = useState(""); // Wer noch gar nicht angefangen hat, tritt fast nie aus einem anderen Grund // aus. Die Vorbelegung nimmt den wahrscheinlichen Fall vorweg, ohne die // übrigen zu verstellen. const [reason, setReason] = useState(employee.status === "Geplant" ? NO_SHOW : EXIT_REASONS[0]); const [note, setNote] = useState(""); const [pending, setPending] = useState(false); // Bei einem Nichtantritt ist das Datum nicht frei wählbar: es ist der Tag, // an dem die Person hätte anfangen sollen. Die Datenbank setzt es ohnehin // so; hier steht es sichtbar, damit niemand ein Datum eintippt, das dann // stillschweigend übergangen wird. const istNoShow = reason === NO_SHOW; const wirksamesDatum = istNoShow ? employee.entry_date : exitDate; async function handleSubmit() { if (!wirksamesDatum) { showToast("Bitte ein Austrittsdatum angeben.", "error"); return; } setPending(true); const result = await terminateEmployee({ employee_id: employee.id, exit_date: wirksamesDatum, exit_reason: reason, note, }); setPending(false); if (result.success) { showToast( istNoShow ? `${employee.first_name} ${employee.last_name} ist nicht angetreten.` : `Austritt für ${employee.first_name} ${employee.last_name} erfasst.` ); router.refresh(); onClose(); } else { showToast(result.error ?? "Fehler beim Speichern.", "error"); } } return ( } >
{/* Zuerst, und nicht zu übersehen: bei besonderem Kündigungsschutz gelten eigene Regeln, bevor beendet werden darf. Die Anwendung entscheidet das nicht — sie darf es aber auch nicht verschweigen, und im Vertragsblatt nachzusehen ist genau der Schritt, den man unter Zeitdruck auslässt. */} {employee.has_kuendigungsschutz && (
Achtung: besonderer Kündigungsschutz {employee.kuendigungsschutz_bis ? ` bis ${fmtDate(employee.kuendigungsschutz_bis)}` : " (Ende nicht erfasst)"}. Vor einer Beendigung ist zu prüfen, ob sie zulässig ist — je nach Grund braucht es eine Zustimmung des Betriebsrats oder des Gerichts.
)} {directReportCount > 0 && (
{directReportCount} direkte Berichte werden automatisch der nächsthöheren Führungskraft zugeordnet.
)} ({ value: r, label: r === NO_SHOW ? "No Show (nicht angetreten)" : r }))} /> {istNoShow && (

Die Planstelle wird wieder frei und gilt als nie besetzt. In allen Auswertungen zählt die Person an keinem Stichtag als beschäftigt.

)} {/* Bei einem Nichtantritt wurde nichts ausgegeben, was zurückkäme — deshalb entsteht dort auch keine Offboarding-Checkliste. Für einen echten Austritt legt terminateEmployee sie in derselben Transaktion an; hier steht nur die Ankündigung, damit niemand sie hier sucht und nichts findet. */} {!istNoShow && (

Nach der Bestätigung steht im Reiter „Offboarding“ eine Checkliste zur Verfügung.

)}
); }