---
title: MZ4 Modul Einsatzplanung/Kalender — Reverse-Engineering-Doku
date: 2026-06-30
status: aktiv
type: project
tags: [reverse-engineering, vsm, mz4, einsatzplanung]
---

# MZ4 Einsatzplanung / Kalender

Default-Landing-Modul der VSM/MZ4-Applikation. URL: `index.php?modul=calendar&sessionkey=<TOKEN>`.
Zeigt eine monatliche Gantt-ähnliche Kalenderansicht: Zeilen = Berater/Consultants, Spalten = Tage des Monats (mit KW-Nummern über den Wochen). Dient zur Planung und Übersicht von Einsätzen, Projekten, Methoden-Tätigkeiten und Abwesenheiten aller Mitarbeiter.

---

## 1. Gesamtlayout

### 1.1 Top-Navigation (dunkle Leiste, global)

| Position | Element | Funktion |
|---|---|---|
| Links | BMW + MINI Logos (img: `images/logo.jpg`) | Link zur Startseite (`index.php?sessionkey=...`) |
| Mitte-Links | Hamburger-Icon (mobile only, `glyphicon-th-list`) | Öffnet Mobile-Menü |
| Mitte | Suchfeld (`id="topsearch"`, GET `index.php?modul=fastsearch&fs=...`) | Globale Schnellsuche mit Autocomplete-Dropdown (`id="afautolist"`) |
| Mitte | Zahnrad-Icon (`fa-gear`, `id="sn_settings"`) | Öffnet Settings-Popup (`settingsPopup()` → `#baseModal`) |
| Rechts | Username (`id="user_icon"`, `title="Kais Aseckzai"`) | Nur Anzeige, kein Link |
| Rechts | Power-Icon (`id="logout_button"`, `href="logout.php"`) | Logout |

### 1.2 Haupt-Navigation (zweite Leiste, `id="navneu"`)

Reihenfolge der Menü-Items:

1. **Einsatzplanung** → `modul=calendar`
2. **Projekte** → `modul=mz4_projects`
3. **Reporting** (Dropdown):
   - Kapa-Monitoring
     - Mitarbeiter → `modul=mz4_capacity_reporting_employee`
     - Arbeitstage → `modul=mz4_capacity_reporting_workdays`
   - KPI Reporting
     - Charts → `modul=mz4_kpi_charts`
     - Export → `modul=mz4_kpi_export`
     - KuZu Auswertung → `modul=mz4_kuzu_auswertung`
4. **Lieferanten** → `modul=dealerselect`
5. **Einstellungen** (Dropdown):
   - Benutzer und Rechteverwaltung → `modul=userverwaltung`
   - Konfiguration → `modul=mz4_config`

Systemlink "Favoriten" und "Logout" sind im Quellcode auskommentiert.

### 1.3 Seiten-Struktur (Modul-Inhaltsbereich)

```
<div class="article">
  <div id="module_content">
    <h1>Einsatzplanung</h1>
    <form id="reloadCalendar">   <!-- Toolbar + Kalender-Grid -->
      <div class="btn-toolbar">   <!-- View-Switches + Filter-Controls -->
      <div id="calendar">
        <div id="actionPlanResponsiveContainer" data-cw="1720">
          <div class="calendarFixed">  <!-- sticky Berater-Spalte + Header (doppelt gerendert) -->
          <div class="calendarInner">  <!-- scrollbarer Daten-Bereich -->
    <div id="calendarLegend">  <!-- Legende unterhalb Kalender -->
```

### 1.4 Modal-Dialog (`id="baseModal"`)

Generischer Bootstrap-Modal für alle Popups (Termin erstellen/bearbeiten, Settings, Warnmeldungen). Header und Body werden per AJAX befüllt.

---

## 2. Toolbar-Controls (vollständig)

Alle Toolbar-Elemente liegen innerhalb `<form id="reloadCalendar">` und `<div id="calendarFilters">`.

