Module tasks + knowledgecenter_update aus Kundenprojekt übernehmen (bereinigt)
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>
This commit is contained in:
292
module/tasks/assets/js/task_cert_admin_mark_completed.js
Normal file
292
module/tasks/assets/js/task_cert_admin_mark_completed.js
Normal file
@@ -0,0 +1,292 @@
|
||||
(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();
|
||||
}
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user