Wandtafel-Dashboard, Dateibereinigung und Auslastungs-Vereinfachung
Neue Wandtafel (Touch-Dashboard für Kiosk-Anzeige): Auslastung/Besprochen je Gewerk, Aufgaben-Kennzahlen, Ereignis-Feed nach Art gruppiert, vollständige Projektliste zum Scrollen, automatischer Rücksprung nach Untätigkeit mit sichtbarem Countdown. Eigene Kiosk-Rolle ohne Fachrolle und ohne Schreibrechte. Auslastung: Möglichkeit zum Wechsel der ausgewerteten Sitzung entfernt. Aufräumen nicht mehr benötigter Dateien, README auf aktuellen Stand gebracht. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bc30f2900b
commit
db63a29365
47 files changed
+16486
-3768
No files matched your search
+92
-59
@@ -1,76 +1,109 @@
|
||||
// -----------------------------
|
||||
// Projektübersicht — Tabs (Navigation) & Passwort-Schutz
|
||||
// Projektübersicht — Tabs (Navigation)
|
||||
// -----------------------------
|
||||
// Modul für die Tab-Navigation und den Passwortschutz der Hauptseite (nur von
|
||||
// Test.html geladen, nach js/showmode.js, vor js/pdf.js). showTab() wechselt
|
||||
// zwischen Board, Auslastung, Projekte und Projektbeteiligte und fordert für
|
||||
// die geschützten Tabs 'projects'/'people' das Passwort an; confirmPassword()
|
||||
// prüft den SHA256-Hash gegen PROTECTED_PASSWORD_HASH (js/globals.js). Die
|
||||
// ausstehende Aktion wird in requestedAction (js/globals.js) gemerkt – davon
|
||||
// profitieren auch die Passwort-Gates in js/weeks.js (Löschen/Entsperren von
|
||||
// Sitzungen). Gerendert werden beim Tab-Wechsel renderBoard()/renderLoad()/
|
||||
// renderProjects()/renderPeople() aus js/board/board.js, js/load/load.js, js/projects/projects.js,
|
||||
// js/people/people.js.
|
||||
// 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'];
|
||||
|
||||
// showTab: wechselt zwischen Ansichten; für sensible Tabs wird ein Passwort verlangt
|
||||
function showTab(t, skipAuth = false){
|
||||
// Für die Tabs 'projects' und 'people' ist ein Passwortschutz vorgesehen
|
||||
if((t==='projects' || t==='people') && !skipAuth && !isAuthenticated){
|
||||
requestedAction = { type: 'tab', tab: t };
|
||||
document.getElementById('passwordModal').style.display='flex';
|
||||
document.getElementById('passwordInput').value='';
|
||||
document.getElementById('passwordInput').focus();
|
||||
return;
|
||||
}
|
||||
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(currentDepartment, t);
|
||||
updateRouteUrl(t);
|
||||
|
||||
['board','projects','load','people'].forEach(x=>{
|
||||
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==='projects') renderProjects();
|
||||
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();
|
||||
}
|
||||
|
||||
// ---------- Passwort-Schutz Implementierung ----------
|
||||
|
||||
// confirmPassword: prüft das eingegebene Passwort gegen den gespeicherten Hash
|
||||
function confirmPassword(){
|
||||
const pwd = document.getElementById('passwordInput').value;
|
||||
if(!pwd) { alert("Bitte Passwort eingeben!"); return; }
|
||||
|
||||
const pwdHash = CryptoJS.SHA256(pwd).toString();
|
||||
|
||||
if(pwdHash === PROTECTED_PASSWORD_HASH){
|
||||
// Sitzungs-Authentifizierung merken, damit Tab-Wechsel und Abteilungs-
|
||||
// wechsel innerhalb dieser Sitzung kein erneutes Passwort verlangen.
|
||||
isAuthenticated = true;
|
||||
// Nur die zuletzt angeforderte Aktion ausführen (nicht eine evtl. bereits
|
||||
// durch closePasswordModal verworfenen Aktion erneut starten) – daher die
|
||||
// Aktion hier erneut über requestedAction prüfen statt sie festzuhalten.
|
||||
setTimeout(() => {
|
||||
const action = requestedAction;
|
||||
requestedAction = null;
|
||||
if(action?.type==='tab') showTab(action.tab, true);
|
||||
else if(action?.type==='deleteWeek') deleteWeek(true);
|
||||
else if(action?.type==='unlockWeek') unlockCurrentWeek(true);
|
||||
else if(action?.type==='importDb') importDb(true);
|
||||
}, 100);
|
||||
document.getElementById('passwordModal').style.display='none';
|
||||
} else {
|
||||
alert("Falsches Passwort!");
|
||||
document.getElementById('passwordInput').value='';
|
||||
document.getElementById('passwordInput').focus();
|
||||
// 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');
|
||||
}
|
||||
}
|
||||
// closePasswordModal: schließt das Passwort-Modal ohne Ausführung und
|
||||
// verwirft die gemerkte Aktion (requestedAction = null).
|
||||
function closePasswordModal(){
|
||||
document.getElementById('passwordModal').style.display='none';
|
||||
requestedAction = null;
|
||||
}
|
||||
|
||||
// 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';
|
||||
}
|
||||
Reference in new issue
Block a user