### 2.1 View-Toggle-Buttons (`id="calendarViewSwitches"`)

| Button | ID | Icon | Title | Aktiv-State |
|---|---|---|---|---|
| Gantt/Einsatzplanung | `viewGantt` | `fa-align-left` | "Einsatzplanung" | Standard-Default (class `active`) |
| Monatsansicht | `viewMonth` | `fa-table` | "Monatsansicht" | — |

Beim Klick auf `viewGantt`: ruft `calendar.setCalendarProjectPlanningView("actionPlanGantt")` oder `"actionPlanInverse"` (je nach Zustand) + `reloadCalendarProjectPlanning()`.
Beim Klick auf `viewMonth`: Setzt View auf `"month"`, blendet `changeView` aus, lädt neu.

Es gibt einen dritten Button `id="changeView"` (wird per JS in das DOM injiziert und nur im Gantt-Modus angezeigt). Er schaltet zwischen `"actionPlanGantt"` und `"actionPlanInverse"` um. ANNAHME: "Inverse" dreht die Achsen oder zeigt Projekte als Zeilen statt Berater.

Hidden-Feld `<input id="calendarView" type="hidden" name="calendarView">` speichert den aktuellen View-Zustand für den POST.

### 2.2 Abteilungs-Dropdown (`id="switchDepartment"`)

Typ: `<select>`, jQuery-UI `selectmenu()`, Breite 180 px.
Name: `departments`. Filter-Typ: `Department`.

Optionen (vollständig aus HTML):

| value | Label |
|---|---|
| `` (leer) | (Alle Abteilungen) — Default |
| `1` | MO-2 — **pre-selected** im gespeicherten Filter |
| `2` | MO-2 UK |
| `3` | MO-22 |
| `4` | MO-23 |
| `8` | MC-14 |
| `9` | MN-14 |
| `10` | MN-90 |
| `13` | MO-24 |

Beim Ändern: aktualisiert auch den Mitarbeiter-Dropdown via `calendar.updateConsultantFilterPP(departmentId)`.

### 2.3 Mitarbeiter-Dropdown (`id="switchConsultant"`)

Typ: `<select>`, jQuery-UI `selectmenu()`, Breite 180 px.
Name: `consultants`. Filter-Typ: `Consultant`.

Optionen — alle Berater (ID → Name):

| ID | Name |
|---|---|
| `` | (Alle Mitarbeiter) — Default |
| 580 | Althammer, Reinhard |
| 765 | Aseckzai, Kais |
| 951 | Barker, Trevor |
| 1126 | Cooper, Matthew |
| 592 | Dickens, William |
| 958 | Fekete, Balázs |
| 1001 | Fischer, Klaus |
| 594 | Gilch, Johann |
| 788 | Gruber, Tomislav |
| 1151 | Hagen, Felix |
| 570 | Klockenhoff, Armin |
| 571 | Knopf, Detlef |
| 595 | Kurzmiller, Jürgen |
| 872 | Löffler, Jürgen |
| 1318 | Nguyen, Duc-Hong |
| 1310 | Rack, Pascal |
| 743 | Schlirf, Hans |
| 1274 | Schlotterbeck, Gerald |
| 597 | Weigand, Klaus |
| 598 | Xue, Ping |
| 1087 | Zich, Christopher |

### 2.4 Aktiv/Alle Scope-Buttons (`id="calendarConsultantScopeSwitches"`)

Hidden-Feld `calendarConsultantScope`. Filter-Typ: `ConsultantScope`.

| Button | ID | Icon | Title | Filter-Text |
|---|---|---|---|---|
| Nur aktive Benutzer | `consultantScopeActive` | `fa-user` | "Nur aktive Benutzer anzeigen" | `consultantScopeActive` — Default |
| Auch inaktive | `consultantScopeAll` | `fa-user-plus` | "Auch inaktive Benutzer anzeigen" | `consultantScopeAll` |

