- Termin aus Mail: Mailtext oder .eml per Drag & Drop/Strg+V auf Terminplan oder Aufgaben, KI schlägt Aufgaben vor, Prüfung im Aufgaben-Panel; neues Notizfeld je Aufgabe (plan_todos.notiz) - Terminplan: Meilensteine ohne Aufgabenbezug je in eigener Zeile, "+ Neu"-Menü klappt neben schmaler Seitenleiste nach rechts, Leistungsphasen - Rollen & Rechte, Abwesenheiten-Seite, Benachrichtigungen, Rundgang - Glas-Darstellung, Logo als Favicon - Pi-Betrieb: PDF-Arbeit im Kindprozess, Wächter, Einrichtungs- und Prüfskript inkl. WLAN-Wächter Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
167 lines
8.7 KiB
JavaScript
167 lines
8.7 KiB
JavaScript
// -----------------------------
|
|
// Projektübersicht — globale Konstanten & Zustand
|
|
// -----------------------------
|
|
// Diese Datei wird als Erstes von App.html geladen und
|
|
// legt alle zentralen Konstanten (Leistungsphasen, Rollen, Gruppen, Abteilungen,
|
|
// Farben, Passwort-Hash) sowie den globalen In-Memory-Zustand (db, currentTab)
|
|
// fest. Sie stellt keine Funktionen, sondern nur Daten bereit, auf die alle
|
|
// weiteren Module (js/store.js, js/board/board.js, js/gantt.js, js/plan/*, ...)
|
|
// zugreifen. Auf dem Plan-Gantt lädt zusätzlich js/plan/plan-state.js Zustand
|
|
// für die Planungsseite. DEPARTMENTS/DEFAULT_DEPARTMENT bleiben trotz Wegfall
|
|
// der Abteilungstrennung erhalten — sie werden weiterhin für die
|
|
// Benutzerverwaltung (users.department, Profil-Anzeige) gebraucht.
|
|
|
|
// Konstanten: Leistungsphasen, Rollen und LocalStorage-Key
|
|
// LPHS: alle Leistungsphasen in fachlicher Reihenfolge. "W+M" steht bewusst
|
|
// AM ENDE und ist keine HOAI-Phase, sondern eine eigene Sammelkategorie —
|
|
// Stellen, die nur die echten Leistungsphasen brauchen (amtierende Phase,
|
|
// Aufwandsmodell), filtern deshalb gezielt "W+M" heraus statt nach Index zu
|
|
// schneiden.
|
|
const LPHS = ["LPH 1","LPH 2","LPH 3","LPH 4","LPH 5","LPH 6","LPH 7","LPH 8","LPH 9", "W+M"]; // Spalten für Leistungsphasen
|
|
const ROLES = ["Planung","Projektleitung","Bauüberwachung"]; // Gruppierung der Personen
|
|
const GROUP_ORDER = ["Aktive Projekte","Kleine Projekte","In Anbahnung","Allgemein"]; // Reihenfolge der Projektgruppen
|
|
const STORAGE = "projuebersicht_v1"; // Durch lokale UI-State-Persistenz genutzter Key
|
|
// DEFAULT_DEPARTMENT: Fallback-Abteilung beim ersten Start
|
|
const DEFAULT_DEPARTMENT = 'ELT';
|
|
// DEFAULT_TAB: Tab, der beim ersten Start angezeigt wird ('dashboard' = Meine
|
|
// Aufgaben, das persönliche Dashboard — Projektboard bleibt über die
|
|
// Navigation vollständig erreichbar, ist aber nicht mehr die Startansicht)
|
|
const DEFAULT_TAB = 'dashboard';
|
|
|
|
// APP_SETTINGS: die firmenweiten Einstellungen (GET /app-settings). Sie
|
|
// waren bis zum Ausbau der Einstellungsseite Konstanten im Quelltext —
|
|
// Aufwandsvorlage, Ampel-Schwellen, Balkenskala, Arbeitsstunden je Tag — und
|
|
// verschieben allesamt Zahlen, die alle Nutzer sehen. Deshalb sind sie jetzt
|
|
// firmenweit gepflegt (Admin) statt im Code verteilt.
|
|
//
|
|
// Die Werte hier sind die VORGABEN und zugleich die Rückfallebene: Sie
|
|
// entsprechen APP_SETTING_DEFS in server.js und gelten, solange
|
|
// loadAppSettings() (js/store.js) noch nicht geantwortet hat oder der Abruf
|
|
// scheitert. Ohne diese Kopie stünde vor dem ersten Rendern gar kein Wert
|
|
// bereit und die Auslastung rechnete mit undefined.
|
|
let APP_SETTINGS = {
|
|
stundensatz: 135,
|
|
arbeitsstundenProTag: 8,
|
|
vollzeitStundenProWoche: 40,
|
|
auslastungSchwelleGelb: 0.75,
|
|
auslastungSchwelleRot: 1.0,
|
|
// referenzHonorar/verteilzeitraumWochen fehlten hier, obwohl
|
|
// berechneAuslastung() (js/load/load.js) beide liest. Die Aufrufstellen
|
|
// fingen das mit eigenen Rückfallwerten ab (|| 250000, || 12) — die Vorgabe
|
|
// stand damit an drei Stellen, und eine im Admin-Bereich geänderte
|
|
// Server-Vorgabe wäre an den Rückfallwerten vorbeigelaufen. Werte identisch
|
|
// zu APP_SETTING_DEFS (server.js), wie bei allen Schlüsseln hier.
|
|
referenzHonorar: 250000,
|
|
verteilzeitraumWochen: 12,
|
|
aufwandVorlage: {
|
|
'LPH 1': { Planung: 0.4, Projektleitung: 0.2, Bauüberwachung: 0 },
|
|
'LPH 2': { Planung: 0.4, Projektleitung: 0.2, Bauüberwachung: 0 },
|
|
'LPH 3': { Planung: 1.0, Projektleitung: 0.3, Bauüberwachung: 0 },
|
|
'LPH 4': { Planung: 0.0, Projektleitung: 0.3, Bauüberwachung: 0 },
|
|
'LPH 5': { Planung: 1.0, Projektleitung: 0.4, Bauüberwachung: 0 },
|
|
'LPH 6': { Planung: 0.2, Projektleitung: 0.7, Bauüberwachung: 0.3 },
|
|
'LPH 7': { Planung: 0.1, Projektleitung: 0.3, Bauüberwachung: 0.5 },
|
|
'LPH 8': { Planung: 0, Projektleitung: 0.2, Bauüberwachung: 1.0 },
|
|
'LPH 9': { Planung: 0, Projektleitung: 0, Bauüberwachung: 0 }
|
|
},
|
|
autoLockAktiv: 1,
|
|
feiertagBundesland: 'NW',
|
|
maxBackups: 10,
|
|
sessionTage: 30,
|
|
loginMaxVersuche: 10,
|
|
loginSperreMinuten: 10
|
|
};
|
|
|
|
// HONORAR_ANTEILE: firmenweite HOAI-Anteile je Leistungsphase in Prozent
|
|
// ({ 'LPH 1': 2, … }), gefüllt aus GET /honorar-config beim Start
|
|
// (loadAppSettings() in js/store.js). Sie sind die Brücke vom Honorar eines
|
|
// Projekts zum Aufwand einer einzelnen Phase und damit Grundlage der
|
|
// Auslastungsrechnung in Stunden (berechneAuslastung(), js/load/load.js).
|
|
// Gepflegt werden sie im Admin-Bereich unter „Honorar"; der Projektbereich
|
|
// holt sie sich für den Leistungsstand weiterhin selbst (plan-leistung.js),
|
|
// weil er sie auch nach einer Änderung im laufenden Betrieb neu braucht.
|
|
// Leer = nicht geladen: Die Auslastung weist dann eine Lücke aus, statt mit
|
|
// erfundenen Anteilen zu rechnen.
|
|
let HONORAR_ANTEILE = {};
|
|
|
|
// HOLIDAYS: Feiertage und sonstige arbeitsfreie Tage als Menge von
|
|
// ISO-Datumsangaben ("2026-12-25"), gefüllt aus GET /holidays. Wirkt in
|
|
// workdaysBetween()/addWorkdays() (js/utils.js) und damit in der
|
|
// Terminplan-Berechnung aus dem Honorar und in der Ressourcen-Auslastung.
|
|
let HOLIDAYS = new Set();
|
|
|
|
// HOLIDAY_NAMES: Datum -> Bezeichnung, nur für die Pflege-Liste im Bereich
|
|
// „Admin" (die Rechnung braucht allein die Menge oben).
|
|
let HOLIDAY_NAMES = {};
|
|
|
|
// GERMAN_STATES: Kürzel -> Name der Bundesländer, vom Server mitgeliefert
|
|
// (GET /holidays). Er ist die eine Stelle, an der die Liste steht; sie wird
|
|
// nur für die Auswahl beim Berechnen der gesetzlichen Feiertage gebraucht.
|
|
let GERMAN_STATES = {};
|
|
|
|
// Farbpalette für GANTT-Balken (eine pro Projekt, wiederholend)
|
|
const GANTT_COLORS = ['#0071e3','#248a3d','#b25000','#7c3aed','#d70015','#0ca678','#d9480f','#5f3dc4'];
|
|
|
|
// saveSyncTimer: Handle für den debounced Speicher-Timer (Sync auf Server)
|
|
let saveSyncTimer = null;
|
|
|
|
// In-memory Datenbank: Sitzungen (weeks) und aktuell ausgewählte Sitzung.
|
|
// globalPeople ist ein flaches Array (keine Abteilungstrennung mehr).
|
|
let db = { weeks: [], currentWeek: null, globalPeople: [],
|
|
// sandboxWeek: Platzhalter bis zum ersten erfolgreichen load() (js/store.js)
|
|
// befüllt ihn — leer statt undefined, damit ein Zugriff vor dem Laden nicht
|
|
// crasht, sondern nur eine leere Planung-Liste zeigt.
|
|
sandboxWeek: { id: 'sandbox', label: '__sandbox__', date: '', people: [], projects: [], attendance: {}, locked: false, updatedAt: '' }
|
|
};
|
|
|
|
// currentTab: aktuell aktiver Tab der Hauptseite (z. B. 'board')
|
|
let currentTab = DEFAULT_TAB;
|
|
// previousTab: der davor angezeigte Bereich. Gebraucht vom "Schließen"-Knopf
|
|
// des Projektbereichs (closePlanPage() in js/gantt.js), der damit dorthin
|
|
// zurückführt, von wo aus das Projekt geöffnet wurde — früher erledigte das
|
|
// die Browser-Historie, weil der Projektbereich ein eigenes Dokument war.
|
|
let previousTab = DEFAULT_TAB;
|
|
// DEPARTMENTS: Auflistung der gültigen Abteilungen mit Anzeigename und Icon
|
|
// (nur noch für users.department/Benutzerverwaltung relevant, keine
|
|
// App-weite Datentrennung mehr)
|
|
const DEPARTMENTS = {
|
|
ELT: { label: 'Elektro', icon: '' },
|
|
HLS: { label: 'HLS', icon: '' },
|
|
ARCH: { label: 'Architektur', icon: '' }
|
|
};
|
|
|
|
// SANDBOX_WEEK_LABEL: die eine, dauerhaft reservierte "Sitzung", in der alle
|
|
// Planung-Terminpläne liegen (CLAUDE.md, "Planung — projektunabhängige
|
|
// Terminplan-Sandbox"). MUSS mit der gleichnamigen Konstante in server.js
|
|
// übereinstimmen — kein gemeinsames Modulsystem zwischen Server und Client.
|
|
const SANDBOX_WEEK_LABEL = '__sandbox__';
|
|
|
|
// TAB_SLUGS / TAB_BY_SLUG: sprechende URL-Abschnitte für die Tabs, damit jeder
|
|
// Tab eine eigene Seite hat (z. B. "/Auslastung"). Wird von js/ui.js
|
|
// (applyUrlRoute/updateRouteUrl) und dem Server genutzt.
|
|
const TAB_SLUGS = {
|
|
board: 'Projektübersicht',
|
|
wochenbericht: 'Wochenbericht',
|
|
load: 'Auslastung',
|
|
abwesenheit: 'Abwesenheiten',
|
|
people: 'Projektbeteiligte',
|
|
planung: 'Planung',
|
|
planchecker: 'PlanChecker',
|
|
dashboard:'MeineAufgaben',
|
|
wand: 'Wandtafel',
|
|
settings: 'Einstellungen',
|
|
admin: 'Admin'
|
|
};
|
|
const TAB_BY_SLUG = Object.fromEntries(Object.entries(TAB_SLUGS).map(([k,v]) => [v,k]));
|
|
// Der Bereich "Projekte" ist in der Projektliste aufgegangen (siehe
|
|
// renderBoard() in js/board/board.js). Sein Adressabschnitt bleibt gültig und
|
|
// führt dorthin, damit weitergegebene Links nicht ins Leere zeigen — er wird
|
|
// aber nicht mehr erzeugt, weil er in TAB_SLUGS fehlt.
|
|
TAB_BY_SLUG['Projekte'] = 'board';
|
|
|
|
// PLAN_SLUG: URL-Schlüssel des Projektbereichs (Bereich #plan von App.html),
|
|
// im gleichen Deep-Link-Schema wie die Tabs: "/Projektplan/<Projektname>",
|
|
// dazu ?weekLabel=…&projNr=… als Parameter. Steht nicht in TAB_SLUGS, weil
|
|
// die Adresse das geöffnete Projekt mitträgt und deshalb nicht fest ist —
|
|
// gebaut wird sie in updateRouteUrl() (js/ui.js).
|
|
const PLAN_SLUG = 'Projektplan'; |