import re with open("static/js/bottom-bar.js", "r") as f: content = f.read() # Replace the innerList rendering logic to handle raw HTML from our templates securely old_render_tab = """ // Render lists const lines = listFor(activeKey, latestSections); const ul = document.createElement('ul'); ul.className = 'bb-tab-list'; lines.forEach(function (line) { const li = document.createElement('li'); li.innerHTML = String(line) .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>'); ul.appendChild(li); }); innerContent.innerHTML = ''; innerContent.appendChild(ul);""" new_render_tab = """ // Render rich UI lists const lines = listFor(activeKey, latestSections); const ul = document.createElement('ul'); ul.className = 'bb-tab-list'; lines.forEach(function (line) { const li = document.createElement('li'); // Allow rich HTML (buttons, inputs) - assuming listFor provides sanitized data wrapped in markup li.innerHTML = line; ul.appendChild(li); }); innerContent.innerHTML = ''; // Add specific headers/controls based on active tab if (activeKey === 'tasks') { const topBar = document.createElement('div'); topBar.className = 'bb-task-actions mb-3'; topBar.innerHTML = ''; innerContent.appendChild(topBar); } if (activeKey === 'messages') { const chatContainer = document.createElement('div'); chatContainer.className = 'd-flex flex-column h-100'; ul.classList.add('flex-grow-1', 'mb-3'); const replyBox = document.createElement('div'); replyBox.className = 'input-group mt-2 border-top pt-2 border-primary-subtle'; replyBox.innerHTML = ''; chatContainer.appendChild(ul); chatContainer.appendChild(replyBox); innerContent.appendChild(chatContainer); } else { innerContent.appendChild(ul); }""" content = content.replace(old_render_tab, new_render_tab) # Now enhance the listFor content with rich HTML strings old_listFor = """ if (key === 'overview') { if (overviewFilter === 'urgent') return urgent.list ? urgent.list.map(u => '🚨 Hastesag: ' + u.title) : ['Ingen hastesager.']; if (overviewFilter === 'kuma') return kuma.list ? kuma.list.map(k => '📉 Nede: ' + k) : ['Alle systemer oppe.']; if (overviewFilter === 'eset') return eset.list ? eset.list.map(e => '🔐 Incident: ' + e) : ['Ingen ESET incidents.']; if (overviewFilter === 'cases') return cases.list ? cases.list.map(c => '📂 ' + c.title) : ['Ingen åbne sager.']; if (overviewFilter === 'mail') return ['📧 ' + mail.unread + ' ulæste mails. ' + mail.customer_reply_needed + ' kundesvar krævet.']; let out = []; if (urgent.count > 0) out.push('🚨 Hastesager: ' + urgent.count + ' aktive'); if (mail.unread > 0) out.push('📧 Ubesvarede mails: ' + mail.unread + ' (' + mail.customer_reply_needed + ' kræver svar)'); if (cases.open > 0) out.push('📂 Åbne sager i alt: ' + cases.open); if (kuma.down > 0) out.push('📉 Uptime Kuma nedetid: ' + kuma.down + ' enheder'); if (eset.incidents > 0) out.push('🔐 ESET incidents: ' + eset.incidents); if (out.length === 0) { out.push('🎉 Alt ser grønt ud! Intet kritisk lige nu.'); out.push('👉 Klik på fanerne til venstre for mere info.'); } return out; } if (key === 'timer') { if (timer.active_count > 0) { return (timer.list || []).map(t => 'Timer aktiv: ' + t.description); } return ['Ingen aktive timere lige nu.']; } if (key === 'messages') { if (messages.count > 0) { return (messages.list || []).map(m => m.from + ': ' + m.text); } return ['Ingen nye beskeder.']; } if (key === 'tasks') { if (tasks.count > 0) { return (tasks.list || []).map(t => t.title + ' (Deadline: ' + t.deadline + ')'); } return ['Ingen aktuelle opgaver.']; } if (key === 'boss') { if (boss.stats) { return [ 'Ufordelte opgaver: ' + boss.stats.unassigned, 'Medarbejdere aktive: ' + boss.stats.active_employees ]; } return ['Henter chef-overblik...']; } return ['Klik rundt i menuen for at se data for ' + key];""" new_listFor = """ function esc(str) { return String(str).replace(/&/g, '&').replace(//g, '>'); } if (key === 'overview') { if (overviewFilter === 'urgent') return urgent.list ? urgent.list.map(u => '