diff --git a/components/employees/HistorieBearbeiten.tsx b/components/employees/HistorieBearbeiten.tsx
index 2a93609..f6a0a74 100644
--- a/components/employees/HistorieBearbeiten.tsx
+++ b/components/employees/HistorieBearbeiten.tsx
@@ -31,6 +31,7 @@ export function HistorieBearbeiten({
changes,
istZukunft,
heute,
+ nurDatum = false,
}: {
historyId: string;
employeeId: string;
@@ -43,6 +44,14 @@ export function HistorieBearbeiten({
* eigenen Zeitzone, und in einer Renderfunktion hat die Uhr ohnehin nichts
* verloren. */
heute: string;
+ /**
+ * Der Eintritt hat keine Felder, die sich zurücknehmen liessen — nur ein
+ * Datum, das falsch erfasst sein kann. Daran hängt trotzdem einiges: die
+ * erste Planstellenbesetzung, der frühestmögliche Zeitpunkt jedes weiteren
+ * Ereignisses, die Zugehörigkeit. Die Datenbank prüft das und weist
+ * verständlich ab.
+ */
+ nurDatum?: boolean;
}) {
const [offen, setOffen] = useState(false);
const [laeuft, setLaeuft] = useState(false);
@@ -72,11 +81,14 @@ export function HistorieBearbeiten({
// Stammdaten und Vorgang gegenläufig anzupassen — dafür gibt es die
// fachlichen Vorgänge. Die Datenbank weist es ohnehin ab; hier steht es
// nur früher und freundlicher.
- if (istZukunft && neuesDatum <= heute) {
+ // Beim Eintritt gilt das nicht: er darf in der Vergangenheit *und* in der
+ // Zukunft liegen — ein geplanter Eintritt ist ein gewöhnlicher Fall. Was
+ // dort zusammenpassen muss, prüft die Datenbank und sagt es verständlich.
+ if (!nurDatum && istZukunft && neuesDatum <= heute) {
showToast("Eine geplante Änderung lässt sich hier nicht vorziehen.", "error");
return;
}
- if (!istZukunft && neuesDatum > heute) {
+ if (!nurDatum && !istZukunft && neuesDatum > heute) {
showToast("Eine bereits wirksame Änderung lässt sich nicht in die Zukunft verschieben.", "error");
return;
}
@@ -127,23 +139,32 @@ export function HistorieBearbeiten({
>
{bezeichnung}
- {istZukunft
- ? " — diese Änderung ist noch nicht wirksam. Berichtigt wird, was am Stichtag passieren soll."
- : " — was hier stand, war falsch erfasst. Für eine tatsächliche Änderung ist „Daten ändern“ der richtige Weg."}
+ {nurDatum
+ ? " — der Eintritt selbst bleibt; berichtigt wird nur sein Datum."
+ : istZukunft
+ ? " — diese Änderung ist noch nicht wirksam. Berichtigt wird, was am Stichtag passieren soll."
+ : " — was hier stand, war falsch erfasst. Für eine tatsächliche Änderung ist „Daten ändern“ der richtige Weg."}
-
+ {nurDatum && (
+
+ Daran hängt mehr als eine Zahl: die erste Planstellenbesetzung wandert mit, und kein anderes Ereignis darf
+ vor dem Eintritt liegen. Passt das neue Datum nicht dazu, wird die Änderung mit dem Grund abgewiesen.
+
+ )}
+
+
@@ -176,9 +197,11 @@ export function HistorieBearbeiten({
- {istZukunft
- ? "An den Stammdaten ändert sich jetzt nichts — die Änderung greift erst am Stichtag. Berichtigt wird der geplante Vorgang selbst."
- : "Die Stammdaten werden nachgezogen — je Feld gilt dann der jüngste Eintrag, der es trägt. Hat eine spätere Änderung dasselbe Feld erneut gesetzt, bleibt deren Wert stehen."}{" "}
+ {nurDatum
+ ? "Stammdaten, Historie und Planstellenbesetzung werden gemeinsam nachgezogen."
+ : istZukunft
+ ? "An den Stammdaten ändert sich jetzt nichts — die Änderung greift erst am Stichtag. Berichtigt wird der geplante Vorgang selbst."
+ : "Die Stammdaten werden nachgezogen — je Feld gilt dann der jüngste Eintrag, der es trägt. Hat eine spätere Änderung dasselbe Feld erneut gesetzt, bleibt deren Wert stehen."}{" "}
Die Berichtigung selbst steht im Protokoll.
diff --git a/components/employees/tabs/HistorieTab.tsx b/components/employees/tabs/HistorieTab.tsx
index 703b78c..e382723 100644
--- a/components/employees/tabs/HistorieTab.tsx
+++ b/components/employees/tabs/HistorieTab.tsx
@@ -1,15 +1,20 @@
+"use client";
+
+import { useState } from "react";
import { HistorieBearbeiten } from "@/components/employees/HistorieBearbeiten";
import { HistorieLoeschen } from "@/components/employees/HistorieLoeschen";
import { AenderungsTabelle } from "@/components/ui/AenderungsTabelle";
+import { TextField } from "@/components/ui/Field";
+import { SegmentedControl } from "@/components/ui/SegmentedControl";
import { actionBadgeStyle } from "@/lib/colors";
import { fmtDate, todayIso } from "@/lib/format";
-import { darfKorrigiertWerden, loeschVorschau } from "@/lib/history";
+import { darfBearbeitetWerden, darfKorrigiertWerden, loeschVorschau } from "@/lib/history";
import type { Database } from "@/lib/supabase/types";
type HistoryRow = Database["public"]["Tables"]["employee_history"]["Row"];
-// Die Geschichte einer Person — aufklappbar bis auf die Werte, und dort, wo
-// ein Eintrag irrtümlich entstanden ist, auch zurücknehmbar.
+// Die Geschichte einer Person — aufklappbar bis auf die Werte, filterbar, und
+// dort, wo ein Eintrag irrtümlich entstanden ist, auch zurücknehmbar.
//
// Vorher stand hier nur „Geänderte Felder: Adresse, Ort". Damit liess sich
// zwar sehen, *dass* jemand die Anschrift angefasst hat, aber nicht, was
@@ -21,80 +26,183 @@ type HistoryRow = Database["public"]["Tables"]["employee_history"]["Row"];
// Werte stehen dann schon in der Seite, sind durchsuchbar (Strg+F) und im
// Ausdruck sichtbar, und es braucht kein Skript dafür.
//
-// Der Löschknopf erscheint nur an Einträgen, die sich zurücknehmen lassen.
-// An allen anderen steht stattdessen der Grund — leise, aber lesbar. Ein
-// Knopf, der erst nach dem Klick sagt „geht nicht", wäre eine Falle; ein
-// fehlender Knopf ohne Erklärung wäre ein Rätsel.
+// Die Knöpfe erscheinen nur an Einträgen, wo sie etwas bewirken können. An
+// allen anderen steht stattdessen der Grund — leise, aber lesbar. Ein Knopf,
+// der erst nach dem Klick sagt „geht nicht", wäre eine Falle; ein fehlender
+// Knopf ohne Erklärung wäre ein Rätsel.
+
+type Sicht = "alle" | "anstehend" | "erledigt";
+
+const SICHTEN: { value: Sicht; label: string }[] = [
+ { value: "alle", label: "Alle" },
+ { value: "anstehend", label: "Anstehend" },
+ { value: "erledigt", label: "Gelaufen" },
+];
export function HistorieTab({ history, employeeId }: { history: HistoryRow[]; employeeId: string }) {
const today = todayIso();
+ const [sicht, setSicht] = useState("alle");
+ const [von, setVon] = useState("");
+ const [bis, setBis] = useState("");
+ // Ereignistypen, die in dieser Akte überhaupt vorkommen — eine Auswahl aus
+ // elf Typen, von denen zehn nie auftauchen, wäre nur Suchaufwand.
+ const vorhandeneTypen = [...new Set(history.map((h) => h.event_type))];
+ const [typen, setTypen] = useState>(new Set());
+
+ const gefiltert = history.filter((h) => {
+ if (sicht === "anstehend" && h.event_date <= today) return false;
+ if (sicht === "erledigt" && h.event_date > today) return false;
+ if (von && h.event_date < von) return false;
+ if (bis && h.event_date > bis) return false;
+ if (typen.size > 0 && !typen.has(h.event_type)) return false;
+ return true;
+ });
+
+ const anstehend = history.filter((h) => h.event_date > today).length;
+ const eingeschraenkt = sicht !== "alle" || Boolean(von) || Boolean(bis) || typen.size > 0;
+
+ function typUmschalten(typ: string) {
+ setTypen((prev) => {
+ const next = new Set(prev);
+ if (next.has(typ)) next.delete(typ);
+ else next.add(typ);
+ return next;
+ });
+ }
+
+ function zuruecksetzen() {
+ setSicht("alle");
+ setVon("");
+ setBis("");
+ setTypen(new Set());
+ }
if (history.length === 0) {
return Keine Historieneinträge vorhanden.
;
}
return (
-