### 2.5 Terminarten-Dropdown (`id="switchAppointmentType"`)

Typ: `<select>`, jQuery-UI `selectmenu()`, Breite 180 px.
Name: `appointmentTypes`. Filter-Typ: `AppointmentType`.

| value | Label |
|---|---|
| `` | (Alle Terminarten) — Default |
| `11` | 1. Kundenauftrag Durchführung |
| `12` | 2. Kundenauftrag Vor-/Nachbereitung |
| `14` | 3. Methoden. Prozesse, Standards |
| `16` | 4. Weiterbildung |
| `17` | 5. Persönliche Abwesenheit |

### 2.6 Projekt-Suche (`id="switchProject"`)

Typ: `<input type="text">`, jQuery-UI `autocomplete()`.
Placeholder: "Projekt". Name: `projects`. Filter-Typ: `Project`.
Dazugehöriges Hidden-Feld: `<input type="hidden" name="projectsId">` (Projekt-ID).

Autocomplete-Source: AJAX-GET auf `ajaxloader.php?modul=calendar&sessionkey=...&aktion=calendar_ac_projects` mit `request.term`-Parameter. Antwort: JSON-Array mit Objekten `{id, value}`. Min-Length: 0 (aber nur bei Eingabe >0 Zeichen wird AJAX gefeuert; bei Leerstring wird Projekt-Filter geleert und Kalender neu geladen).

### 2.7 Monat/Jahr-Navigation

| Element | ID/Name | Typ | Optionen | Default |
|---|---|---|---|---|
| « Vorheriger Monat | `previousMonthButton` | `<button>` | — | — |
| Monat | `switchMonth` (name: `monthmon`) | `<select>`, jQuery-UI selectmenu | Januar–Dezember (`01`–`12`) | `06` (Juni) |
| Jahr | `switchYear` (name: `monthjahr`) | `<select>`, jQuery-UI selectmenu (Breite: 85 px) | 2017–2027 | `2017` |
| » Nächster Monat | `nextMonthButton` | `<button>` | — | — |

Jahres-Range: 2017–2027. Navigation über Monatsgrenzen/Jahresgrenzen wird korrekt behandelt (Dezember + 1 → Januar nächstes Jahr; wenn nächstes Jahr nicht in der Liste → Klick ignoriert).

---

## 3. Kalender-Grid

### 3.1 Aufbau

Die Tabelle (`class="actionPlanTable"`) ist zweigeteilt, um die Berater-Spalte (links) beim horizontalen Scrollen zu fixieren:

- `div.calendarFixed`: Enthält eine Kopie des `<thead>` mit "Berater"-Header und Tagesspalten-Headern. Liegt statisch links.
- `div.calendarInner`: Enthält die vollständige Tabelle (Header + tbody) mit allen Zellen. Scrollt horizontal.

Header-Zeile (`<tr class="actionPlanHeader">`):
- Erste Spalte: `<th class="actionPlanDateHeader">` → Label "Berater"
- Weitere Spalten: `<th class="actionPlanDate text-center">` → Wochentagskürzel + Tagesnummer + KW-Bezeichner (als `<span>` mit `font-size:10px`, positioned absolute, erscheint nur am ersten Tag der Woche)

Format einer Tagesspalte (KW-Start): `KW 22 / DO / 1`
Format einer normalen Tagesspalte: `FR / 2`

### 3.2 Zeilen (Berater)

Jede Berater-Zeile: `<tr class="actionPlanRow">`.
- Erste Zelle: `<td class="actionPlanConsultant" title="Nachname, Vorname">` → `<span class="actionPlanConsultantOverflow">Nachname</span>`
- Datenzellen: eine pro Monatstag, ID-Schema `day<hexhash>` (serverseitig generierter Hash, keine sequenzielle Nummerierung)

### 3.3 Zell-Typen (CSS-Klassen)

