56 lines
4.6 KiB
JavaScript
56 lines
4.6 KiB
JavaScript
'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';
|