Plan Checker (neues Studenten-Werkzeug, Seitenleiste + Admin → Plan-Checker- Merkmale): lädt einen Bauplan als PDF hoch und lässt ChatGPT oder Claude ihn gegen eine gepflegte Merkmalsliste prüfen. PDF-Rendering (pdf.js + @napi-rs/canvas) berücksichtigt jetzt Ebenen (Optional Content Groups) und WASM-Decoder korrekt, sendet eine Übersicht plus mehrere hochaufgelöste Kachel-Ausschnitte statt eines einzelnen, zu groben Bilds, und beide Anbieter bekommen dasselbe Bildmaterial. Jedes Prüfergebnis kann auf eines der gesendeten Bilder als Beleg verweisen (bildIndex) — als Screenshot im Ergebnis anklickbar, öffnet vergrößert in einem Modal statt eines von Chrome blockierten target=_blank-Links auf eine data:-URL. KI-Modellname ist jetzt pro Konto überschreibbar (Einstellungen → Mein Konto) für Drittanbieter mit abweichendem Modell-Katalog. Wochenbericht: dichte Kartenansicht bekommt einen Fokus-Modus — nur der Kommentar liegt offen, Leistungsphasen und Beteiligte stecken in aufklappbaren Abschnitten statt dauerhaft nebeneinander zu stehen. Projektbereich: Terminplan-Auslastung rechnet jetzt exakt wie der Auslastungs-Tab/die Wandtafel (statt einer unabhängigen, tageweisen Schätzung); die LPH-Fortschrittsliste im Dashboard zeigt nur noch die beauftragten Leistungsphasen. Wandtafel: Dashboard-Kopf mit Kennzahlen-Reihe und Panel-Reihe neu strukturiert, in den Blautönen des Firmenlogos; dieselbe Palette wurde anschließend auf den gesamten App-weiten Dark Mode übernommen. Dateibereinigung: nicht mehr benötigte Test-Uploads/Backups entfernt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
111 lines
5.7 KiB
JavaScript
111 lines
5.7 KiB
JavaScript
// -----------------------------
|
|
// 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','people','planung','planchecker','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==='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');
|
|
if(label) label.textContent = name || 'Projektbereich';
|
|
item.title = name ? ('Projektbereich: ' + name) : 'Projektbereich';
|
|
}
|