| CSS-Klasse | Bedeutung | Rendering |
|---|---|---|
| `actionPlanFree` | Freier Arbeitstag (anderer Berater) | Weiß, Höhe 46 px, klickbar → neuer Termin |
| `actionPlanFreeConsultant` | Freier Arbeitstag (eingeloggter Nutzer) | Hellgrau, klickbar → neuer Termin |
| `actionPlanWeekend` | Wochenende (anderer Berater) | Grau, kein `height:46px` |
| `actionPlanWeekendConsultant` | Wochenende (eingeloggter Nutzer) | Etwas dunkler Grau |
| `feiertag` | Gesetzlicher Feiertag | Bläulich, `title="*Feiertagsname*"` |

ANNAHME: Appointment-Zellen haben zusätzliche CSS-Klassen aus der folgenden Farbpalette (aus Legende, keine Beispieltermine im Snapshot):

| CSS-Klasse | Terminart | Farbe |
|---|---|---|
| `kalorange` | 1. Kundenauftrag Durchführung | Orange |
| `kalblue` | 2. Kundenauftrag Vor-/Nachbereitung | Hellblau |
| `kalbrombeere` | 3. Methoden, Prozesse, Standards | Brombeere (Pink/Lila) |
| `kallhblue` | 4. Weiterbildung | Hellcyan |
| `kalgreen` | 5. Persönliche Abwesenheit | Hellgrün |

Ortsangabe-Marker (overlayen die Terminzelle visuell):

| CSS-Klasse | Bedeutung | Farbe (lt. Screenshot) |
|---|---|---|
| `method_homeoffice` | Homeoffice | Dunkelblau |
| `method_client` | Vor Ort | Mittelgrün |
| `method_remote` | Remote | Dunkelgrün |

### 3.4 Zell-Interaktion

Jede freie Zelle ist per jQuery `bind('click', ...)` mit `app.calendar.onClickWhitespaceMonthView(date, currentUserId, {projectId, consultant}, event)` verknüpft.

Signatur: `onClickWhitespaceMonthView(Date_UTC, loggedInUserId, {projectId: 0, consultant: <rowConsultantId>}, event)`

- `date`: UTC-Datum des Tages (Date.UTC(year, month-1, day))
- Zweiter Parameter: ID des eingeloggten Nutzers (765 = Kais Aseckzai)
- `consultant`: ID des Beraters in der Zeile
- `projectId`: 0 = kein Projekt vorausgewählt (ändert sich wenn Projekt-Filter gesetzt)

Klick auf Appointment-Zelle (ANNAHME): öffnet Context-Menü oder Tooltip mit Optionen: Bearbeiten, Status ändern, Duplizieren, Löschen.

---

## 4. Terminarten und Legende

### 4.1 Terminarten (Appointment Types)

Aus `switchAppointmentType` und `calendarLegend`:

| ID | Label | CSS-Farbe | AJAX-Legend-ID |
|---|---|---|---|
| 11 | 1. Kundenauftrag Durchführung | `kalorange` | `appointment_id=11` |
| 12 | 2. Kundenauftrag Vor-/Nachbereitung | `kalblue` | `appointment_id=12` |
| 14 | 3. Methoden. Prozesse, Standards | `kalbrombeere` | `appointment_id=14` |
| 16 | 4. Weiterbildung | `kallhblue` | `appointment_id=16` |
| 17 | 5. Persönliche Abwesenheit | `kalgreen` | `appointment_id=17` |

### 4.2 Ortstypen (Location Types)

| CSS-Klasse | Label |
|---|---|
| `method_homeoffice` | Homeoffice |
| `method_client` | Vor Ort |
| `method_remote` | Remote |

### 4.3 Legende (`id="calendarLegend"`)

Bootstrap Panel unten auf der Seite. Klick auf eine Legende-Zeile ruft `ajaxLayer('mz4_appointment_legend', '&appointment_id=<id>')` auf — zeigt einen Info-Layer zu der jeweiligen Terminart.

