// ----------------------------- // 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, 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','people','planung','dashboard','wand','settings','admin','plan']; function showTab(t){ // Der Bereich „Admin" steht nur Administratoren offen. Ohne diese Weiche // zeigte ein weitergereichter Deep-Link „/Admin" einem Mitglied eine leere // Seite — die Karten darin rendert renderAdminArea() für Nicht-Admins gar // nicht erst. (Ein UI-Gate, mehr nicht: Die Routen dahinter sind // serverseitig mit requireRole('admin') abgesichert.) if(t === 'admin' && !(currentUser && currentUser.role === 'admin')) 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==='people') renderPeople(); if(t==='planung') renderPlanung(); 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'); if(label) label.textContent = name || 'Projektbereich'; item.title = name ? ('Projektbereich: ' + name) : 'Projektbereich'; }