Files
deskhub/desktop/src/admin-status.js
T
2026-10-24 20:15:40 +00:00

56 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
// Verwaltungsbereich „Statusseite“: öffentliche Seite für Kunden mit dem Zustand der Dienste und Störungsmeldungen.
// Gleicher Funktionsumfang wie in der Android-App; die Texte der Seite selbst stehen unter /status.php.
async function adminStatusPage(body) {
const A = 'status.php';
const COLORS = { operational: '#157a45', maintenance: '#2563eb', degraded: '#b45309', outage: '#dc2626' };
let st = null;
const act = async (payload, ok) => { const r = await run(() => post(A, payload), ok); if (r) { st = r.status; paint(); } return r; };
function paint() {
const s = st;
const comps = h('div', { class: 'panel' }, h('div', { class: 'sect first' }, 'Dienste'),
s.components.length ? h('table', {}, ...s.components.map((c) => h('tr', {},
h('td', {}, h('b', {}, c.name), c.description ? h('div', { class: 'muted' }, c.description) : null),
h('td', {}, h('select', { onchange: (e) => act({ action: 'set_component', id: c.id, status: e.target.value }, 'Zustand geändert') },
...s.componentStates.map((o) => h('option', { value: o.key, selected: o.key === c.status }, o.label))))))) : h('div', { class: 'muted' }, 'Noch keine Dienste eingetragen.'));
const title = h('input', { placeholder: 'z. B. E-Mail-Versand verzögert' }), text = h('textarea', { placeholder: 'Was ist passiert? Was tun wir dagegen?' });
const level = h('select', {}, ...s.incidentStates.map((o) => h('option', { value: o.key }, o.label)));
const comp = h('select', {}, h('option', { value: '' }, '– kein bestimmter Dienst –'), ...s.components.map((c) => h('option', { value: c.id }, c.name)));
const compState = h('select', {}, h('option', { value: '' }, '– nicht ändern –'), ...s.componentStates.map((o) => h('option', { value: o.key }, o.label)));
const create = h('div', { class: 'panel' }, h('div', { class: 'sect first' }, 'Neue Meldung (Störung oder Wartung)'),
h('label', {}, 'Titel'), title, h('label', {}, 'Beschreibung'), text, h('label', {}, 'Stufe'), level, h('label', {}, 'Betrifft'), comp, h('label', {}, 'Dienst setzen auf'), compState,
h('div', { class: 'row top' }, h('button', { class: 'btn', onclick: () => act({ action: 'create_incident', title: title.value, body: text.value, status: level.value, componentId: comp.value ? +comp.value : null, componentState: compState.value || null }, 'Meldung veröffentlicht') }, 'Veröffentlichen')));
const incidents = h('div', { class: 'panel' }, h('div', { class: 'sect first' }, 'Meldungen (letzte 14 Tage)'),
s.incidents.length ? null : h('div', { class: 'muted' }, 'Keine Meldungen.'),
...s.incidents.map((i) => {
const upd = h('input', { placeholder: 'Update für die Kunden' });
const lv = h('select', {}, ...s.incidentStates.map((o) => h('option', { value: o.key, selected: o.key === i.status }, o.label)));
return h('div', { class: 'incident' },
h('div', {}, h('b', {}, i.title), ' ', h('span', { class: 'chip' }, i.statusLabel), i.component ? h('span', { class: 'muted' }, ' · ' + i.component) : null, h('span', { class: 'muted' }, ' · ' + fmtDate(i.createdAt))),
...i.updates.map((u) => h('div', { class: 'muted' }, `${u.statusLabel}${u.body ? ': ' + u.body : ''}`)),
h('div', { class: 'row top' }, i.status === 'resolved' ? null : [lv, upd, h('button', { class: 'btn sm', onclick: () => act({ action: 'update_incident', id: i.id, status: lv.value, body: upd.value }, 'Update veröffentlicht') }, 'Update senden')],
h('button', { class: 'btn danger sm', onclick: () => confirm('Meldung löschen?') && act({ action: 'delete_incident', id: i.id }, 'Gelöscht') }, '✕')));
}));
clear(view).append(
h('div', { class: 'panel' }, h('div', { class: 'row' },
h('span', { class: 'pill', color: s.overall.color }, s.overall.label),
h('label', { class: 'check grow' }, h('input', { type: 'checkbox', checked: s.enabled, onchange: (e) => act({ action: 'set_enabled', enabled: e.target.checked }, e.target.checked ? 'Statusseite eingeschaltet' : 'Statusseite ausgeschaltet') }), 'Statusseite für Kunden anzeigen')),
s.enabled ? h('div', { class: 'muted' }, 'Adresse für Kunden: ' + s.url) : null),
comps, create, incidents);
}
const view = h('div');
body.append(view);
const r = await run(() => api(A)); if (!r) return;
st = r.status; paint();
}
ADMIN.push({ key: 'statuspage', label: 'Statusseite', admin: true, custom: adminStatusPage });
ADMIN_GROUPS.find((g) => g[0] === 'System')[1].push('statuspage');
ADMIN_DESC.statuspage = 'Dienste und Störungsmeldungen für Kunden';