265 lines
8.8 KiB
Python
265 lines
8.8 KiB
Python
|
|
with open("static/js/bottom-bar.js", "r") as f:
|
||
|
|
content = f.read()
|
||
|
|
|
||
|
|
# We replace everything from getCounts downwards.
|
||
|
|
# Let's find the start of getCounts
|
||
|
|
start_idx = content.find("function getCounts")
|
||
|
|
|
||
|
|
new_logic = """function getCounts(sections) {
|
||
|
|
const mail = sections.mail || {};
|
||
|
|
const cases = sections.cases || {};
|
||
|
|
const urgent = sections.urgent || {};
|
||
|
|
const timer = sections.timer || {};
|
||
|
|
const kuma = sections.kuma || {};
|
||
|
|
const eset = sections.eset || {};
|
||
|
|
|
||
|
|
return {
|
||
|
|
mail: Number(mail.unread || 0),
|
||
|
|
cases: Number(cases.open || 0),
|
||
|
|
urgent: Number(urgent.count || 0),
|
||
|
|
timer: Number(timer.active_count || 0),
|
||
|
|
kuma: Number(kuma.down || 0),
|
||
|
|
eset: Number(eset.incidents || 0)
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function detailTextFor(key, sections) {
|
||
|
|
const counts = getCounts(sections);
|
||
|
|
const nameMap = {
|
||
|
|
mail: 'Ubesvarede mails',
|
||
|
|
cases: 'Åbne sager',
|
||
|
|
urgent: 'Hastesager',
|
||
|
|
timer: 'Aktive timere',
|
||
|
|
kuma: 'Kuma alerts',
|
||
|
|
eset: 'ESET incidents'
|
||
|
|
};
|
||
|
|
const val = counts[key] || 0;
|
||
|
|
return nameMap[key] + ': ' + val;
|
||
|
|
}
|
||
|
|
|
||
|
|
function listFor(key, sections) {
|
||
|
|
const mail = sections.mail || {};
|
||
|
|
const cases = sections.cases || {};
|
||
|
|
const urgent = sections.urgent || {};
|
||
|
|
const timer = sections.timer || {};
|
||
|
|
const kuma = sections.kuma || {};
|
||
|
|
const eset = sections.eset || {};
|
||
|
|
const messages = sections.messages || {};
|
||
|
|
const tasks = sections.tasks || {};
|
||
|
|
const boss = sections.boss || {};
|
||
|
|
|
||
|
|
if (key === 'overview') {
|
||
|
|
return [
|
||
|
|
'Velkommen til dit overblik',
|
||
|
|
'Her vises det vigtigste på tværs af systemet',
|
||
|
|
'Næste opgave kl. 14:00'
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
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];
|
||
|
|
}
|
||
|
|
|
||
|
|
function updateBar(sections) {
|
||
|
|
const counts = getCounts(sections);
|
||
|
|
const keys = Object.keys(counts);
|
||
|
|
|
||
|
|
for (let i = 0; i < keys.length; i++) {
|
||
|
|
const key = keys[i];
|
||
|
|
const chipText = document.querySelector('.bb-chip[data-bb-key="' + key + '"] .bb-chip-text');
|
||
|
|
const chip = document.querySelector('.bb-chip[data-bb-key="' + key + '"]');
|
||
|
|
if (chipText && chip) {
|
||
|
|
const val = counts[key];
|
||
|
|
|
||
|
|
const labels = {
|
||
|
|
mail: 'Mails',
|
||
|
|
cases: 'Sager',
|
||
|
|
urgent: 'Hastesager',
|
||
|
|
timer: 'Timere',
|
||
|
|
kuma: 'Kuma',
|
||
|
|
eset: 'ESET'
|
||
|
|
};
|
||
|
|
|
||
|
|
chipText.textContent = labels[key] + ': ' + val;
|
||
|
|
chip.classList.toggle('has-items', val > 0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function renderTabPanel() {
|
||
|
|
const titleContainer = byId('bbTabTitle');
|
||
|
|
const innerContent = byId('bbTabInnerContent');
|
||
|
|
if (!titleContainer || !innerContent) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const titleText = titleContainer.querySelector('.bb-tab-title-text');
|
||
|
|
|
||
|
|
const titleByKey = {
|
||
|
|
overview: 'Overblik',
|
||
|
|
timer: 'Timere',
|
||
|
|
messages: 'Beskeder',
|
||
|
|
tasks: 'Opgaver',
|
||
|
|
boss: 'Chef Dashboard'
|
||
|
|
};
|
||
|
|
|
||
|
|
const iconByKey = {
|
||
|
|
overview: 'bi-bell',
|
||
|
|
timer: 'bi-stopwatch',
|
||
|
|
messages: 'bi-chat-dots',
|
||
|
|
tasks: 'bi-calendar-check',
|
||
|
|
boss: 'bi-person-workspace'
|
||
|
|
};
|
||
|
|
|
||
|
|
const activeTitle = titleByKey[activeKey] || 'Info';
|
||
|
|
if (titleText) {
|
||
|
|
titleText.textContent = activeTitle;
|
||
|
|
} else {
|
||
|
|
titleContainer.textContent = activeTitle;
|
||
|
|
}
|
||
|
|
|
||
|
|
const iconSpan = titleContainer.querySelector('.bi');
|
||
|
|
if (iconSpan) {
|
||
|
|
iconSpan.className = 'bi ' + (iconByKey[activeKey] || 'bi-info-circle') + ' me-2 text-accent';
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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);
|
||
|
|
}
|
||
|
|
|
||
|
|
function bindSideTabs() {
|
||
|
|
const buttons = document.querySelectorAll('.bb-tab-btn');
|
||
|
|
for (let i = 0; i < buttons.length; i++) {
|
||
|
|
buttons[i].addEventListener('click', function () {
|
||
|
|
for (let j = 0; j < buttons.length; j++) {
|
||
|
|
buttons[j].classList.remove('is-active');
|
||
|
|
buttons[j].setAttribute('aria-selected', 'false');
|
||
|
|
}
|
||
|
|
this.classList.add('is-active');
|
||
|
|
this.setAttribute('aria-selected', 'true');
|
||
|
|
|
||
|
|
activeKey = this.getAttribute('data-bb-tab');
|
||
|
|
renderTabPanel();
|
||
|
|
|
||
|
|
const detail = byId('bbCountDetail');
|
||
|
|
if (detail) {
|
||
|
|
detail.innerHTML = '<i class="bi bi-info-circle me-1 opacity-75"></i> Viser: ' + (activeKey.charAt(0).toUpperCase() + activeKey.slice(1));
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function bindChipClicks() {
|
||
|
|
const chips = document.querySelectorAll('.bb-chip');
|
||
|
|
for (let i = 0; i < chips.length; i++) {
|
||
|
|
chips[i].addEventListener('click', function () {
|
||
|
|
const key = this.getAttribute('data-bb-key');
|
||
|
|
if (!key) return;
|
||
|
|
|
||
|
|
const detail = byId('bbCountDetail');
|
||
|
|
if (detail) {
|
||
|
|
detail.innerHTML = '<i class="bi bi-check-circle me-1 text-accent"></i> ' + detailTextFor(key, latestSections);
|
||
|
|
}
|
||
|
|
|
||
|
|
// If not expanded, expand it
|
||
|
|
const shell = byId('globalBottomBar');
|
||
|
|
if (shell && !shell.classList.contains('is-expanded')) {
|
||
|
|
setExpanded(true);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Map top chip keys to side tabs if applicable, else 'overview'
|
||
|
|
let targetTab = 'overview';
|
||
|
|
if (key === 'timer') targetTab = 'timer';
|
||
|
|
|
||
|
|
const tabBtn = document.querySelector('.bb-tab-btn[data-bb-tab="' + targetTab + '"]');
|
||
|
|
if (tabBtn) tabBtn.click();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function bindSheetToggle() {
|
||
|
|
const toggle = byId('bbSheetToggle');
|
||
|
|
if (!toggle) return;
|
||
|
|
toggle.addEventListener('click', function () {
|
||
|
|
const shell = byId('globalBottomBar');
|
||
|
|
if (!shell) return;
|
||
|
|
const isExp = shell.classList.contains('is-expanded');
|
||
|
|
setExpanded(!isExp);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function tick() {
|
||
|
|
fetchBottomBarState().then(function (data) {
|
||
|
|
if (data && data.enabled) {
|
||
|
|
latestSections = data.sections || {};
|
||
|
|
updateBar(latestSections);
|
||
|
|
renderTabPanel();
|
||
|
|
setVisibility(true);
|
||
|
|
} else {
|
||
|
|
setVisibility(false);
|
||
|
|
}
|
||
|
|
}).catch(function (err) {
|
||
|
|
console.warn('Bottom bar poll failed', err);
|
||
|
|
}).finally(function () {
|
||
|
|
window.setTimeout(tick, 15000);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
document.addEventListener('DOMContentLoaded', function () {
|
||
|
|
activeKey = 'overview'; // Default overview state
|
||
|
|
bindChipClicks();
|
||
|
|
bindSheetToggle();
|
||
|
|
bindSideTabs();
|
||
|
|
tick();
|
||
|
|
});
|
||
|
|
|
||
|
|
})();
|
||
|
|
"""
|
||
|
|
|
||
|
|
combined = content[:start_idx] + new_logic
|
||
|
|
with open("static/js/bottom-bar.js", "w") as f:
|
||
|
|
f.write(combined)
|