sdsad
All checks were successful
Deploy / deploy-staging (push) Successful in 6s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-04-27 02:23:44 +02:00
parent 0174fa9d27
commit f4fa8acb97
2 changed files with 134 additions and 2 deletions

View File

@@ -198,6 +198,47 @@
color: var(--muted);
}
.pihole-console-modal {
width: min(720px, 96vw);
}
.pihole-console-body {
margin-top: 16px;
display: grid;
gap: 10px;
max-height: 48vh;
overflow: auto;
padding: 4px;
}
.pihole-console-line {
display: grid;
gap: 4px;
padding: 12px 14px;
border-radius: 14px;
border: 1px solid var(--line);
background: rgba(255, 255, 255, 0.6);
}
.pihole-console-line span {
font-size: 0.8rem;
color: var(--muted);
}
.pihole-console-line strong {
font-weight: 600;
}
.pihole-console-line.is-success {
border-color: rgba(0, 179, 164, 0.22);
background: rgba(0, 179, 164, 0.08);
}
.pihole-console-line.is-error {
border-color: rgba(255, 90, 61, 0.24);
background: rgba(255, 90, 61, 0.08);
}
.pihole-instance-card {
padding: 16px;
background: var(--panel-2);

View File

@@ -19,6 +19,9 @@
let refreshTimer = null;
let loadInFlight = false;
let actionInFlight = false;
let actionConsoleApi = null;
let actionConsoleBody = null;
let actionConsoleClose = null;
const apiCall = async (action, payload = {}) => {
const res = await fetch(`/module/pihole/api?action=${encodeURIComponent(action)}`,
@@ -47,9 +50,82 @@
el.textContent = value;
};
const ensureActionConsole = () => {
if (actionConsoleApi && actionConsoleBody && actionConsoleClose) {
return;
}
const root = document.createElement('div');
root.className = 'modal';
root.dataset.piholeConsoleModal = '1';
root.setAttribute('aria-hidden', 'true');
root.innerHTML = `
<div class="modal-card pihole-console-modal" role="dialog" aria-modal="true" aria-labelledby="pihole-console-title">
<div class="modal-header">
<div>
<strong id="pihole-console-title">Pi-hole Aktion</strong>
<div class="muted">Status und Rueckmeldungen zur laufenden Aktion.</div>
</div>
<div class="modal-actions">
<button class="module-button module-button--secondary module-button--small" type="button" data-pihole-console-clear>Leeren</button>
<button class="icon-button" type="button" data-pihole-console-close aria-label="Konsole schliessen">×</button>
</div>
</div>
<div class="pihole-console-body" data-pihole-console-body></div>
</div>
`;
document.body.appendChild(root);
actionConsoleBody = root.querySelector('[data-pihole-console-body]');
actionConsoleClose = root.querySelector('[data-pihole-console-close]');
const clearBtn = root.querySelector('[data-pihole-console-clear]');
actionConsoleApi = window.NexusModal && typeof window.NexusModal.create === 'function'
? window.NexusModal.create(root, { initialFocus: '[data-pihole-console-close]' })
: {
open() {
root.classList.add('is-open');
root.setAttribute('aria-hidden', 'false');
},
close() {
root.classList.remove('is-open');
root.setAttribute('aria-hidden', 'true');
},
};
if (actionConsoleClose) {
actionConsoleClose.addEventListener('click', () => {
if (!actionInFlight) {
actionConsoleApi.close();
}
});
}
if (clearBtn) {
clearBtn.addEventListener('click', () => {
if (actionConsoleBody) {
actionConsoleBody.innerHTML = '';
}
});
}
};
const appendActionLog = (message, tone = 'info') => {
ensureActionConsole();
if (!actionConsoleBody) {
return;
}
const row = document.createElement('div');
row.className = `pihole-console-line is-${tone}`;
row.innerHTML = `<span>${new Date().toLocaleTimeString('de-DE')}</span><strong>${message}</strong>`;
actionConsoleBody.appendChild(row);
actionConsoleBody.scrollTop = actionConsoleBody.scrollHeight;
};
const setActionLock = (locked, message = 'Bitte warten ...') => {
actionInFlight = locked;
page.classList.toggle('is-busy', locked);
ensureActionConsole();
let overlay = page.querySelector('[data-pihole-busy-overlay]');
if (!overlay) {
@@ -67,6 +143,9 @@
}
overlay.hidden = !locked;
if (actionConsoleClose) {
actionConsoleClose.disabled = locked;
}
page.querySelectorAll('button, input, select, textarea').forEach((el) => {
const formControl = el;
@@ -229,26 +308,38 @@
: action === 'gravity'
? 'Listen werden aktualisiert ...'
: 'Aktion wird ausgefuehrt ...';
ensureActionConsole();
if (actionConsoleBody) {
actionConsoleBody.innerHTML = '';
}
actionConsoleApi.open();
appendActionLog(actionLabel, 'info');
setActionLock(true, actionLabel);
if (action === 'enable') {
appendActionLog(`Aktiviere ${instance === 'all' ? 'alle Instanzen' : `Instanz ${instance}`}.`, 'info');
await apiCall('enable', payload);
} else if (action === 'disable' || action === 'disable-custom') {
if (!payload.minutes || payload.minutes <= 0) {
alert('Bitte Minuten angeben.');
appendActionLog('Fehler: Bitte Minuten angeben.', 'error');
return;
}
appendActionLog(`Deaktiviere ${instance === 'all' ? 'alle Instanzen' : `Instanz ${instance}`} fuer ${payload.minutes} Minuten.`, 'info');
await apiCall('disable', payload);
} else if (action === 'gravity') {
appendActionLog(`Starte Listen-Update fuer ${instance === 'all' ? 'alle Instanzen' : `Instanz ${instance}`}.`, 'info');
await apiCall('gravity', payload);
const status = document.querySelector('[data-list-update-status]');
if (status) status.textContent = 'Listen-Update gestartet.';
} else if (action === 'update') {
appendActionLog(`Starte Pi-hole-Update fuer ${instance === 'all' ? 'alle Instanzen' : `Instanz ${instance}`}.`, 'info');
await apiCall('update', payload);
}
appendActionLog('Aktion abgeschlossen. Dashboard wird aktualisiert.', 'success');
await loadDashboard();
appendActionLog('Anzeige erfolgreich aktualisiert.', 'success');
} catch (err) {
alert(`Aktion fehlgeschlagen: ${err.message}`);
appendActionLog(`Aktion fehlgeschlagen: ${err.message}`, 'error');
} finally {
setActionLock(false);
}