---

## 5. Termin-Felder (aus JS-Labels in calendarOptions)

Felder, die im Tooltip bzw. Create/Edit-Dialog eines Termins erscheinen:

| Label-Key | Deutsches Label | Feld-Bedeutung |
|---|---|---|
| `labelAppointmentConsultant` | Berater | Berater/Mitarbeiter |
| `labelAppointmentPeriod` | Zeitraum | Start- und Enddatum |
| `labelAppointmentComment` | Terminbeschreibung | Freitext-Beschreibung |
| `labelAppointmentSupplier` | Lieferant | Verknüpfter Lieferant/Händler |
| `labelAppointmentPlace` | Ort | Ortsangabe (Homeoffice/Vor Ort/Remote) |

### 5.1 Termin-Status

Zwei Status-Werte (aus `labelFixed`, `labelPlanned`):
- `fix` — fixer/bestätigter Termin
- `geplant` — nur geplant

Status-Änderung: Kontextmenü-Option "Status Ändern" → AJAX `updateStatusAppointment`.
Berechtigungs-Guard: "Sie haben keine Berechtigung den Status zu ändern!" (User darf nicht immer Status ändern — Rollenregelung, ANNAHME: Nur Projektleiter oder zugewiesener Berater).

### 5.2 Warnmeldungen bei Termin-Erstellung

| Situation | Warndialog |
|---|---|
| Nutzer ist weder Projektleiter noch Referent des gebuchten Projekts | "Sie buchen gerade auf ein Projekt, indem Sie weder Projektleiter noch Referent sind! Trotzdem buchen?" → Ja/Nein |
| Gebuchtes Projekt ist bereits abgeschlossen | "Sie buchen gerade auf ein bereits abgeschlossenes Projekt! Trotzdem buchen?" → Ja/Nein |
| Terminüberschneidung | Fehlermeldung: "Terminüberschneidung" |

---

## 6. AJAX-Endpunkte

Alle AJAX-Calls gehen an `ajaxloader.php?modul=calendar&sessionkey=<TOKEN>` + `&aktion=<action>`.

| aktion | Methode | Parameter | Funktion |
|---|---|---|---|
| `showAppointment` | ANNAHME POST | appointment_id? | Termin-Details anzeigen (Tooltip/Popup) |
| `createAppointment` | ANNAHME POST | date, consultant_id, appointment_type, project_id, place, comment, ... | Neuen Termin anlegen |
| `editAppointment` | ANNAHME POST | appointment_id + Felder | Termin bearbeiten |
| `removeAppointment` | ANNAHME POST | appointment_id | Termin löschen |
| `duplicateAppointment` | ANNAHME POST | appointment_id, target_date | Termin duplizieren (auf nächsten Tag) |
| `updateStatusAppointment` | ANNAHME POST | appointment_id, status (fix/geplant) | Status ändern |
| `reloadCalendar` | POST | Alle Filter-Felder (month, year, dept, consultant, appointmentType, project) | Kalender-HTML neu laden |
| `calendar_ac_projects` | GET | `term` (Suchstring) | Projekt-Autocomplete → JSON `[{id, value}]` |
| `mz4_appointment_legend` | ANNAHME GET | `appointment_id` | Info-Layer für Terminart-Legende |

Formular-POST `reloadCalendar` → `index.php?modul=calendar&sessionkey=...&project=0#3` (Fallback für Non-AJAX-Reload). In der Praxis wird das Neuladen per `calendar.reloadCalendarProjectPlanning()` als AJAX durchgeführt.

Weitere Endpunkte aus dem globalen System:
- `index.php?modul=fastsearch&fs=<term>` (GET) — Globale Suche
- `ajaxloader.php` mit Layer-Aufrufen für Settings (`settingsPopup()`)

---

