⚡ Update wird installiert...
// -- Zeiten: Aufklappen, Bulk, Checkboxen, Auto-Collapse ------------------ (function() { var _checked = {}; var _initCollapsed = false; function isZeiten() { return location.hash === '#times' || location.hash === '#zeiten'; } function tok() { return localStorage.getItem('ac_token') || ''; } function H(j) { var h={Authorization:'Bearer '+tok()}; if(j) h['Content-Type']='application/json'; return h; } function updateBulkBar() { var count = Object.keys(_checked).length; var bar = document.getElementById('ac-bulk-bar'); if (!bar) return; bar.style.display = count > 0 ? 'flex' : 'none'; var sc = document.getElementById('ac-sel-count'); if (sc) sc.textContent = count + ' ausgewählt'; } function collapseCard(card, collapse) { var table = card.querySelector('table'); var icon = card.querySelector('.ac-col-icon'); if (!table) return; if (collapse) { table.style.display = 'none'; if (icon) icon.textContent = '⊞'; card._acCollapsed = true; } else { table.style.display = ''; if (icon) icon.textContent = '⊟'; card._acCollapsed = false; } } function enhanceCard(card) { if (card._acDone) return; var avatar = card.querySelector('.avatar'); var table = card.querySelector('table'); if (!avatar || !table) return; card._acDone = true; // -- Collapse icon in header var header = card.firstElementChild; if (header && !header._acCollapsible) { header._acCollapsible = true; header.style.cursor = 'pointer'; var icon = document.createElement('span'); icon.className = 'ac-col-icon'; icon.style.cssText = 'margin-left:auto;color:var(--text3);font-size:16px;padding-left:8px;flex-shrink:0'; icon.textContent = '⊟'; header.style.display = 'flex'; header.style.alignItems = 'center'; header.appendChild(icon); header.onclick = function(e) { if (e.target.closest('input')) return; collapseCard(card, !card._acCollapsed); }; } // -- Pending badge on header var pendingRows = Array.from(table.querySelectorAll('tbody tr')).filter(function(r) { return r.querySelector('.badge-pending'); }); if (pendingRows.length > 0) { var badge = card.querySelector('.ac-pending-badge'); if (!badge) { badge = document.createElement('span'); badge.className = 'ac-pending-badge'; badge.style.cssText = 'background:#fef3c7;color:#d97706;border-radius:99px;padding:2px 9px;font-size:11px;font-weight:700;margin-left:8px'; badge.textContent = pendingRows.length + ' offen'; var nameEl = card.querySelector('[style*="fontWeight:700"]'); if (nameEl) nameEl.appendChild(badge); } } // -- Checkboxes per row var thead = table.querySelector('thead tr'); if (thead && !thead._acSelAll) { thead._acSelAll = true; var th = document.createElement('th'); th.style.cssText = 'width:32px;padding:6px 4px'; var cbAll = document.createElement('input'); cbAll.type = 'checkbox'; cbAll.title = 'Alle auswählen'; cbAll.style.cursor = 'pointer'; cbAll.onchange = function() { table.querySelectorAll('[data-row-cb]').forEach(function(cb) { cb.checked = cbAll.checked; var id = cb.dataset.rowCb; if (cbAll.checked) _checked[id] = true; else delete _checked[id]; }); updateBulkBar(); }; th.appendChild(cbAll); thead.insertBefore(th, thead.firstChild); } var rows = table.querySelectorAll('tbody tr'); rows.forEach(function(row) { if (row._acCbDone) return; // Extract ID from delete button onclick var delBtn = Array.from(row.querySelectorAll('button')).find(function(b){ return b.title === 'Löschen'; }); if (!delBtn) return; // Get id from React fiber - find it via __reactFiber var id = null; try { var fiberKey = Object.keys(delBtn).find(function(k){ return k.startsWith('__reactFiber') || k.startsWith('__reactProps'); }); if (fiberKey) { var props = delBtn[fiberKey]; var onClickStr = (props.onClick || '').toString(); var m = onClickStr.match(/\((\d+)\)/); if (m) id = m[1]; } } catch(e) {} // Fallback: parse from siblings if (!id) { var approvBtn = Array.from(row.querySelectorAll('button')).find(function(b){ return b.title === 'Genehmigen'; }); if (approvBtn) { try { var fk = Object.keys(approvBtn).find(function(k){ return k.startsWith('__reactProps'); }); if (fk) { var str = approvBtn[fk].onClick.toString(); var mm = str.match(/\((\d+)\)/); if (mm) id = mm[1]; } } catch(e) {} } } if (!id) return; row._acCbDone = true; var td = document.createElement('td'); td.style.cssText = 'padding:4px;width:32px'; var cb = document.createElement('input'); cb.type = 'checkbox'; cb.dataset.rowCb = id; cb.style.cursor = 'pointer'; cb.onchange = function() { if (cb.checked) _checked[id] = true; else delete _checked[id]; updateBulkBar(); }; td.appendChild(cb); row.insertBefore(td, row.firstChild); }); // Auto-collapse on first load (only once) if (!_initCollapsed) { collapseCard(card, true); } } function injectUI() { if (!isZeiten()) return; // Filter row var monthInput = document.querySelector('input[type="month"]'); if (!monthInput) return; var filterRow = monthInput.parentNode; // Collapse-all button if (!document.getElementById('ac-col-all')) { var btn = document.createElement('button'); btn.id = 'ac-col-all'; btn.style.cssText = 'padding:8px 14px;border-radius:8px;border:1px solid var(--border);background:#fff;color:var(--text2);font-size:13px;font-weight:600;cursor:pointer'; btn.textContent = '⊞ Alle aufklappen'; btn.onclick = function() { var anyCollapsed = !!document.querySelector('.card[_acCollapsed]') || Array.from(document.querySelectorAll('.card')).some(function(c){ return c._acCollapsed; }); document.querySelectorAll('.card').forEach(function(c) { if (c.querySelector('table')) collapseCard(c, !anyCollapsed); }); btn.textContent = anyCollapsed ? '⊟ Alle zuklappen' : '⊞ Alle aufklappen'; }; filterRow.appendChild(btn); } // Bulk bar if (!document.getElementById('ac-bulk-bar')) { var bar = document.createElement('div'); bar.id = 'ac-bulk-bar'; bar.style.cssText = 'display:none;align-items:center;gap:10px;padding:10px 16px;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:10px;margin-bottom:12px;flex-wrap:wrap'; bar.innerHTML = '0 ausgewählt' + '' + '' + ''; filterRow.parentNode.insertBefore(bar, filterRow.nextSibling); document.getElementById('ac-b-app').onclick = async function() { var ids = Object.keys(_checked); if (!ids.length || !confirm(ids.length + ' Einträge genehmigen?')) return; for (var id of ids) { await fetch('/api/clock/entries/'+id+'/approve',{method:'POST',headers:H(true),body:JSON.stringify({location_id:1})}).catch(function(){}); } _checked = {}; setTimeout(function(){ location.reload(); }, 400); }; document.getElementById('ac-b-del').onclick = async function() { var ids = Object.keys(_checked); if (!ids.length || !confirm(ids.length + ' Einträge löschen?')) return; for (var id of ids) { await fetch('/api/clock/entries/'+id,{method:'DELETE',headers:H()}).catch(function(){}); } _checked = {}; setTimeout(function(){ location.reload(); }, 400); }; document.getElementById('ac-b-clr').onclick = function() { _checked = {}; document.querySelectorAll('[data-row-cb]').forEach(function(cb){ cb.checked = false; }); updateBulkBar(); }; } // Enhance all cards document.querySelectorAll('.card').forEach(enhanceCard); // Mark init done after first pass if (!_initCollapsed) _initCollapsed = true; } var _t = null; new MutationObserver(function() { if (!isZeiten()) return; clearTimeout(_t); _t = setTimeout(injectUI, 300); }).observe(document.body, { childList: true, subtree: true }); window.addEventListener('hashchange', function() { _initCollapsed = false; _checked = {}; setTimeout(injectUI, 500); }); })(); // -- Zeiten v5: Auto-collapse, Checkboxen, Bulk ---------------------------- (function() { var _checked = {}; var _firstLoad = true; function isZeiten() { return location.hash === '#times' || location.hash === '#zeiten'; } function tok() { return localStorage.getItem('ac_token') || ''; } function H(j) { var h={Authorization:'Bearer '+tok()}; if(j) h['Content-Type']='application/json'; return h; } function updateBulkBar() { var count = Object.keys(_checked).length; var bar = document.getElementById('ac-bulk-bar'); if (bar) bar.style.display = count > 0 ? 'flex' : 'none'; var sc = document.getElementById('ac-sel-count'); if (sc) sc.textContent = count + ' ausgewählt'; } function collapseCard(card, collapse) { var table = card.querySelector('table'); var icon = card.querySelector('.ac-icon'); if (!table) return; table.style.display = collapse ? 'none' : ''; if (icon) icon.textContent = collapse ? '⊞' : '⊟'; card._collapsed = collapse; } function enhanceCard(card) { if (card._done) return; if (!card.querySelector('table') || !card.querySelector('.avatar')) return; card._done = true; // Collapse icon var header = card.firstElementChild; if (header && !header._hDone) { header._hDone = true; header.style.cssText = (header.getAttribute('style')||'') + ';cursor:pointer;display:flex;align-items:center'; var icon = document.createElement('span'); icon.className = 'ac-icon'; icon.style.cssText = 'margin-left:auto;color:var(--text3);font-size:16px;flex-shrink:0'; icon.textContent = '⊟'; header.appendChild(icon); header.onclick = function(e) { if (e.target.tagName === 'INPUT') return; collapseCard(card, !card._collapsed); }; } // Pending count badge var rows = Array.from(card.querySelectorAll('tbody tr')); var pending = rows.filter(function(r){ return r.querySelector('.badge-pending'); }).length; if (pending > 0 && !card.querySelector('.ac-badge')) { var badge = document.createElement('span'); badge.className = 'ac-badge'; badge.style.cssText = 'background:#fef3c7;color:#d97706;border-radius:99px;padding:2px 8px;font-size:11px;font-weight:700;margin-left:8px'; badge.textContent = pending + ' offen'; var nm = card.querySelector('[style*="fontWeight"]'); if (nm) nm.parentNode.insertBefore(badge, nm.nextSibling); } // Select-all checkbox in thead var thead = card.querySelector('thead tr'); if (thead && !thead._done) { thead._done = true; var th = document.createElement('th'); th.style.width = '32px'; var cbAll = document.createElement('input'); cbAll.type = 'checkbox'; cbAll.style.cursor = 'pointer'; cbAll.onchange = function() { card.querySelectorAll('[data-rcb]').forEach(function(cb) { cb.checked = cbAll.checked; if (cbAll.checked) _checked[cb.dataset.rcb] = true; else delete _checked[cb.dataset.rcb]; }); updateBulkBar(); }; th.appendChild(cbAll); thead.insertBefore(th, thead.firstChild); } // Per-row checkboxes using data-eid rows.forEach(function(row) { if (row._cbDone) return; var id = row.dataset.eid; if (!id) return; row._cbDone = true; var td = document.createElement('td'); td.style.cssText = 'padding:4px;width:32px'; var cb = document.createElement('input'); cb.type = 'checkbox'; cb.dataset.rcb = id; cb.style.cursor = 'pointer'; cb.onchange = function() { if (cb.checked) _checked[id] = true; else delete _checked[id]; updateBulkBar(); }; td.appendChild(cb); row.insertBefore(td, row.firstChild); }); // Auto-collapse on first page load if (_firstLoad) collapseCard(card, true); } function injectUI() { if (!isZeiten()) return; var monthInput = document.querySelector('input[type="month"]'); if (!monthInput) return; var filterRow = monthInput.parentNode; // Collapse-all button if (!document.getElementById('ac-col-btn')) { var btn = document.createElement('button'); btn.id = 'ac-col-btn'; btn.style.cssText = 'padding:8px 14px;border-radius:8px;border:1px solid var(--border);background:var(--bg2,#f1f5f9);color:var(--text2);font-size:13px;font-weight:600;cursor:pointer'; btn.textContent = '⊞ Alle aufklappen'; btn.onclick = function() { var anyCol = Array.from(document.querySelectorAll('.card')).some(function(c){ return c._collapsed; }); document.querySelectorAll('.card').forEach(function(c){ if (c.querySelector('table')) collapseCard(c, !anyCol); }); btn.textContent = anyCol ? '⊟ Alle zuklappen' : '⊞ Alle aufklappen'; }; filterRow.appendChild(btn); } // Bulk bar if (!document.getElementById('ac-bulk-bar')) { var bar = document.createElement('div'); bar.id = 'ac-bulk-bar'; bar.style.cssText = 'display:none;align-items:center;gap:10px;padding:10px 16px;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:10px;margin-bottom:12px;flex-wrap:wrap'; bar.innerHTML = '0' +'' +'' +''; filterRow.parentNode.insertBefore(bar, filterRow.nextSibling); document.getElementById('ac-b-ok').onclick = async function() { var ids = Object.keys(_checked); if (!ids.length || !confirm(ids.length+' Einträge genehmigen?')) return; for (var id of ids) await fetch('/api/clock/entries/'+id+'/approve',{method:'POST',headers:H(true),body:JSON.stringify({location_id:1})}).catch(function(){}); _checked={}; location.reload(); }; document.getElementById('ac-b-del').onclick = async function() { var ids = Object.keys(_checked); if (!ids.length || !confirm(ids.length+' Einträge löschen?')) return; for (var id of ids) await fetch('/api/clock/entries/'+id,{method:'DELETE',headers:H()}).catch(function(){}); _checked={}; location.reload(); }; document.getElementById('ac-b-clr').onclick = function() { _checked={}; document.querySelectorAll('[data-rcb]').forEach(function(cb){ cb.checked=false; }); updateBulkBar(); }; } // Enhance all cards document.querySelectorAll('.card').forEach(enhanceCard); if (_firstLoad) _firstLoad = false; } var _t; new MutationObserver(function() { if (!isZeiten()) return; clearTimeout(_t); _t = setTimeout(injectUI, 350); }).observe(document.body, {childList:true, subtree:true}); window.addEventListener('hashchange', function() { _firstLoad=true; _checked={}; setTimeout(injectUI, 600); }); })(); // -- HA Admin: Type-Tip + bessere Suche ---------------------------------- (function() { function isSettings() { return location.hash === '#settings' || location.hash === ''; } function inject() { if (!isSettings()) return; // Find the Entity ID input var eidInput = document.getElementById('ha-eid'); if (!eidInput || eidInput._haDone) return; eidInput._haDone = true; // Add type tip div after the datalist var dl = document.getElementById('ha-eid-dl'); if (dl && !document.getElementById('ha-type-tip')) { var tip = document.createElement('div'); tip.id = 'ha-type-tip'; tip.style.cssText = 'display:none;font-size:12px;font-weight:600;color:var(--blue,#2563eb);margin-top:4px;padding:4px 8px;background:#eff6ff;border-radius:6px'; dl.parentNode.parentNode.appendChild(tip); var stateTip = document.createElement('div'); stateTip.id = 'ha-state-tip'; stateTip.style.cssText = 'display:none;font-size:11px;color:var(--text3,#94a3b8);margin-top:2px;padding:2px 8px'; dl.parentNode.parentNode.appendChild(stateTip); } // Improve datalist display - show type label var origInput = eidInput.oninput; eidInput.addEventListener('input', function() { // Hide tips when typing var tip = document.getElementById('ha-type-tip'); var sv = document.getElementById('ha-state-tip'); if (tip) tip.style.display = 'none'; if (sv) sv.style.display = 'none'; }); } var _t; new MutationObserver(function() { clearTimeout(_t); _t = setTimeout(inject, 400); }).observe(document.body, {childList: true, subtree: true}); window.addEventListener('hashchange', function() { setTimeout(inject, 600); }); })();