195 lines
7.6 KiB
JavaScript
195 lines
7.6 KiB
JavaScript
// -----------------------------
|
|
// Projektübersicht — UI-Zustand (LocalStorage, Theme, Header)
|
|
// -----------------------------
|
|
// Diese Datei verwaltet den reinen UI-Zustand: gespeicherte Abteilung in
|
|
// LocalStorage, den kollabierbaren Header sowie das Light/Dark-Theme. Sie wird
|
|
// nach js/globals.js von beiden HTML-Einstiegspunkten geladen (Test.html und
|
|
// project-plan.html). Die bereitgestellten Funktionen werden von js/init.js
|
|
// beim Start sowie von js/tabs-auth.js (Tab-Wechsel, showTab) und js/store.js
|
|
// (Abteilungswechsel, switchDepartment) aufgerufen; im Theme-Umschalter der
|
|
// Seite werden toggleTheme/applyTheme direkt eingebunden.
|
|
|
|
// saveUiState: speichert Abteilung und aktiven Tab in den LocalStorage
|
|
// Parameter: keine. Rückgabe: keine.
|
|
function saveUiState(){
|
|
try {
|
|
const state = { department: currentDepartment, tab: currentTab };
|
|
window.localStorage.setItem(STORAGE, JSON.stringify(state));
|
|
} catch (err) {
|
|
console.warn('UI-State kann nicht gespeichert werden:', err);
|
|
}
|
|
}
|
|
|
|
// loadUiState: stellt die zuletzt gewählte Abteilung aus dem LocalStorage wieder her
|
|
// Parameter: keine. Rückgabe: keine (setzt currentDepartment aus js/globals.js).
|
|
function loadUiState(){
|
|
try {
|
|
const state = JSON.parse(window.localStorage.getItem(STORAGE) || '{}');
|
|
if(state.department && DEPARTMENTS[state.department]) currentDepartment = state.department;
|
|
// Tab wird nicht aus dem localStorage wiederhergestellt, damit nach dem
|
|
// Neuladen immer das Projektboard angezeigt wird.
|
|
} catch (err) {
|
|
console.warn('UI-State kann nicht geladen werden:', err);
|
|
}
|
|
}
|
|
|
|
// ---------- Deep-Link-Routen (Abteilung + Tab als URL) ----------
|
|
|
|
// applyUrlRoute: wertet einen Deep-Link "/<Abteilung>/<Tab>" aus der URL aus,
|
|
// z. B. "/HLS/Auslastung" oder "/ELT/Projektübersicht". Gültige Routen liefern
|
|
// { department, tab }, alles andere (auch file://-Pfade) liefert null.
|
|
function applyUrlRoute(){
|
|
try {
|
|
const m = window.location.pathname.match(/^\/([^/]+)\/([^/]+)\/?$/);
|
|
if(!m) return null;
|
|
const dept = m[1];
|
|
const tab = TAB_BY_SLUG[decodeURIComponent(m[2])];
|
|
if(!DEPARTMENTS[dept] || !tab) return null;
|
|
return { department: dept, tab };
|
|
} catch (err) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// updateRouteUrl: schreibt Abteilung + aktiven Tab als sprechende URL in die
|
|
// Adresszeile (z. B. "/HLS/Auslastung"), ohne die Seite neu zu laden. Nur über
|
|
// HTTP (nicht per file://) und nur bei bekannter Abteilung/Tab. Keine Rückgabe.
|
|
function updateRouteUrl(dept, tab){
|
|
try {
|
|
if(!window.location.protocol || window.location.protocol.indexOf('http') !== 0) return;
|
|
const slug = TAB_SLUGS[tab];
|
|
if(!slug || !DEPARTMENTS[dept]) return;
|
|
window.history.replaceState(null, '', '/' + dept + '/' + slug);
|
|
} catch (err) {}
|
|
}
|
|
|
|
// ---------- Kollabierbarer Header ----------
|
|
|
|
// HEADER_COLLAPSED_KEY: LocalStorage-Key für den Einklapp-Zustand des Header-Menüs
|
|
const HEADER_COLLAPSED_KEY = 'projuebersicht-header-collapsed';
|
|
|
|
// loadHeaderState: liest den gespeicherten Header-Zustand aus dem LocalStorage
|
|
// Parameter: keine. Rückgabe: keine (wendet den Zustand per setHeaderCollapsed an).
|
|
function loadHeaderState(){
|
|
let collapsed = true; // Default: Menü ist beim ersten Start geschlossen
|
|
try {
|
|
const stored = window.localStorage.getItem(HEADER_COLLAPSED_KEY);
|
|
if(stored !== null){
|
|
collapsed = stored === 'true';
|
|
}
|
|
} catch (err) {}
|
|
setHeaderCollapsed(collapsed, false);
|
|
}
|
|
|
|
// saveHeaderState: speichert den Einklapp-Zustand des Headers im LocalStorage
|
|
// Parameter: collapsed (Boolean). Rückgabe: keine.
|
|
function saveHeaderState(collapsed){
|
|
try {
|
|
window.localStorage.setItem(HEADER_COLLAPSED_KEY, collapsed ? 'true' : 'false');
|
|
} catch (err) {}
|
|
}
|
|
|
|
// setHeaderCollapsed: klappt das Header-Menü auf/zu und aktualisiert Icon und aria-Attribute
|
|
// Parameter: collapsed (Boolean), persist (Boolean, ob in LocalStorage gespeichert wird). Rückgabe: keine.
|
|
function setHeaderCollapsed(collapsed, persist){
|
|
const menu = document.getElementById('headerMenu');
|
|
const btn = document.querySelector('.header-bar .header-toggle');
|
|
const icon = document.querySelector('.header-bar .header-toggle-icon');
|
|
if(!menu) return;
|
|
if(collapsed){
|
|
menu.classList.remove('open');
|
|
menu.setAttribute('aria-hidden', 'true');
|
|
if(icon) icon.textContent = '☰';
|
|
if(btn) btn.setAttribute('aria-expanded', 'false');
|
|
} else {
|
|
menu.classList.add('open');
|
|
menu.setAttribute('aria-hidden', 'false');
|
|
if(icon) icon.textContent = '✕';
|
|
if(btn) btn.setAttribute('aria-expanded', 'true');
|
|
}
|
|
if(persist) saveHeaderState(collapsed);
|
|
}
|
|
|
|
// toggleHeader: klappt das Header-Menü zwischen offen und geschlossen um
|
|
// Parameter: keine. Rückgabe: keine (persistiert den neuen Zustand).
|
|
function toggleHeader(){
|
|
const menu = document.getElementById('headerMenu');
|
|
const isOpen = menu && menu.classList.contains('open');
|
|
setHeaderCollapsed(isOpen, true);
|
|
}
|
|
|
|
// ---------- Theme (Light/Dark) ----------
|
|
|
|
// applyThemeIcon: setzt das Sonne-/Mond-Icon passend zum effektiven Theme
|
|
// Parameter: theme (String 'dark'/'light'). Rückgabe: keine.
|
|
function applyThemeIcon(theme){
|
|
const icon = document.getElementById('themeIcon');
|
|
if(!icon) return;
|
|
icon.textContent = theme === 'dark' ? '☀️' : '🌙';
|
|
}
|
|
|
|
// loadTheme: liest das gespeicherte Theme aus dem LocalStorage und wendet es an
|
|
// Parameter: keine. Rückgabe: keine.
|
|
function loadTheme(){
|
|
let theme = 'auto';
|
|
try {
|
|
theme = window.localStorage.getItem('projuebersicht-theme') || 'auto';
|
|
} catch (err) {
|
|
console.warn('Theme kann nicht geladen werden:', err);
|
|
}
|
|
applyTheme(theme, false);
|
|
}
|
|
|
|
// saveTheme: speichert das gewählte Theme im LocalStorage
|
|
// Parameter: theme (String 'auto'/'light'/'dark'). Rückgabe: keine.
|
|
function saveTheme(theme){
|
|
try {
|
|
window.localStorage.setItem('projuebersicht-theme', theme);
|
|
} catch (err) {
|
|
console.warn('Theme kann nicht gespeichert werden:', err);
|
|
}
|
|
}
|
|
|
|
// effectiveTheme: ermittelt das tatsächlich verwendete Theme ('light'/'dark')
|
|
// Parameter: theme (String 'auto'/'light'/'dark'). Rückgabe: 'dark' oder 'light'
|
|
// (bei 'auto' anhand der Systemeinstellung).
|
|
function effectiveTheme(theme){
|
|
if(theme === 'dark') return 'dark';
|
|
if(theme === 'light') return 'light';
|
|
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
}
|
|
|
|
// applyTheme: setzt das data-theme-Attribut am html-Element und aktualisiert das Icon
|
|
// Parameter: theme (String), persist (Boolean, ob in LocalStorage gespeichert wird). Rückgabe: keine.
|
|
function applyTheme(theme, persist){
|
|
const html = document.documentElement;
|
|
const eff = effectiveTheme(theme);
|
|
if(theme === 'dark') html.setAttribute('data-theme', 'dark');
|
|
else if(theme === 'light') html.setAttribute('data-theme', 'light');
|
|
else html.removeAttribute('data-theme');
|
|
applyThemeIcon(eff);
|
|
if(persist) saveTheme(theme);
|
|
}
|
|
|
|
// toggleTheme: wechselt zwischen hellem und dunklem Theme und speichert die Auswahl
|
|
// Parameter: keine. Rückgabe: keine.
|
|
function toggleTheme(){
|
|
const html = document.documentElement;
|
|
const currentAttr = html.getAttribute('data-theme');
|
|
const currentTheme = currentAttr || 'auto';
|
|
const currentEffective = effectiveTheme(currentTheme);
|
|
const next = currentEffective === 'dark' ? 'light' : 'dark';
|
|
applyTheme(next, true);
|
|
}
|
|
|
|
// Beim Wechsel der System-Farbschema-Einstellung wird das Theme im
|
|
// "Auto"-Modus sofort neu angewendet (kein Neuladen nötig).
|
|
if(window.matchMedia){
|
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(){
|
|
let theme = 'auto';
|
|
try {
|
|
theme = window.localStorage.getItem('projuebersicht-theme') || 'auto';
|
|
} catch (err) {}
|
|
applyTheme(theme, false);
|
|
});
|
|
} |