"use client"; import { useRouter } from "next/navigation"; import { useMemo, useState } from "react"; import { promoteEmployee } from "@/actions/employees"; import { Button } from "@/components/ui/Button"; import { SelectField, TextField } from "@/components/ui/Field"; import { SlideOver } from "@/components/ui/SlideOver"; import { useToast } from "@/components/ui/Toast"; import type { OpenPositionResolved } from "@/lib/positions"; import type { Database, PaygradeType } from "@/lib/types"; import { fmtName } from "@/lib/format"; type EmployeeRow = Database["public"]["Tables"]["employees"]["Row"]; const PAYGRADES: { value: PaygradeType; label: string }[] = [ { value: "A", label: "A – Einstieg" }, { value: "B", label: "B – Qualifiziert" }, { value: "C", label: "C – Erfahren" }, { value: "D", label: "D – Spezialist:in" }, { value: "E", label: "E – Teamleitung" }, { value: "F", label: "F – Bereichsleitung / GF" }, ]; // Eine Beförderung kann auf derselben Planstelle stattfinden oder auf eine // andere führen. Beides kommt vor: jemand wächst auf seiner Stelle, oder er // rückt auf eine höhere — und dann ist die Zielstelle dieselbe Auswahl wie // bei der Versetzung. type Stellenwahl = "selbe" | "neue"; export function PromotePanel({ open, onClose, employee, openPositions, }: { open: boolean; onClose: () => void; employee: EmployeeRow; openPositions: OpenPositionResolved[]; }) { const { showToast } = useToast(); const router = useRouter(); const [effectiveDate, setEffectiveDate] = useState(""); const [newTitle, setNewTitle] = useState(employee.job_title); const [paygrade, setPaygrade] = useState(employee.paygrade); const [stellenwahl, setStellenwahl] = useState("selbe"); const [positionId, setPositionId] = useState(""); const [pending, setPending] = useState(false); const options = useMemo( () => openPositions .slice() .sort((a, b) => a.orgLabel.localeCompare(b.orgLabel, "de") || a.title.localeCompare(b.title, "de")) .map((p) => ({ value: p.id, label: `${p.orgLabel} · ${p.title} (${p.position_number})` })), [openPositions] ); const selected = openPositions.find((p) => p.id === positionId); /** * Die Stelle wählen schlägt ihre Tätigkeit als neue Bezeichnung vor. * * Nur ein Vorschlag: `employees.job_title` darf von der Planstelle * abweichen, und wer eine eigene Bezeichnung tippt, soll sie behalten. * Deshalb wird nur überschrieben, solange das Feld unverändert auf dem * alten Titel steht. */ function stelleWaehlen(id: string) { setPositionId(id); const stelle = openPositions.find((p) => p.id === id); if (stelle && newTitle === employee.job_title) setNewTitle(stelle.title); } async function handleSubmit() { if (!effectiveDate || !newTitle) { showToast("Bitte alle Pflichtfelder ausfüllen.", "error"); return; } if (stellenwahl === "neue" && !positionId) { showToast("Bitte die neue Planstelle auswählen.", "error"); return; } setPending(true); const result = await promoteEmployee({ employee_id: employee.id, effective_date: effectiveDate, new_title: newTitle, new_paygrade: paygrade, target_position_id: stellenwahl === "neue" ? positionId : undefined, }); setPending(false); if (result.success) { showToast(`${employee.first_name} ${employee.last_name} wurde befördert.`); router.refresh(); onClose(); } else { showToast(result.error ?? "Fehler beim Speichern.", "error"); } } return ( {/* Promotion keeps its own purple, matching the Beförderung badge used in the history tab. */} } >
Planstelle
{stellenwahl === "neue" && ( <> {selected && (
{selected.title}
{selected.orgLabel}
{selected.is_chief ? "Leitungsplanstelle" : "Mitarbeiterplanstelle"} {selected.managerName ? ` · berichtet an ${selected.managerName}` : ""}
)} )} setPaygrade(v as PaygradeType)} options={PAYGRADES} />
); }