🚿

Spülmanager

Spülaufträge verwalten · Entnahmestellen dokumentieren · QR-basierte Durchführung

📋 Letzte Spülaufträge
Noch keine Spülaufträge.
🕐 Letzte Protokolleinträge
Noch keine Einträge.
ID Name Ort Typ Spülzeit Letzte Spülung Status Aktionen
Noch keine Entnahmestellen. Klicke «+ Entnahmestelle» um zu beginnen.
Datum / Zeit Armatur Ort Dauer Visum Rolle Bemerkungen Status
Noch keine Protokolleinträge.
📋 Spülkonzept
Standard: 72h (SVGW W10009)
Pro Entnahmestelle anpassbar
📧 Benachrichtigungen
🏢 Projektinformationen
Aktionen
G
GEMA Spülmanager
Trinkwasserhygiene
🚿
Wird geladen…
Armatur öffnen, dann Taste drücken
⬤ Spülung läuft…
2:00
Verbleibend
Armatur geöffnet halten bis Timer abläuft
Spülung abgeschlossen!
Armatur schliessen, dann Formular ausfüllen.
📝 Dokumentation
🎉
Gespeichert!
Die Spülung wurde protokolliert.
`); win.document.close(); win.onload = () => { win.focus(); win.print(); }; } window.printLabel = printLabel; // ═══════════════════════════════════════════════════════ // SPÜLAUFTRÄGE // ═══════════════════════════════════════════════════════ function openAuftragModal() { const armaturen = getArmaturen(); const listDiv = document.getElementById('auftragArmaturList'); if (!armaturen.length) { listDiv.innerHTML = '
Zuerst Entnahmestellen hinzufügen.
'; } else { listDiv.innerHTML = armaturen.map(a => { const loc = [a.gebaeude, a.stockwerk, a.raum].filter(Boolean).join(' · '); const isFaellig = a.status === 'faellig'; return ``; }).join(''); } openModal('auftragModal'); generateNotifPreview(); } window.openAuftragModal = openAuftragModal; function selectAllArmaturen() { document.querySelectorAll('.auftrag-arm-cb').forEach(cb => cb.checked = true); } window.selectAllArmaturen = selectAllArmaturen; function selectNoArmaturen() { document.querySelectorAll('.auftrag-arm-cb').forEach(cb => cb.checked = false); } window.selectNoArmaturen = selectNoArmaturen; function selectFaelligArmaturen() { document.querySelectorAll('.auftrag-arm-cb').forEach(cb => { const id = cb.value; const a = getArmaturen().find(x => x.id === id); cb.checked = a && a.status === 'faellig'; }); } window.selectFaelligArmaturen = selectFaelligArmaturen; function saveAuftrag() { const checked = [...document.querySelectorAll('.auftrag-arm-cb:checked')].map(cb => cb.value); if (!checked.length) { alert('Bitte mindestens eine Armatur auswählen.'); return; } const auftrag = { id: 'SPA_' + Date.now(), erstellt: new Date().toISOString(), ausloeser: document.getElementById('auftragAusloeser').value, verantwortlich: document.getElementById('auftragVerantwortlich')?.value?.trim() || '', prio: document.getElementById('auftragPrio').value, armaturIds: checked, erledigte: [], status: 'offen', bemerkungen: document.getElementById('auftragBemerkungen').value.trim() }; const auftraege = getAuftraege(); auftraege.unshift(auftrag); dbSet('auftraege', auftraege); dbSet('timer72', Date.now()); closeModal('auftragModal'); renderRecentAuftraege(); renderStats(); toast('✓ Spülauftrag erstellt · 72h Timer zurückgesetzt'); } window.saveAuftrag = saveAuftrag; function renderRecentAuftraege() { const auftraege = getAuftraege().slice(0, 5); const div = document.getElementById('recentAuftraege'); if (!auftraege.length) { div.innerHTML = '
Noch keine Spülaufträge.
'; return; } div.innerHTML = '
' + auftraege.map(a => { const armaturen = getArmaturen(); const armNames = a.armaturIds.map(id => { const ar = armaturen.find(x => x.id === id); return ar ? ar.name : id; }); const statusClass = {offen:'blue',aktiv:'amber',abgeschlossen:'green'}[a.status] || 'blue'; const prioMap = {normal:'',hoch:'🔶 ',dringend:'🔴 '}; return `
📋
${prioMap[a.prio] || ''}${esc(a.ausloeser)} – ${a.armaturIds.length} Armatur${a.armaturIds.length !== 1 ? 'en' : ''}
${armNames.slice(0,3).map(n => esc(n)).join(', ')}${armNames.length > 3 ? ' …' : ''}${a.verantwortlich ? ' · 👷 '+esc(a.verantwortlich) : ''}
${formatDate(new Date(a.erstellt))}
`; }).join('') + '
'; } function toggleNotifPreview() { const div = document.getElementById('notifPreviewContent'); div.style.display = div.style.display === 'none' ? 'block' : 'none'; if (div.style.display === 'block') generateNotifPreview(); } window.toggleNotifPreview = toggleNotifPreview; function generateNotifPreview() { const s = getSettings(); const ausloeser = document.getElementById('auftragAusloeser')?.value || 'Manuell'; const text = `Von: GEMA Spülmanager An: ${s.notifRollen.join(', ')} Betreff: [GEMA] Spülauftrag erstellt – ${s.objekt || 'Liegenschaft'} Guten Tag Es wurde ein neuer Spülauftrag erstellt. Objekt: ${s.objekt || '—'} Auslöser: ${ausloeser} Verantwortlich: ${s.verantwortlich || '—'} Bitte führen Sie die Spülungen gemäss Spülkonzept durch und dokumentieren Sie diese im GEMA Spülmanager. Freundliche Grüsse GEMA connect GmbH ${s.kontakt || 'info@gema-connect.ch'}`; const el = document.getElementById('notifPreviewText'); if (el) el.textContent = text; } function showNotifPreview() { const s = getSettings(); const text = `Von: GEMA Spülmanager An: ${s.notifRollen.join(', ')} Betreff: [GEMA] Spülauftrag erstellt – ${s.objekt || 'Liegenschaft'} Guten Tag Es wurde ein neuer Spülauftrag erstellt. Objekt: ${s.objekt || '—'} Verantwortlich: ${s.verantwortlich || '—'} Bitte führen Sie die Spülungen gemäss Spülkonzept durch und dokumentieren Sie diese im GEMA Spülmanager. Freundliche Grüsse GEMA connect GmbH ${s.kontakt || 'info@gema-connect.ch'}`; const wrap = document.getElementById('settingsNotifPreview'); const el = document.getElementById('settingsNotifText'); if (wrap && el) { el.textContent = text; wrap.style.display = wrap.style.display === 'none' ? 'block' : 'none'; } } window.showNotifPreview = showNotifPreview; // ═══════════════════════════════════════════════════════ // DESKTOP FLUSH // ═══════════════════════════════════════════════════════ function startDesktopFlush(id) { const a = getArmaturen().find(x => x.id === id); if (!a) return; currentDfArmatur = a; document.getElementById('dfArmaturName').textContent = a.name; document.getElementById('dfArmaturLoc').textContent = [a.gebaeude, a.stockwerk, a.raum].filter(Boolean).join(' · '); document.getElementById('dfStateReady').style.display = ''; document.getElementById('dfStateRunning').style.display = 'none'; document.getElementById('dfStateDone').style.display = 'none'; document.getElementById('dfSaveBtn').style.display = 'none'; document.getElementById('dfVisum').value = ''; document.getElementById('dfBemerkungen').value = ''; document.getElementById('dfAbweichung').checked = false; document.getElementById('dfAbweichungGroup').style.display = 'none'; openModal('desktopFlushModal'); } window.startDesktopFlush = startDesktopFlush; let dfStartTime = null; let dfDuration = 0; function dfStartFlush() { if (!currentDfArmatur) return; dfDuration = currentDfArmatur.spuelzeit; dfStartTime = Date.now(); document.getElementById('dfStateReady').style.display = 'none'; document.getElementById('dfStateRunning').style.display = ''; const bar = document.getElementById('dfProgressBar'); bar.style.width = '100%'; if (dfTimerInterval) clearInterval(dfTimerInterval); dfTimerInterval = setInterval(() => { const elapsed = (Date.now() - dfStartTime) / 1000; const remaining = Math.max(0, dfDuration - elapsed); const pct = (remaining / dfDuration) * 100; document.getElementById('dfTimerDisplay').textContent = formatMMSS(remaining); bar.style.width = pct + '%'; if (remaining <= 0) { clearInterval(dfTimerInterval); dfFlushDone(); } }, 250); // Also add to active sessions addActiveSession(currentDfArmatur.id, dfDuration); } window.dfStartFlush = dfStartFlush; function dfFlushDone() { document.getElementById('dfStateRunning').style.display = 'none'; document.getElementById('dfStateDone').style.display = ''; document.getElementById('dfSaveBtn').style.display = ''; } function dfAbortFlush() { if (dfTimerInterval) clearInterval(dfTimerInterval); document.getElementById('dfStateRunning').style.display = 'none'; document.getElementById('dfStateReady').style.display = ''; removeActiveSession(currentDfArmatur?.id); closeModal('desktopFlushModal'); } window.dfAbortFlush = dfAbortFlush; function closeDesktopFlush() { if (dfTimerInterval) clearInterval(dfTimerInterval); removeActiveSession(currentDfArmatur?.id); closeModal('desktopFlushModal'); } window.closeDesktopFlush = closeDesktopFlush; function dfSaveFlush() { const visum = document.getElementById('dfVisum').value.trim(); if (!visum) { document.getElementById('dfVisum').focus(); alert('Bitte Visum eintragen.'); return; } const a = currentDfArmatur; const actualDur = Math.round((Date.now() - dfStartTime) / 1000); saveProtokollEntry({ armaturId: a.id, armaturName: a.name, armaturOrt: [a.gebaeude, a.stockwerk, a.raum].filter(Boolean).join(' · '), dauerSek: actualDur || dfDuration, visum, rolle: document.getElementById('dfRolle').value, bemerkungen: document.getElementById('dfBemerkungen').value.trim(), abweichung: document.getElementById('dfAbweichung').checked, abweichungText: document.getElementById('dfAbweichungText')?.value.trim() || '' }); removeActiveSession(a.id); closeModal('desktopFlushModal'); renderArmaturen(); renderStats(); renderProtokoll(); renderRecentProto(); checkFaelligBadge(); toast('✓ Spülung protokolliert'); } window.dfSaveFlush = dfSaveFlush; function toggleDfAbweichung() { document.getElementById('dfAbweichungGroup').style.display = document.getElementById('dfAbweichung').checked ? '' : 'none'; } // ═══════════════════════════════════════════════════════ // ACTIVE SESSIONS (Parallel flush display) // ═══════════════════════════════════════════════════════ function addActiveSession(armaturId, durationSek) { activeSessions[armaturId] = { startTime: Date.now(), duration: durationSek, armatur: getArmaturen().find(a => a.id === armaturId) }; startSessionTimer(armaturId); updateActiveFlushesUI(); } function removeActiveSession(armaturId) { if (!armaturId) return; if (activeSessions[armaturId]?.interval) clearInterval(activeSessions[armaturId].interval); delete activeSessions[armaturId]; updateActiveFlushesUI(); renderArmaturen(); } window.removeActiveSession = removeActiveSession; function startSessionTimer(armaturId) { const sess = activeSessions[armaturId]; if (!sess) return; sess.interval = setInterval(() => { const elapsed = (Date.now() - sess.startTime) / 1000; const remaining = Math.max(0, sess.duration - elapsed); updateFlushCard(armaturId, remaining, sess.duration); if (remaining <= 0) { clearInterval(sess.interval); // Auto-mark done on card const card = document.getElementById('fcard_' + armaturId); if (card) card.querySelector('.flush-timer-countdown').textContent = '✓ Fertig'; } }, 500); updateActiveFlushesUI(); } function updateActiveFlushesUI() { const sessions = Object.keys(activeSessions); const sec = document.getElementById('activeFlushesSec'); const grid = document.getElementById('flushCardsGrid'); const countEl = document.getElementById('activeFlushCount'); if (!sessions.length) { sec.style.display = 'none'; return; } sec.style.display = ''; countEl.textContent = sessions.length + ' aktiv'; grid.innerHTML = sessions.map(id => { const sess = activeSessions[id]; const a = sess.armatur; if (!a) return ''; const loc = [a.gebaeude, a.stockwerk, a.raum].filter(Boolean).join(' · '); const circ = 2 * Math.PI * 36; return `
🚿 ${esc(a.name)}
${esc(loc)}
⬤ Läuft
Verbleibend
–:––
`; }).join(''); } function updateFlushCard(armaturId, remaining, duration) { const timerEl = document.getElementById('ftimer_' + armaturId); const arcEl = document.getElementById('farc_' + armaturId); if (!timerEl) return; timerEl.textContent = formatMMSS(remaining); timerEl.className = 'flush-timer-countdown' + (remaining < 30 ? ' urgent' : ''); if (arcEl) { const circ = 2 * Math.PI * 36; const offset = circ * (1 - remaining / duration); arcEl.style.strokeDashoffset = offset; arcEl.style.stroke = remaining < 30 ? '#d97706' : '#16a34a'; } } // ═══════════════════════════════════════════════════════ // PROTOKOLL // ═══════════════════════════════════════════════════════ function saveProtokollEntry(data) { const entry = Object.assign({ id: 'PROT_' + Date.now(), datum: new Date().toISOString() }, data); const proto = getProtokoll(); proto.unshift(entry); dbSet('protokoll', proto); // Update armatur lastSpuelung const armaturen = getArmaturen(); const idx = armaturen.findIndex(a => a.id === entry.armaturId); if (idx >= 0) { armaturen[idx].letzteSpuelung = entry.datum; if (armaturen[idx].status === 'faellig') armaturen[idx].status = 'ok'; } dbSet('armaturen', armaturen); } function renderProtokoll() { const proto = getProtokoll(); const search = (document.getElementById('protoSearch')?.value || '').toLowerCase(); const rolleFilter = document.getElementById('protoRolleFilter')?.value || ''; const abwFilter = document.getElementById('protoAbwFilter')?.value || ''; let filtered = proto; if (search) filtered = filtered.filter(p => (p.armaturName + p.visum + p.armaturOrt + p.bemerkungen).toLowerCase().includes(search)); if (rolleFilter) filtered = filtered.filter(p => p.rolle === rolleFilter); if (abwFilter === '1') filtered = filtered.filter(p => p.abweichung); if (abwFilter === '0') filtered = filtered.filter(p => !p.abweichung); const tbody = document.getElementById('protokollTableBody'); if (!filtered.length) { tbody.innerHTML = '
Keine Einträge gefunden.
'; return; } tbody.innerHTML = filtered.map(p => { const abw = p.abweichung ? `⚠ Abweichung` : '✓ OK'; return `
${formatDate(new Date(p.datum))}
${formatTime(new Date(p.datum))}
${esc(p.armaturName)}
${esc(p.armaturOrt || '')}
${p.dauerSek}s ${esc(p.visum)} ${esc(p.rolle)}
${p.bemerkungen ? esc(p.bemerkungen.substring(0,60)) + (p.bemerkungen.length > 60 ? '…' : '') : '—'}
${abw} `; }).join(''); } function renderRecentProto() { const proto = getProtokoll().slice(0, 6); const div = document.getElementById('recentProto'); if (!proto.length) { div.innerHTML = '
Noch keine Protokolleinträge.
'; return; } div.innerHTML = '
' + proto.map(p => `
${p.abweichung ? '⚠' : '✓'}
${esc(p.armaturName)}
${esc(p.visum)} · ${esc(p.rolle)} · ${p.dauerSek}s
${formatDate(new Date(p.datum))}
`).join('') + '
'; } function exportCSV() { const proto = getProtokoll(); if (!proto.length) { toast('⚠ Keine Daten zum Exportieren'); return; } const headers = ['Datum','Zeit','Armatur','Ort','Dauer (s)','Visum','Rolle','Bemerkungen','Abweichung','Abweichung Detail']; const rows = proto.map(p => [ formatDate(new Date(p.datum)), formatTime(new Date(p.datum)), p.armaturName, p.armaturOrt || '', p.dauerSek, p.visum, p.rolle, p.bemerkungen || '', p.abweichung ? 'Ja' : 'Nein', p.abweichungText || '' ].map(v => '"' + String(v).replace(/"/g,'""') + '"')); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'GEMA_Spuelprotokoll_' + formatDateISO(new Date()) + '.csv'; a.click(); } window.exportCSV = exportCSV; // ═══════════════════════════════════════════════════════ // MOBILE FLUSH VIEW (QR Mode) // ═══════════════════════════════════════════════════════ function initMobileFlushView(armaturId) { document.getElementById('desktopNav').style.display = 'none'; document.getElementById('mainApp').style.display = 'none'; document.getElementById('mobileFlushView').classList.add('show'); const armaturen = dbGet('armaturen') || []; mfvArmatur = armaturen.find(a => a.id === armaturId); if (!mfvArmatur) { document.getElementById('mfvArmaturName').textContent = 'Armatur nicht gefunden'; document.getElementById('mfvArmaturLoc').textContent = 'ID: ' + armaturId; return; } document.getElementById('mfvArmaturName').textContent = mfvArmatur.name; const loc = [mfvArmatur.gebaeude, mfvArmatur.stockwerk, mfvArmatur.raum].filter(Boolean).join(' · '); document.getElementById('mfvArmaturLoc').textContent = loc; const typColors = { KW:'background:#eff4ff;color:#2563eb;', WW:'background:#fef2f2;color:#dc2626;', Dusche:'background:#f0fdf4;color:#16a34a;' }; const typEl = document.getElementById('mfvArmaturTyp'); typEl.innerHTML = `${mfvArmatur.typ}`; mfvSetState('ready'); } function mfvSetState(state) { ['ready','running','done','saved'].forEach(s => { const el = document.getElementById('mfvState' + s.charAt(0).toUpperCase() + s.slice(1)); if (el) el.classList.toggle('active', s === state); }); } function mfvStartFlush() { if (!mfvArmatur) return; const duration = mfvArmatur.spuelzeit || 120; mfvStartTime = Date.now(); mfvActualDuration = duration; mfvSetState('running'); const arc = document.getElementById('mfvArc'); const circumference = 2 * Math.PI * 96; // r=96 arc.style.strokeDasharray = circumference; if (mfvTimer) clearInterval(mfvTimer); mfvTimer = setInterval(() => { const elapsed = (Date.now() - mfvStartTime) / 1000; const remaining = Math.max(0, duration - elapsed); const pct = remaining / duration; document.getElementById('mfvTimerDisplay').textContent = formatMMSS(remaining); arc.style.strokeDashoffset = circumference * (1 - pct); arc.style.stroke = remaining < 30 ? '#d97706' : '#16a34a'; if (remaining <= 0) { clearInterval(mfvTimer); mfvSetState('done'); } }, 250); } window.mfvStartFlush = mfvStartFlush; function mfvAbortFlush() { if (mfvTimer) clearInterval(mfvTimer); mfvSetState('ready'); } window.mfvAbortFlush = mfvAbortFlush; function toggleAbweichung() { document.getElementById('mfvAbweichungGroup').style.display = document.getElementById('mfvAbweichung').checked ? '' : 'none'; } function mfvSubmitFlush() { const visum = document.getElementById('mfvVisum').value.trim(); if (!visum) { document.getElementById('mfvVisum').focus(); return; } const actualDur = Math.round((Date.now() - mfvStartTime) / 1000); const a = mfvArmatur; const entry = { id: 'PROT_' + Date.now(), datum: new Date().toISOString(), armaturId: a.id, armaturName: a.name, armaturOrt: [a.gebaeude, a.stockwerk, a.raum].filter(Boolean).join(' · '), dauerSek: actualDur, visum, rolle: document.getElementById('mfvRolle').value, bemerkungen: document.getElementById('mfvBemerkungen').value.trim(), abweichung: document.getElementById('mfvAbweichung').checked, abweichungText: document.getElementById('mfvAbweichungText').value.trim() }; // Save to localStorage const proto = (function(){ try{ return JSON.parse((typeof _GemaDB!=='undefined'?(_GemaDB.c[DB_KEYS.protokoll]??null):null)||'[]'); }catch{ return []; } })(); proto.unshift(entry); if(typeof _GemaDB!=='undefined') _GemaDB.save(DB_KEYS.protokoll, JSON.stringify(proto)); // Update armatur const armaturen = (function(){ try{ return JSON.parse((typeof _GemaDB!=='undefined'?(_GemaDB.c[DB_KEYS.armaturen]??null):null)||'[]'); }catch{ return []; } })(); const idx = armaturen.findIndex(x => x.id === a.id); if (idx >= 0) { armaturen[idx].letzteSpuelung = entry.datum; if (armaturen[idx].status === 'faellig') armaturen[idx].status = 'ok'; } if(typeof _GemaDB!=='undefined') _GemaDB.save(DB_KEYS.armaturen, JSON.stringify(armaturen)); mfvSetState('saved'); } window.mfvSubmitFlush = mfvSubmitFlush; function mfvReset() { document.getElementById('mfvVisum').value = ''; document.getElementById('mfvBemerkungen').value = ''; document.getElementById('mfvAbweichung').checked = false; document.getElementById('mfvAbweichungGroup').style.display = 'none'; mfvSetState('ready'); } window.mfvReset = mfvReset; // ═══════════════════════════════════════════════════════ // MODAL HELPERS // ═══════════════════════════════════════════════════════ function openModal(id) { document.getElementById(id).classList.add('open'); } function closeModal(id) { document.getElementById(id).classList.remove('open'); } window.closeModal = closeModal; document.querySelectorAll('.modal-overlay').forEach(m => { m.addEventListener('click', e => { if (e.target === m) m.classList.remove('open'); }); }); // ═══════════════════════════════════════════════════════ // DATA MANAGEMENT // ═══════════════════════════════════════════════════════ function exportAllData() { const data = { armaturen: getArmaturen(), protokoll: getProtokoll(), auftraege: getAuftraege(), settings: getSettings(), exportedAt: new Date().toISOString() }; const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'GEMA_Spuelmanager_' + formatDateISO(new Date()) + '.json'; a.click(); } window.exportAllData = exportAllData; function confirmClearData() { if (!confirm('ACHTUNG: Alle Daten (Entnahmestellen, Protokoll, Aufträge) werden unwiderruflich gelöscht. Fortfahren?')) return; if(typeof _GemaDB!=='undefined') Object.values(DB_KEYS).forEach(k => _GemaDB.remove(k)); renderAll(); loadSettings(); toast('🗑 Alle Daten gelöscht'); } window.confirmClearData = confirmClearData; // ═══════════════════════════════════════════════════════ // UTILITY // ═══════════════════════════════════════════════════════ function esc(s) { return String(s||'').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); } function formatDate(d) { return d.toLocaleDateString('de-CH',{day:'2-digit',month:'2-digit',year:'numeric'}); } function formatTime(d) { return d.toLocaleTimeString('de-CH',{hour:'2-digit',minute:'2-digit'}); } function formatDateTime(d) { return formatDate(d) + ' ' + formatTime(d); } function formatDateISO(d) { return d.toISOString().split('T')[0]; } function formatMMSS(sek) { const s = Math.max(0,Math.ceil(sek)); return Math.floor(s/60) + ':' + String(s%60).padStart(2,'0'); } function timeAgo(iso) { const diff = Date.now() - new Date(iso).getTime(); const h = Math.floor(diff / 3600000); if (h < 1) return 'vor wenigen Minuten'; if (h < 24) return 'vor ' + h + ' Std.'; const d = Math.floor(h / 24); return 'vor ' + d + ' Tag' + (d !== 1 ? 'en' : ''); } // Toast let toastTO = null; function toast(msg) { let el = document.getElementById('gToast'); if (!el) { el = document.createElement('div'); el.id = 'gToast'; el.style.cssText = 'position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:#111827;color:#fff;padding:10px 20px;border-radius:10px;font-size:13px;font-weight:600;z-index:9999;box-shadow:0 4px 20px rgba(0,0,0,.3);transition:opacity .3s;'; document.body.appendChild(el); } el.textContent = msg; el.style.opacity = '1'; if (toastTO) clearTimeout(toastTO); toastTO = setTimeout(() => { el.style.opacity = '0'; }, 2800); } // Demo data for first load function loadDemoData() { if (getArmaturen().length > 0) return; const demo = [ { id:'ARM_001', name:'Dusche EG', gebaeude:'Haus A', stockwerk:'EG', raum:'Bad', typ:'Dusche', spuelzeit:180, status:'faellig', letzteSpuelung: new Date(Date.now()-5*24*3600000).toISOString() }, { id:'ARM_002', name:'WC Herren 1.OG', gebaeude:'Haus A', stockwerk:'1.OG', raum:'WC Herren', typ:'KW', spuelzeit:120, status:'ok', letzteSpuelung: new Date(Date.now()-2*24*3600000).toISOString() }, { id:'ARM_003', name:'Küche EG', gebaeude:'Haus A', stockwerk:'EG', raum:'Küche', typ:'KW+WW', spuelzeit:120, status:'ok', letzteSpuelung: new Date(Date.now()-1*24*3600000).toISOString() }, { id:'ARM_004', name:'Dusche 2.OG', gebaeude:'Haus B', stockwerk:'2.OG', raum:'Bad', typ:'Dusche', spuelzeit:180, status:'faellig', letzteSpuelung: null }, { id:'ARM_005', name:'WC Damen EG', gebaeude:'Haus B', stockwerk:'EG', raum:'WC Damen', typ:'KW', spuelzeit:120, status:'ok', letzteSpuelung: new Date(Date.now()-12*3600000).toISOString() }, ]; dbSet('armaturen', demo); // Demo timer dbSet('timer72', Date.now() - 45 * 3600 * 1000); // 45h ago // Demo settings dbSet('settings', { aktiv: true, intervalH: 72, defaultSpuelzeit: 120, notifRollen: ['Technischer Dienst'], objekt: 'Liegenschaft Musterstrasse 12', verantwortlich: 'Max Muster', kontakt: 'muster@example.ch' }); }