"use client"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { setPositionCostCenter, updatePosition } from "@/actions/positions"; 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 type { PlanstelleZumAendern } from "@/lib/positions"; import type { UnitOption } from "./CreatePositionModal"; // Ändern statt löschen und neu anlegen. // // Die Planstellennummer steht in Ausschreibungen, Budgets und // Protokolleinträgen. Wer wegen eines Tippfehlers in der Tätigkeit eine neue // Nummer vergibt, macht die alten Bezüge wertlos — deshalb gibt es diesen // Dialog. /** * Erwartet eine Planstelle, keine „vielleicht keine". * * Die aufrufende Seite hängt einen `key` mit der Kennung daran und rendert * ihn nur, solange etwas bearbeitet wird. Dadurch baut React den Dialog je * Planstelle neu auf, und die Felder lassen sich direkt aus den Eigenschaften * vorbelegen — statt sie in einem Effekt nachzuziehen, der beim zweiten * Öffnen kurz die Werte der vorigen Stelle zeigt. */ export function EditPositionModal({ position, units, kostenstellen, heute, onClose, }: { position: PlanstelleZumAendern; units: UnitOption[]; kostenstellen: { id: string; code: string; name: string }[]; /** Vom Server, nicht aus new Date(): sonst rechnet der Browser mit seiner Zone. */ heute: string; onClose: () => void; }) { const { showToast } = useToast(); const router = useRouter(); const [jobTitle, setJobTitle] = useState(position.title); const [orgUnitId, setOrgUnitId] = useState(position.org_unit_id); const [isChief, setIsChief] = useState(position.is_chief); const [validFrom, setValidFrom] = useState(position.valid_from); const [validTo, setValidTo] = useState(position.valid_to ?? ""); // Die Kontierung ist ein eigener Vorgang mit eigenem Stichtag, kein Feld // unter anderen: die laufende wird beendet, die neue beginnt. Deshalb steht // hier ein Datum daneben und kein stilles Überschreiben. const [kostenstelleId, setKostenstelleId] = useState( kostenstellen.find((k) => k.code === position.kostenstelle?.code)?.id ?? "" ); const [kostenstelleAb, setKostenstelleAb] = useState(heute); const [pending, setPending] = useState(false); const kostenstelleGeaendert = kostenstelleId !== "" && kostenstellen.find((k) => k.id === kostenstelleId)?.code !== position.kostenstelle?.code; const unit = units.find((u) => u.id === orgUnitId); // Die eigene Leitung zählt nicht als Hindernis für sich selbst. const chiefTaken = Boolean(unit?.hasChief) && !(position.is_chief && orgUnitId === position.org_unit_id); async function handleSubmit() { if (!jobTitle.trim() || !orgUnitId || !validFrom) { showToast("Tätigkeit, Einheit und Gültigkeitsbeginn sind Pflicht.", "error"); return; } setPending(true); const result = await updatePosition({ position_id: position.id, org_unit_id: orgUnitId, job_title: jobTitle.trim(), is_chief: isChief && !chiefTaken, valid_from: validFrom, valid_to: validTo || null, }); if (result.success && kostenstelleGeaendert) { // Nacheinander, nicht gemeinsam: die Umkontierung ist ein eigener // Vorgang mit eigenem Stichtag und eigenem Protokolleintrag. Schlägt sie // fehl, steht die übrige Änderung trotzdem — und die Meldung sagt, was // offen blieb, statt beides stillschweigend zu verwerfen. const kosten = await setPositionCostCenter({ position_id: position.id, cost_center_id: kostenstelleId, valid_from: kostenstelleAb, }); if (!kosten.success) { setPending(false); showToast(kosten.error ?? "Die Kostenstelle konnte nicht geändert werden.", "error"); router.refresh(); return; } } setPending(false); if (result.success) { showToast(kostenstelleGeaendert ? "Planstelle geändert und umkontiert." : "Planstelle geändert."); router.refresh(); onClose(); } else { // Die Meldungen der Datenbankfunktion sind für die Oberfläche // geschrieben („Diese Planstelle ist vergeben …“) und werden gezeigt. showToast(result.error ?? "Die Änderung war nicht möglich.", "error"); } } return ( } >
({ value: u.id, label: `${"  ".repeat(u.depth)}${u.name} (${u.unit_type})`, }))} />
({ value: k.id, label: `${k.code} · ${k.name}` }))} /> {kostenstelleGeaendert && (

Die bisherige Kontierung endet an diesem Tag, die neue beginnt. Was vorher gebucht wurde, bleibt, wo es gebucht wurde.

)}

Die Planstellennummer bleibt. Ist die Stelle vergeben, lassen sich Einheit und Gültigkeitsende nicht ändern — ein Wechsel der Einheit ist eine Versetzung und gehört zur Person, nicht zur Stelle.

); }