// ----------------------------- // Projektübersicht — Tabs (Navigation) // ----------------------------- // Modul für die Tab-Navigation der Hauptseite (nur von App.html geladen, nach // js/wand/wand.js, vor js/pdf.js). showTab() wechselt zwischen Board, // Wochenbericht, Auslastung, Projekte, Projektbeteiligte, Planung, Plan // Checker, Meine Aufgaben, Wandtafel, Einstellungen, Admin und dem // Projektbereich eines einzelnen Projekts ('plan', siehe TAB_PANELS). // Einstellungen und Admin sind keine eigenen Tabs mehr, sondern aufklappbare // Bereiche direkt in der Seitenleiste (siehe toggleSidebarAccordion() in // js/ui.js). Gerendert werden beim Tab-Wechsel renderBoard()/ // renderWochenbericht()/renderLoad()/renderProjects()/renderPeople()/ // renderDashboard()/renderWandboard() aus js/board/board.js, // js/wochenbericht/wochenbericht.js, js/load/load.js, js/projects/projects.js, // js/people/people.js, js/dashboard/dashboard.js, js/wand/wand.js. Der // frühere clientseitige Passwortschutz für die Tabs 'projects'/'people' wurde // entfernt — der Zugriffsschutz läuft jetzt serverseitig über die Session // (siehe js/auth.js, server.js). // // TAB_PANELS: alle Bereiche der Hauptseite. 'plan' ist der Projektbereich // EINES Projekts (#plan) — früher die eigenständige Seite project-plan.html, // die in einem eigenen Dokument geöffnet wurde. Seit dem Umbau ist er ein // Bereich wie jeder andere: Menüband und Seitenleiste bleiben stehen, der // Wechsel dorthin läuft über denselben showTab()-Weg wie der Wechsel vom // Dashboard ins Projektboard. const TAB_PANELS = ['board','wochenbericht','load','abwesenheit','notizen','people','planung','planchecker','dashboard','wand','settings','admin','plan']; function showTab(t){ // Weichen nach Rollen & Rechte (UI-Gate, die Routen dahinter prüft der // Server mit requireRecht()): „Admin“ nur mit einem Admin-Recht, die // Wandtafel nur mit „Wandtafel anzeigen“, und eine Rolle ohne „Anwendung // nutzen“ (früher das Kiosk-Konto) landet immer auf der Wandtafel. if(currentUser && !hatRecht('app') && hatRecht('wand')) t = 'wand'; if(t === 'admin' && !hatAdminRecht()) t = DEFAULT_TAB; if(t === 'abwesenheit' && currentUser && !darfIrgendeineAbwesenheit()) t = DEFAULT_TAB; if(t === 'wand' && currentUser && !hatRecht('wand')) t = DEFAULT_TAB; if(t !== currentTab) previousTab = currentTab; currentTab = t; saveUiState(); updateRouteUrl(t); TAB_PANELS.forEach(x=>{ const el = document.getElementById(x); if(el) el.style.display=(x===t?'block':'none'); }); // Der Projektbereich steht als einziger Bereich AUSSERHALB von .container // (er bringt eigene Kopfzeile und Innenabstände mit). Der leere Container // hinterließe sonst seinen Innenabstand als blinden Streifen über der // Kopfzeile des Projektbereichs. const containerEl = document.querySelector('.app-main > .container'); if(containerEl) containerEl.style.display = (t === 'plan') ? 'none' : ''; // Wandtafel: Seitenleiste und Kopfzeile ausblenden (body.wandtafel-aktiv, // styles.css) — das Kiosk-Konto hat echte Schreibrechte, eine zufällige // Berührung der Navigation soll nicht in einen bearbeitbaren Bereich // führen. startWandboard()/stopWandboard() (js/wand/wand.js) starten bzw. // beenden dabei die Hintergrund-Aktualisierung und die Kachel-Rotation. document.body.classList.toggle('wandtafel-aktiv', t === 'wand'); if(t === 'wand') startWandboard(); else stopWandboard(); document.querySelectorAll('.tab').forEach(el=>el.classList.toggle('active',el.dataset.tab===t)); if(t==='board' && currentWeek()) renderBoard(); if(t==='wochenbericht') renderWochenbericht(); if(t==='load') renderLoad(); if(t==='abwesenheit'){ abwBeimOeffnen(); renderAbwesenheiten(); } if(t==='notizen' && typeof renderMeineNotizen === 'function') renderMeineNotizen(); if(t==='people') renderPeople(); if(t==='planung') renderPlanung(); if(t==='planchecker') renderPlanChecker(); if(t==='dashboard') renderDashboard(); if(t==='wand') renderWandboard(); if(t==='settings') renderSettings(); if(t==='admin') renderAdminArea(); syncAdminNavState(t); // Der Projektbereich wird erst NACH dem Einblenden gerendert: das GANTT // rechnet mit der Containerbreite, die in einem ausgeblendeten Bereich 0 // wäre. Das Ziel (Sitzung + Projekt) steht vorher über setPlanTarget(). if(t==='plan') openPlanView(); syncPlanNavItem(); } // syncAdminNavState: „Admin" ist in der Seitenleiste kein .tab-Knopf, // sondern der Kopf einer aufklappbaren Gruppe — die allgemeine // .tab-Hervorhebung oben greift dort also nicht. Diese Funktion markiert ihn // stattdessen, solange der Bereich offen ist, und klappt die Gruppe dabei // auf, damit der gewählte Abschnitt sichtbar wird. // Parameter: t (aktiver Bereich). Keine Rückgabe. function syncAdminNavState(t){ const toggle = document.getElementById('adminAccordionToggle'); const subnav = document.getElementById('adminSubnav'); if(!toggle || !subnav) return; const aktiv = (t === 'admin'); toggle.classList.toggle('active', aktiv); if(aktiv && !subnav.classList.contains('open')){ subnav.classList.add('open'); toggle.classList.add('expanded'); toggle.setAttribute('aria-expanded', 'true'); } } // syncPlanNavItem: hält den Eintrag des Projektbereichs in der Seitenleiste // aktuell. Er ist der einzige Navigationspunkt, der nicht immer da ist — er // erscheint erst, wenn ein Projekt geöffnet wurde, und trägt dann dessen // Bezeichnung. Damit zeigt die Seitenleiste auch im Projektbereich, wo man // gerade ist, statt (wie es ohne ihn wäre) gar keinen Punkt hervorzuheben. // Keine Parameter, keine Rückgabe. function syncPlanNavItem(){ const item = document.getElementById('planNavItem'); if(!item) return; const app = window.PlanApp; const name = app && app.projName ? app.projName : ''; item.style.display = name ? '' : 'none'; const label = document.getElementById('planNavItemLabel'); // Projektnummer in Mono-Ziffern davor (Kennzeichen „Bauplan“). if(label) label.innerHTML = name ? (app.projNr ? '' + esc(app.projNr) + ' ' : '') + esc(name) : 'Projektbereich'; item.title = name ? ('Projektbereich: ' + name) : 'Projektbereich'; }