## 7. JavaScript-Architektur

### 7.1 Geladene JS-Dateien

- `jquery-3.1.1.min.js`, `bootstrap.min.js`, `jquery-ui.min.js` — UI-Basis
- `flatpickr.js` + `l10n/de.js` — Datepicker (für Termin-Formulare)
- `jquery.datetimepicker.full.min.js` — alternativer Datepicker
- `App.js` — Globales App-Objekt (`app`)
- `Filter.js` — Filter-Klasse
- `Calendar.js` — Kalender-Komponente
- `jquery.alerts.js` — Custom Alert/Confirm-Dialoge
- `ajaxbase.js`, `js-new/base.js`, `base.js` — AJAX-Hilfsfunktionen
- `js-new/mobile.js` — Mobile-Anpassungen
- `settings_box.js` — Settings-Seitenleiste
- `infobox.js`, `ajax_infobox.js` — Info-Layer

### 7.2 Globale Objekte

```
app               // Haupt-App-Objekt (App.js)
app.calendar      // Instanz von Calendar (Calendar.js)
Filter            // Klasse für Filter-Objekte (Filter.js)
```

### 7.3 Calendar-Klasse (Calendar.js, Methoden bestätigt)

| Methode | Parameter | Funktion |
|---|---|---|
| `new Calendar(options)` | calendarOptions-Objekt | Initialisierung |
| `setCalendarProjectPlanningView(view)` | "actionPlanGantt", "actionPlanInverse", "month" | Setzt Ansicht |
| `setFilter(filter)` | Filter-Instanz | Speichert Filter-State |
| `initFilter(feature, type, value)` | feature="calendar", type, value | Initialisiert Filter beim Start |
| `reloadCalendarProjectPlanning()` | — | AJAX-Reload des Kalenders |
| `onClickWhitespaceMonthView(date, userId, config, event)` | — | Klick auf leere Zelle → Create-Appointment-Flow |
| `updateContainerHeight(height)` | px-Wert | Passt Kalender-Höhe an Viewport an |
| `calculateContainerHeight()` | — | Berechnet optimale Höhe |
| `getCalendarAjaxLink()` | — | Gibt ajaxloader.php-URL zurück |
| `updateConsultantFilterPP(departmentId)` | — | Aktualisiert Mitarbeiter-Dropdown nach Abteilungswechsel |

### 7.4 Filter-Klasse (Filter.js)

```js
new Filter({
    filterFeature: "calendar",
    filterType: "Month" | "Year" | "Department" | "Consultant" | "ConsultantScope" | "AppointmentType" | "Project" | "View",
    filterText: <value>
})
```

Filter-State wird in Hidden-Feld `<input id="filters" type="hidden">` als (vermutlich JSON) serialisiert.

---

## 8. Inferred Data Model

### 8.1 Entitäten

**Berater / Mitarbeiter (consultant)**
- `id` (integer, Primärschlüssel, Beispielwerte: 570–1318)
- `vorname`, `nachname` (dargestellt als "Nachname, Vorname")
- `department_id` (FK → Abteilung)
- `aktiv` (boolean, steuert scope consultantScopeActive/All)

**Abteilung / Department**
- `id` (integer, Beispielwerte: 1–13, nicht durchgehend)
- `name` (MO-2, MO-2 UK, MO-22, MO-23, MC-14, MN-14, MN-90, MO-24)

**Terminart / AppointmentType**
- `id` (integer, Werte: 11, 12, 14, 16, 17)
- `bezeichnung` (1–5, siehe Abschnitt 4.1)
- `css_klasse` (kalorange, kalblue, kalbrombeere, kallhblue, kalgreen)

