Beide Module wurden aus einem anderen Kundenprojekt kopiert und bereinigt: Knowledgecenter: - Bild-Cache (1.985 Dateien) geleert, Ordnerstruktur mit .gitkeep behalten - Files-Gallery-Lizenzschlüssel entfernt (config.php) - Kundenspezifische Kategorien gelöscht: Teil I/II/III QM-Struktur, nummerierte QM-Kapitel, Gremien (Präsidium/Finanzausschuss/Landesausschuss), Sitzungsservice-Serie, Protokoll-Abteilungen, Testeinträge - 56 generische Kategorien bleiben (Downloads, Onboarding, Personalthemen …) Tasks: - Alle Betriebsdaten gelöscht (114 Tasks, 914 Submissions, 579 Assignments) - Task-Definitionen und Verlinkungen geleert - Kategoriestruktur bleibt (Allgemein, IT, Personal, Buchhaltung …) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
293 lines
12 KiB
JavaScript
293 lines
12 KiB
JavaScript
(function () {
|
|
'use strict';
|
|
|
|
var dialog = document.getElementById('task-cert-mark-completed-dialog');
|
|
var config = document.getElementById('task-cert-mark-completed-config');
|
|
if (!dialog || !config) { return; }
|
|
|
|
var methodsUrl = config.getAttribute('data-methods-url') || '';
|
|
var submitUrl = config.getAttribute('data-submit-url') || '';
|
|
var taskId = parseInt(config.getAttribute('data-task-id') || '0', 10);
|
|
if (!methodsUrl || !submitUrl || !taskId) { return; }
|
|
|
|
var METHOD_LABELS = {
|
|
knowledge_read: 'Wissensbestätigung',
|
|
form_submit: 'Formular',
|
|
quiz: 'Quiz',
|
|
manual_approval: 'Manuelle Freigabe'
|
|
};
|
|
|
|
function esc(str) {
|
|
var div = document.createElement('div');
|
|
div.appendChild(document.createTextNode(String(str == null ? '' : str)));
|
|
return div.innerHTML;
|
|
}
|
|
|
|
function buildMethodHtml(method) {
|
|
var id = method.id;
|
|
var type = method.method_type;
|
|
var label = METHOD_LABELS[type] || type;
|
|
var required = method.is_required ? '' : ' <span class="task-cert-hint">(optional)</span>';
|
|
var fulfilled = method.already_fulfilled;
|
|
|
|
var html = '<fieldset class="task-cert-dialog-method' + (fulfilled ? ' is-fulfilled' : '') + '">';
|
|
html += '<legend>' + esc(label) + required;
|
|
if (fulfilled) {
|
|
html += ' <span class="task-cert-admin-fulfilled-badge">Bereits erledigt</span>';
|
|
}
|
|
html += '</legend>';
|
|
|
|
if (fulfilled) {
|
|
html += '<p class="task-cert-hint">Wird übersprungen.</p>';
|
|
} else if (type === 'knowledge_read') {
|
|
html += '<p class="task-cert-hint">Alle ' + method.knowledge_post_count + ' Beiträge werden automatisch als gelesen markiert.</p>';
|
|
} else if (type === 'quiz') {
|
|
html += '<p class="task-cert-hint">Quiz wird automatisch als bestanden gewertet (' + method.quiz_question_count + ' Fragen, volle Punktzahl).</p>';
|
|
} else if (type === 'manual_approval') {
|
|
html += '<p class="task-cert-hint">Manuelle Freigabe wird automatisch erteilt.</p>';
|
|
} else if (type === 'form_submit') {
|
|
var fields = method.form_fields || [];
|
|
if (fields.length === 0) {
|
|
html += '<p class="task-cert-hint">Keine Formularfelder definiert.</p>';
|
|
} else {
|
|
html += '<div class="task-cert-dialog-fields">';
|
|
for (var i = 0; i < fields.length; i++) {
|
|
html += buildFieldHtml(id, fields[i]);
|
|
}
|
|
html += '</div>';
|
|
}
|
|
}
|
|
|
|
html += '</fieldset>';
|
|
return html;
|
|
}
|
|
|
|
function buildFieldHtml(methodId, field) {
|
|
var key = field.field_key;
|
|
var label = field.label || key;
|
|
var type = field.field_type;
|
|
var req = field.is_required ? ' <span class="task-cert-hint">(Pflicht)</span>' : '';
|
|
var placeholder = esc(field.placeholder || '');
|
|
var name = 'method__' + methodId + '__' + key;
|
|
var inputId = 'mc-field-' + methodId + '-' + key;
|
|
|
|
var html = '<div class="task-cert-dialog-field">';
|
|
html += '<label for="' + inputId + '">' + esc(label) + req + '</label>';
|
|
|
|
if (type === 'textarea') {
|
|
html += '<textarea name="' + name + '" id="' + inputId + '" rows="3" placeholder="' + placeholder + '"></textarea>';
|
|
} else if (type === 'select' && field.options.length > 0) {
|
|
html += '<select name="' + name + '" id="' + inputId + '">';
|
|
html += '<option value="">-- Bitte wählen --</option>';
|
|
for (var i = 0; i < field.options.length; i++) {
|
|
html += '<option value="' + esc(field.options[i].value) + '">' + esc(field.options[i].label) + '</option>';
|
|
}
|
|
html += '</select>';
|
|
} else if (type === 'checkbox' && field.options.length > 0) {
|
|
html += '<div class="task-cert-dialog-checkbox-group">';
|
|
for (var j = 0; j < field.options.length; j++) {
|
|
html += '<label class="task-cert-dialog-check-label"><input type="checkbox" name="' + name + '[]" value="' + esc(field.options[j].value) + '"> ' + esc(field.options[j].label) + '</label>';
|
|
}
|
|
html += '</div>';
|
|
} else if (type === 'radio' && field.options.length > 0) {
|
|
html += '<div class="task-cert-dialog-radio-group">';
|
|
for (var k = 0; k < field.options.length; k++) {
|
|
html += '<label class="task-cert-dialog-check-label"><input type="radio" name="' + name + '" value="' + esc(field.options[k].value) + '"> ' + esc(field.options[k].label) + '</label>';
|
|
}
|
|
html += '</div>';
|
|
} else {
|
|
var inputType = type === 'number' ? 'number' : (type === 'date' ? 'date' : 'text');
|
|
html += '<input type="' + inputType + '" name="' + name + '" id="' + inputId + '" placeholder="' + placeholder + '">';
|
|
}
|
|
|
|
html += '</div>';
|
|
return html;
|
|
}
|
|
|
|
function buildDialogContent(data) {
|
|
var html = '<div class="task-cert-dialog-inner">';
|
|
|
|
html += '<div class="task-cert-dialog-header">';
|
|
html += '<h3>Als erledigt markieren</h3>';
|
|
html += '<button type="button" class="task-cert-dialog-close" aria-label="Schließen">×</button>';
|
|
html += '</div>';
|
|
|
|
html += '<div class="task-cert-dialog-info">';
|
|
html += '<table class="task-cert-info-table">';
|
|
html += '<tr><th>Aufgabe</th><td>' + esc(data.task_title) + '</td></tr>';
|
|
html += '<tr><th>Mitarbeiter</th><td>' + esc(data.contact_name);
|
|
if (data.contact_email) { html += ' <span class="task-cert-hint">(' + esc(data.contact_email) + ')</span>'; }
|
|
html += '</td></tr>';
|
|
html += '<tr><th>Zyklus</th><td>' + esc(data.cycle_key) + '</td></tr>';
|
|
html += '</table>';
|
|
html += '</div>';
|
|
|
|
html += '<form id="task-cert-mark-completed-form">';
|
|
html += '<input type="hidden" name="csrf_token" value="' + esc(data.csrf_token) + '">';
|
|
|
|
if (data.methods && data.methods.length > 0) {
|
|
html += '<div class="task-cert-dialog-methods">';
|
|
html += '<h4>Methoden</h4>';
|
|
html += '<p class="task-cert-hint">Einreichungen werden automatisch erstellt. Bei Formularen können Sie optional Antworten hinterlegen.</p>';
|
|
for (var i = 0; i < data.methods.length; i++) {
|
|
html += buildMethodHtml(data.methods[i]);
|
|
}
|
|
html += '</div>';
|
|
}
|
|
|
|
html += '<div class="task-cert-dialog-note">';
|
|
html += '<h4>Admin-Notiz</h4>';
|
|
html += '<textarea name="admin_note" rows="2" placeholder="Optionale Begründung (wird im Änderungsprotokoll gespeichert)"></textarea>';
|
|
html += '</div>';
|
|
|
|
html += '<div class="task-cert-dialog-actions">';
|
|
html += '<button type="button" class="btn task-cert-dialog-cancel">Abbrechen</button>';
|
|
html += '<button type="submit" class="btn task-cert-btn-success">Als erledigt markieren</button>';
|
|
html += '</div>';
|
|
|
|
html += '<div id="task-cert-dialog-error" class="task-cert-error" hidden></div>';
|
|
|
|
html += '</form>';
|
|
html += '</div>';
|
|
return html;
|
|
}
|
|
|
|
function collectFormData(data) {
|
|
var form = document.getElementById('task-cert-mark-completed-form');
|
|
if (!form) { return null; }
|
|
|
|
var adminNote = (form.querySelector('[name="admin_note"]') || {}).value || '';
|
|
var csrfToken = (form.querySelector('[name="csrf_token"]') || {}).value || '';
|
|
|
|
var methodPayloads = {};
|
|
var methods = data.methods || [];
|
|
for (var i = 0; i < methods.length; i++) {
|
|
var m = methods[i];
|
|
if (m.already_fulfilled || m.method_type !== 'form_submit') { continue; }
|
|
|
|
var fields = m.form_fields || [];
|
|
var payload = {};
|
|
for (var j = 0; j < fields.length; j++) {
|
|
var key = fields[j].field_key;
|
|
var name = 'method__' + m.id + '__' + key;
|
|
|
|
if (fields[j].field_type === 'checkbox') {
|
|
var checked = form.querySelectorAll('[name="' + name + '[]"]:checked');
|
|
var vals = [];
|
|
for (var k = 0; k < checked.length; k++) { vals.push(checked[k].value); }
|
|
payload[key] = vals;
|
|
} else {
|
|
var el = form.querySelector('[name="' + name + '"]');
|
|
payload[key] = el ? el.value : '';
|
|
}
|
|
}
|
|
methodPayloads[m.id] = payload;
|
|
}
|
|
|
|
return {
|
|
csrf_token: csrfToken,
|
|
task_id: taskId,
|
|
assignment_id: data.assignment_id,
|
|
admin_note: adminNote,
|
|
method: methodPayloads
|
|
};
|
|
}
|
|
|
|
function showError(msg) {
|
|
var errEl = document.getElementById('task-cert-dialog-error');
|
|
if (errEl) {
|
|
errEl.textContent = msg;
|
|
errEl.hidden = false;
|
|
}
|
|
}
|
|
|
|
function setLoading(on) {
|
|
var btn = dialog.querySelector('[type="submit"]');
|
|
if (btn) {
|
|
btn.disabled = on;
|
|
btn.textContent = on ? 'Wird gespeichert...' : 'Als erledigt markieren';
|
|
}
|
|
}
|
|
|
|
var currentData = null;
|
|
|
|
function openDialog(assignmentId) {
|
|
dialog.innerHTML = '<div class="task-cert-dialog-inner"><p>Daten werden geladen...</p></div>';
|
|
dialog.showModal();
|
|
|
|
fetch(methodsUrl + '?assignment_id=' + assignmentId, { credentials: 'same-origin' })
|
|
.then(function (res) { return res.json(); })
|
|
.then(function (data) {
|
|
if (data.error) {
|
|
dialog.innerHTML = '<div class="task-cert-dialog-inner"><div class="task-cert-error">' + esc(data.error) + '</div><div class="task-cert-dialog-actions"><button type="button" class="btn task-cert-dialog-cancel">Schließen</button></div></div>';
|
|
return;
|
|
}
|
|
currentData = data;
|
|
dialog.innerHTML = buildDialogContent(data);
|
|
})
|
|
.catch(function (err) {
|
|
dialog.innerHTML = '<div class="task-cert-dialog-inner"><div class="task-cert-error">Fehler: ' + esc(err.message) + '</div><div class="task-cert-dialog-actions"><button type="button" class="btn task-cert-dialog-cancel">Schließen</button></div></div>';
|
|
});
|
|
}
|
|
|
|
function submitDialog() {
|
|
if (!currentData) { return; }
|
|
|
|
var body = collectFormData(currentData);
|
|
if (!body) { return; }
|
|
|
|
setLoading(true);
|
|
|
|
fetch(submitUrl, {
|
|
method: 'POST',
|
|
credentials: 'same-origin',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(body)
|
|
})
|
|
.then(function (res) { return res.json(); })
|
|
.then(function (result) {
|
|
if (result.success) {
|
|
dialog.close();
|
|
if (window.TaskCertAdminActions && window.TaskCertAdminActions.showToast) {
|
|
window.TaskCertAdminActions.showToast('Zuweisung als erledigt markiert.', 'success');
|
|
}
|
|
setTimeout(function () { location.reload(); }, 800);
|
|
} else {
|
|
setLoading(false);
|
|
showError(result.error || 'Unbekannter Fehler');
|
|
}
|
|
})
|
|
.catch(function (err) {
|
|
setLoading(false);
|
|
showError('Netzwerkfehler: ' + err.message);
|
|
});
|
|
}
|
|
|
|
// Event delegation
|
|
document.addEventListener('click', function (e) {
|
|
var btn = e.target.closest('.task-cert-btn-mark-completed');
|
|
if (btn) {
|
|
e.preventDefault();
|
|
var aid = parseInt(btn.getAttribute('data-assignment-id') || '0', 10);
|
|
if (aid > 0) { openDialog(aid); }
|
|
return;
|
|
}
|
|
|
|
if (e.target.closest('.task-cert-dialog-close') || e.target.closest('.task-cert-dialog-cancel')) {
|
|
dialog.close();
|
|
return;
|
|
}
|
|
});
|
|
|
|
dialog.addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
submitDialog();
|
|
});
|
|
|
|
dialog.addEventListener('click', function (e) {
|
|
if (e.target === dialog) {
|
|
dialog.close();
|
|
}
|
|
});
|
|
})();
|