**Termin / Appointment**
- `id` (integer)
- `consultant_id` (FK → Berater)
- `appointment_type_id` (FK → Terminart)
- `projekt_id` (FK → Projekt, nullable)
- `lieferant_id` (FK → Lieferant/Händler, nullable)
- `datum_start`, `datum_ende` (Datum, kann mehrere Tage umfassen)
- `beschreibung` / `comment` (Freitext)
- `ort` / `place` (Homeoffice / Vor Ort / Remote)
- `status` ('fix' | 'geplant')

**Projekt**
- `id` (integer)
- `bezeichnung` (Suchtext)
- `projektleiter_id` (FK → Berater)
- `referent_id` (FK → Berater)
- `abgeschlossen` (boolean — Warnung beim Buchen)

**Lieferant / Händler** (aus anderen Modulen, `modul=dealerselect`)
- Verknüpft mit Terminen via `lieferant_id`

### 8.2 Berechtigungen (ANNAHME)

- Termin-Erstellung: alle Nutzer (mit Warn-Dialog wenn nicht PL/Referent)
- Status-Änderung: eingeschränkt auf bestimmte Rollen
- ANNAHME: Admins können für jeden Berater buchen; normale Berater nur für sich selbst

---

## 9. UI-Komponenten im Detail

### 9.1 Globale Overlay-Elemente

| Element | ID/Klasse | Funktion |
|---|---|---|
| Dunkelmaske 1 | `#Datenblack` / `class="blackscreen"` | Overlay für Modals |
| Dunkelmaske 2 | `#Datenblack2` / `class="blackscreen2"` | Zweite Ebene |
| Ladeanimation | `#loadstopperover` | Zeigt "lade..." während AJAX-Request (Bild: `whiteloading.gif`) |
| Ladeblocker | `#loadstopper` | Blockiert Interaktion während Laden |
| Info-Box | `#InfoBox` | Floating Info-Layer (initial off-screen: left:-4000px) |
| Info-Box Schwarz | `#InfoBox_Black` | Schwarz-Overlay für Info-Box |
| Daten-Info-Box | `#DatenInfoBox` | Klick-schließbares Info-Layer (`hideLayer2('self')`) |
| Daten-Popup | `#DatenPopup` / `class="massnahmeneintrag"` | Schwebendes Popup (Breite 400px, initial: top:-1500px) |
| Neu-Mitarbeiter | `#DatenNeuMitarbeiter` / `class="massnahmeneintragbox"` | "Neuen Mitarbeiter anlegen"-Formular (off-screen) |
| Settings-Box | `#seetings_box_sub` / `class="ajaxbox"` | Seitenleiste für Settings (mit Schließen-X `fa-3x fa-times`) |
| Alert-Container | `#alertmessagecontainer` → `#alertmessages` | Einblenden von Bootstrap-Alerts |
| Session-Timer | `#sessiontime` | Anzeige der Session-Restzeit (initial display:none) |
| Greyout | `#greyout` | Ausgeh-Overlay |

### 9.2 Bootstrap-Modal (`#baseModal`)

Generischer Modal für alle Popups. Enthält:
- Close-Button (`fa-2x fa-times`, `data-dismiss="modal"`)
- `<h4>` (dynamisch)
- `.modal-body` (dynamisch per AJAX befüllt)
- "Close"-Button

Alle Termin-Create/Edit-Dialoge, die Settings-Ansicht und Warnmeldungen nutzen diesen Modal.

### 9.3 Responsive-Verhalten

Container `#actionPlanResponsiveContainer` hat `data-cw="1720"` (calendar width in px). Bei schmalen Viewports scrollt der `calendarInner`-Bereich horizontal; die Berater-Spalte (`calendarFixed`) bleibt sichtbar.

---

## 10. Business Rules und Sonderfälle

1. **Feiertage**: Werden serverseitig als `class="feiertag" title="*Feiertagsname*"` gerendert. Bsp.: "Pfingstmontag", "Fronleichnam". Keine Buchung möglich (kein Click-Handler).
2. **Wochenenden**: Klassen `actionPlanWeekend`/`actionPlanWeekendConsultant`. Keine `height:46px` → visuell schmaler. ANNAHME: Keine Buchung möglich.
3. **Logged-in User Row**: Zeile des eingeloggten Nutzers (Kais Aseckzai = ID 765) hat Sonder-Klassen (`*Consultant`), die eine andere Hintergrundfarbe erzeugen.
4. **Jahr-Range**: Harte Grenze 2017–2027 im Year-Dropdown. Navigation über die Grenze hinaus wird per JS abgefangen und ignoriert.
5. **Projekt-Filter**: `projectId=0` im Payload bedeutet "kein Projekt-Filter". Die URL der Form-Action enthält `project=0#3` als Fallback.
6. **Terminüberschneidung**: Serverseitig validiert. Fehlercode `"Terminüberschneidung"` → Client zeigt Fehlermeldung.
7. **Abgeschlossenes Projekt**: Serverseitig-Flag wird beim Create-Call geprüft, führt zu clientseitigem Warn-Dialog (Ja/Nein).
8. **Nicht-zugeordnetes Projekt**: Buchung möglich nach Bestätigung.
9. **Status-Berechtigung**: Rollenbasiert. Fehlermeldung wenn nicht berechtigt.
10. **Kommentierter Code**: `switchProgram`-Select (Programm-Filter) ist auskommentiert — war geplant oder wurde entfernt.
11. **Termin duplizieren**: Dialog "Termin auf den folgenden Tag buchen" mit Datepicker ("Wählen Sie das Datum"/"Wählen Sie die Woche").

---

## 11. OPEN QUESTIONS

- OQ-1: Genaues JSON-Format des POST-Body für `createAppointment` (Pflichtfelder, optionale Felder)?
- OQ-2: Was genau zeigt `actionPlanInverse`-View? (Achsen-Tausch, Projekt-Sicht, ...?)
- OQ-3: Wie werden Termin-Blöcke gerendert, die mehrere Tage umfassen? (Colspan? Separate Zellen?)
- OQ-4: Welche Felder hat der `createAppointment`-Dialog genau (Formularlayout, Dropdowns, Pflichtfelder)?
- OQ-5: Wie sieht der `viewMonth`-Modus aus (andere Tabellen-Struktur)?
- OQ-6: Berechtigungsmatrix — welche Rollen (Admin, PL, Referent, Berater) dürfen was?
- OQ-7: Welche Parameter akzeptiert `reloadCalendar` als AJAX (werden Filter als JSON in `filters`-Feld gesendet?)?
- OQ-8: Was macht `mz4_appointment_legend`-Layer genau (Text-Beschreibung oder Termin-Liste)?
- OQ-9: Wie wird der `changeView`-Button ins DOM eingefügt (serverseitig oder per JS)?
- OQ-10: Gibt es Pagination im Kalender oder wird immer ein voller Monat geladen?
- OQ-11: Sind Termin-Blöcke drag-and-drop-fähig (jQuery-UI draggable/droppable)? Kein Hinweis im Snapshot gefunden.
- OQ-12: Was genau triggert das `DatenNeuMitarbeiter`-Formular (Person hinzufügen-Button nicht im Snapshot-HTML gefunden)?

---

## 12. CSS-Dateien

| Datei | Inhalt |
|---|---|
| `/css/css_kalender.css` | Kalender-spezifische Styles (actionPlan*, feiertag, kal*, method_*) |
| `/css/styles-new/base.css` | Basis-Layout, primary_background etc. |
| `/css/styles-new/header.css` | Navigation, Topbar |
| `/css/font-awesome.min.css` | Icon-Font |
| `/css/bootstrap/css/bootstrap.min.css` | Bootstrap Grid/Komponenten |
| `/css/jquery-ui.min.css` | jQuery-UI Widgets (selectmenu, autocomplete, datepicker) |
| `/css/flatpickr/flatpickr.css` | Datepicker-Alternative |
