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:
552
module/tasks/pages/admin/task_detail.php
Normal file
552
module/tasks/pages/admin/task_detail.php
Normal file
@@ -0,0 +1,552 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
if (!isset($task) && !isset($error)) {
|
||||
require_once dirname(dirname(__DIR__)) . '/actions/admin/task_detail_action.php';
|
||||
$viewData = task_cert_admin_task_detail_action();
|
||||
if (is_array($viewData)) {
|
||||
extract($viewData, EXTR_SKIP);
|
||||
}
|
||||
}
|
||||
|
||||
$error = $error ?? null;
|
||||
$task = $task ?? null;
|
||||
$dashboard = is_array($dashboard ?? null) ? $dashboard : [];
|
||||
|
||||
if (!function_exists('task_cert_render_styles')) {
|
||||
require_once dirname(dirname(__DIR__)) . '/bootstrap.php';
|
||||
}
|
||||
|
||||
task_cert_render_styles(['admin'], ['vendor/gridjs/mermaid.min.css']);
|
||||
task_cert_render_scripts([
|
||||
'vendor/gridjs/gridjs.umd.js',
|
||||
'js/task_cert_tabs.js',
|
||||
'js/task_cert_admin_overview_grid.js',
|
||||
'js/task_cert_admin_actions.js',
|
||||
'js/task_cert_admin_mark_completed.js',
|
||||
]);
|
||||
|
||||
$taskId = (int) ($task['id'] ?? 0);
|
||||
$taskTitle = (string) ($task['title'] ?? 'Übersicht');
|
||||
$taskListUrl = task_cert_view_url('admin.task_list');
|
||||
$taskEditUrl = $taskId > 0
|
||||
? task_cert_view_url('admin.task_form', ['id' => $taskId])
|
||||
: task_cert_view_url('admin.task_form');
|
||||
$taskDetailUrl = task_cert_view_url('admin.task_detail');
|
||||
$approvalDecisionUrl = task_cert_base_url() . '/endpoints/admin/approval_decision.php';
|
||||
$resetAssignmentUrl = task_cert_base_url() . '/endpoints/admin/reset_assignment_progress.php';
|
||||
$resetCycleUrl = task_cert_base_url() . '/endpoints/admin/reset_cycle_progress.php';
|
||||
$markCompletedMethodsUrl = task_cert_base_url() . '/endpoints/admin/mark_completed_methods.php';
|
||||
$markCompletedUrl = task_cert_base_url() . '/endpoints/admin/mark_completed.php';
|
||||
$csrfToken = (string) ($csrf_token ?? '');
|
||||
|
||||
$filters = is_array($dashboard['filters'] ?? null) ? $dashboard['filters'] : [];
|
||||
$cycleOptions = is_array($dashboard['cycle_options'] ?? null) ? $dashboard['cycle_options'] : [];
|
||||
$selectedCycleKey = (string) ($dashboard['selected_cycle_key'] ?? '');
|
||||
$cycleSelectionWarning = trim((string) ($dashboard['cycle_selection_warning'] ?? ''));
|
||||
$activeTabKey = (string) ($dashboard['active_tab_key'] ?? 'overview');
|
||||
$showCycleSelector = (bool) ($dashboard['show_cycle_selector'] ?? true);
|
||||
$exportMode = (string) ($dashboard['export_mode'] ?? 'cycle');
|
||||
$canExportAnswers = (bool) ($dashboard['can_export_answers'] ?? false);
|
||||
|
||||
$tabIndexMap = [
|
||||
'overview' => '0',
|
||||
'approvals' => '1',
|
||||
'answers' => '2',
|
||||
// Backward-Kompatibilität: alte Tab-Keys zeigen auf Übersicht
|
||||
'todo' => '0',
|
||||
'completed' => '0',
|
||||
'overdue' => '0',
|
||||
];
|
||||
|
||||
// Status-Vorfilter aus altem Tab-Key ableiten
|
||||
$initialStatusFilter = '';
|
||||
if ($activeTabKey === 'todo') {
|
||||
$initialStatusFilter = 'open';
|
||||
} elseif ($activeTabKey === 'completed') {
|
||||
$initialStatusFilter = 'completed';
|
||||
} elseif ($activeTabKey === 'overdue') {
|
||||
$initialStatusFilter = 'overdue';
|
||||
}
|
||||
$activeTabIndex = $tabIndexMap[$activeTabKey] ?? '0';
|
||||
|
||||
$kpis = is_array($dashboard['kpis'] ?? null) ? $dashboard['kpis'] : [];
|
||||
$methodStats = is_array($dashboard['method_stats'] ?? null) ? $dashboard['method_stats'] : [];
|
||||
$todoRows = is_array($dashboard['todo_rows'] ?? null) ? $dashboard['todo_rows'] : [];
|
||||
$completedRows = is_array($dashboard['completed_rows'] ?? null) ? $dashboard['completed_rows'] : [];
|
||||
$approvalRows = is_array($dashboard['approval_rows'] ?? null) ? $dashboard['approval_rows'] : [];
|
||||
$overdueRows = is_array($dashboard['overdue_rows'] ?? null) ? $dashboard['overdue_rows'] : [];
|
||||
$submissionRows = is_array($dashboard['submission_rows'] ?? null) ? $dashboard['submission_rows'] : [];
|
||||
|
||||
// Zusammengeführte Zeilen für die Übersicht-Grid-Tabelle
|
||||
$mergedRows = [];
|
||||
$seenAssignmentIds = [];
|
||||
$todoAssignmentIds = [];
|
||||
|
||||
foreach ($todoRows as $row) {
|
||||
$assignmentId = (int) ($row['id'] ?? 0);
|
||||
if ($assignmentId > 0 && isset($seenAssignmentIds[$assignmentId])) {
|
||||
continue;
|
||||
}
|
||||
$seenAssignmentIds[$assignmentId] = true;
|
||||
$todoAssignmentIds[$assignmentId] = true;
|
||||
|
||||
$graceUntilRaw = trim((string) ($row['grace_until'] ?? ''));
|
||||
$graceBadgeHtml = '';
|
||||
if ($graceUntilRaw !== '') {
|
||||
try {
|
||||
$graceUntilDt = new DateTimeImmutable($graceUntilRaw);
|
||||
if ((new DateTimeImmutable()) < $graceUntilDt) {
|
||||
$graceBadgeHtml = '<span class="task-cert-grace-badge">Karenz bis ' . TaskCertView::e($graceUntilDt->format('d.m.Y')) . '</span>';
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
$todoActionHtml = '';
|
||||
if ($assignmentId > 0) {
|
||||
$todoActionHtml = '<button type="button" class="btn task-cert-btn-success task-cert-btn-sm task-cert-btn-mark-completed" data-assignment-id="' . $assignmentId . '">Erledigt markieren</button>';
|
||||
}
|
||||
|
||||
$mergedRows[] = [
|
||||
'contact_name' => (string) ($row['contact_name'] ?? '-'),
|
||||
'contact_email' => (string) ($row['contact_email'] ?? ''),
|
||||
'_row_group' => 'todo',
|
||||
'_row_group_label' => 'Offen',
|
||||
'status' => (string) ($row['status'] ?? ''),
|
||||
'status_label' => '',
|
||||
'assigned_at_sort' => (string) ($row['created_at'] ?? ''),
|
||||
'due_at_sort' => (string) ($row['due_at'] ?? ''),
|
||||
'completed_at_sort' => null,
|
||||
'overdue_at_sort' => (string) ($row['overdue_at'] ?? ''),
|
||||
'escalation_level' => (int) ($row['escalation_level'] ?? 0),
|
||||
'grace_badge_html' => $graceBadgeHtml,
|
||||
'last_activity_sort' => (string) ($row['last_submitted_at'] ?? ''),
|
||||
'certificate_label' => null,
|
||||
'certificate_code' => null,
|
||||
'valid_until_sort' => null,
|
||||
'cycle_key' => (string) ($row['cycle_key'] ?? ''),
|
||||
'_row_html_action' => $todoActionHtml,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($completedRows as $row) {
|
||||
$completedAssignmentId = (int) ($row['id'] ?? 0);
|
||||
if ($completedAssignmentId > 0 && isset($seenAssignmentIds[$completedAssignmentId])) {
|
||||
continue;
|
||||
}
|
||||
if ($completedAssignmentId > 0) {
|
||||
$seenAssignmentIds[$completedAssignmentId] = true;
|
||||
}
|
||||
$certStatus = (string) ($row['certificate_status'] ?? '');
|
||||
|
||||
$actionHtml = '';
|
||||
if ($completedAssignmentId > 0) {
|
||||
$actionHtml = '<button type="button" class="btn task-cert-btn-danger task-cert-btn-sm task-cert-btn-reset-assignment" data-assignment-id="' . $completedAssignmentId . '">Zurücksetzen</button>';
|
||||
}
|
||||
|
||||
$mergedRows[] = [
|
||||
'contact_name' => (string) ($row['contact_name'] ?? '-'),
|
||||
'contact_email' => (string) ($row['contact_email'] ?? ''),
|
||||
'_row_group' => 'completed',
|
||||
'_row_group_label' => 'Erledigt',
|
||||
'status' => 'completed',
|
||||
'status_label' => '',
|
||||
'assigned_at_sort' => null,
|
||||
'due_at_sort' => null,
|
||||
'completed_at_sort' => (string) ($row['completed_at'] ?? ''),
|
||||
'overdue_at_sort' => null,
|
||||
'escalation_level' => null,
|
||||
'grace_badge_html' => '',
|
||||
'last_activity_sort' => null,
|
||||
'certificate_label' => '',
|
||||
'certificate_code' => (string) ($row['certificate_code'] ?? ''),
|
||||
'valid_until_sort' => (string) ($row['certificate_valid_until'] ?? ''),
|
||||
'cycle_key' => (string) ($row['cycle_key'] ?? ''),
|
||||
'_row_html_action' => $actionHtml,
|
||||
];
|
||||
|
||||
// Zertifikat-Label setzen
|
||||
$lastIdx = count($mergedRows) - 1;
|
||||
if ((int) ($row['certificate_id'] ?? 0) > 0) {
|
||||
$mergedRows[$lastIdx]['certificate_label'] = $certStatus;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($overdueRows as $row) {
|
||||
$assignmentId = (int) ($row['id'] ?? 0);
|
||||
if ($assignmentId > 0 && isset($seenAssignmentIds[$assignmentId])) {
|
||||
continue; // Bereits in Todo- oder Completed-Zeilen enthalten
|
||||
}
|
||||
if ($assignmentId > 0) {
|
||||
$seenAssignmentIds[$assignmentId] = true;
|
||||
}
|
||||
|
||||
$overdueActionHtml = '';
|
||||
if ($assignmentId > 0) {
|
||||
$overdueActionHtml = '<button type="button" class="btn task-cert-btn-success task-cert-btn-sm task-cert-btn-mark-completed" data-assignment-id="' . $assignmentId . '">Erledigt markieren</button>';
|
||||
}
|
||||
|
||||
$mergedRows[] = [
|
||||
'contact_name' => (string) ($row['contact_name'] ?? '-'),
|
||||
'contact_email' => (string) ($row['contact_email'] ?? ''),
|
||||
'_row_group' => 'overdue',
|
||||
'_row_group_label' => 'Überfällig',
|
||||
'status' => (string) ($row['status'] ?? ''),
|
||||
'status_label' => '',
|
||||
'assigned_at_sort' => null,
|
||||
'due_at_sort' => (string) ($row['due_at'] ?? ''),
|
||||
'completed_at_sort' => null,
|
||||
'overdue_at_sort' => (string) ($row['overdue_at'] ?? ''),
|
||||
'escalation_level' => (int) ($row['escalation_level'] ?? 0),
|
||||
'grace_badge_html' => '',
|
||||
'last_activity_sort' => (string) ($row['last_submitted_at'] ?? ''),
|
||||
'certificate_label' => null,
|
||||
'certificate_code' => null,
|
||||
'valid_until_sort' => null,
|
||||
'cycle_key' => '',
|
||||
'_row_html_action' => $overdueActionHtml,
|
||||
];
|
||||
}
|
||||
|
||||
$statusLabelMap = TaskCertConstants::assignmentStatusLabels();
|
||||
$methodLabelMap = TaskCertConstants::methodLabels();
|
||||
$submissionStatusLabelMap = TaskCertConstants::submissionStatusLabels();
|
||||
$certificateStatusLabelMap = TaskCertConstants::certificateStatusLabels();
|
||||
|
||||
// Status- und Zertifikat-Labels in die mergedRows einfügen
|
||||
foreach ($mergedRows as &$mergedRow) {
|
||||
$s = $mergedRow['status'] ?? '';
|
||||
if ($mergedRow['status_label'] === '' && $s !== '') {
|
||||
$mergedRow['status_label'] = $statusLabelMap[$s] ?? $s;
|
||||
}
|
||||
$cl = $mergedRow['certificate_label'] ?? null;
|
||||
if ($cl !== null && $cl !== '' && isset($certificateStatusLabelMap[$cl])) {
|
||||
$mergedRow['certificate_label'] = $certificateStatusLabelMap[$cl];
|
||||
}
|
||||
}
|
||||
unset($mergedRow);
|
||||
|
||||
$mergedRowsJson = json_encode($mergedRows, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE);
|
||||
|
||||
// Antworten-Zeilen für Grid.js aufbereiten
|
||||
$downloadFileUrl = task_cert_base_url() . '/endpoints/admin/download_submission_file.php';
|
||||
$answersGridRows = [];
|
||||
foreach ($submissionRows as $subRow) {
|
||||
$subStatus = (string) ($subRow['status'] ?? '');
|
||||
$methodType = (string) ($subRow['method_type'] ?? '');
|
||||
$answerItems = is_array($subRow['answer_items'] ?? null) ? $subRow['answer_items'] : [];
|
||||
|
||||
$detailHtml = '';
|
||||
if ($answerItems !== []) {
|
||||
$detailHtml = '<div class="task-cert-monitoring-answer-items">';
|
||||
foreach ($answerItems as $item) {
|
||||
$detailHtml .= '<div><strong>' . TaskCertView::e((string) ($item['label'] ?? '')) . ':</strong> ';
|
||||
|
||||
$itemFiles = is_array($item['files'] ?? null) ? $item['files'] : [];
|
||||
if ($itemFiles !== []) {
|
||||
$links = [];
|
||||
foreach ($itemFiles as $file) {
|
||||
$url = $downloadFileUrl . '?' . http_build_query([
|
||||
'submission_id' => (int) ($file['submission_id'] ?? 0),
|
||||
'field_key' => (string) ($file['field_key'] ?? ''),
|
||||
'index' => (int) ($file['index'] ?? 0),
|
||||
'csrf_token' => $csrfToken,
|
||||
]);
|
||||
$links[] = '<a href="' . TaskCertView::e($url) . '" target="_blank" rel="noopener">'
|
||||
. TaskCertView::e((string) ($file['name'] ?? 'Datei')) . '</a>';
|
||||
}
|
||||
$detailHtml .= implode(', ', $links);
|
||||
} else {
|
||||
$detailHtml .= TaskCertView::e((string) ($item['value'] ?? ''));
|
||||
}
|
||||
|
||||
$detailHtml .= '</div>';
|
||||
}
|
||||
$detailHtml .= '</div>';
|
||||
}
|
||||
|
||||
$score = null;
|
||||
if ($subRow['score'] !== null && $subRow['max_score'] !== null) {
|
||||
$score = (int) $subRow['score'] . '/' . (int) $subRow['max_score'];
|
||||
}
|
||||
|
||||
// answer_items als Key-Value-Map für CSV-Export
|
||||
$answerMap = [];
|
||||
foreach ($answerItems as $item) {
|
||||
$label = (string) ($item['label'] ?? '');
|
||||
if ($label !== '') {
|
||||
$answerMap[$label] = (string) ($item['value'] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
$answersGridRows[] = [
|
||||
'contact_name' => (string) ($subRow['contact_name'] ?? '-'),
|
||||
'contact_email' => (string) ($subRow['contact_email'] ?? ''),
|
||||
'method_label' => (string) ($subRow['method_label'] ?? ($methodLabelMap[$methodType] ?? $methodType)),
|
||||
'status_label' => $submissionStatusLabelMap[$subStatus] ?? $subStatus,
|
||||
'submitted_at_sort' => (string) ($subRow['submitted_at'] ?? ''),
|
||||
'score' => $score,
|
||||
'score_num' => $subRow['score'] !== null ? (int) $subRow['score'] : null,
|
||||
'detail_html' => $detailHtml,
|
||||
'answer_fields' => $answerMap,
|
||||
];
|
||||
}
|
||||
$answersGridRowsJson = json_encode($answersGridRows, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE);
|
||||
|
||||
$formatDateTime = static function ($value): string {
|
||||
$value = trim((string) $value);
|
||||
if ($value === '') {
|
||||
return '-';
|
||||
}
|
||||
|
||||
try {
|
||||
return (new DateTimeImmutable($value))->format('d.m.Y H:i');
|
||||
} catch (Throwable $throwable) {
|
||||
return $value;
|
||||
}
|
||||
};
|
||||
|
||||
$hasCycle = (bool) ($dashboard['has_cycle'] ?? ($selectedCycleKey !== ''));
|
||||
$noCycleReason = trim((string) ($dashboard['no_cycle_reason'] ?? ''));
|
||||
if ($noCycleReason === '') {
|
||||
$noCycleReason = 'Aktuell ist kein Zyklus verfügbar.';
|
||||
}
|
||||
|
||||
$exportAnswersUrl = '';
|
||||
if ($taskId > 0 && $canExportAnswers) {
|
||||
$exportQuery = [
|
||||
'id' => $taskId,
|
||||
'mode' => $exportMode,
|
||||
'csrf_token' => $csrfToken,
|
||||
];
|
||||
if ($exportMode === 'cycle' && $selectedCycleKey !== '') {
|
||||
$exportQuery['cycle_key'] = $selectedCycleKey;
|
||||
}
|
||||
$exportAnswersUrl = task_cert_base_url() . '/endpoints/admin/export_answers_csv.php?' . http_build_query($exportQuery);
|
||||
}
|
||||
?>
|
||||
<div
|
||||
class="task-cert task-cert-admin-detail task-cert-admin-monitoring"
|
||||
data-task-cert-tabs
|
||||
data-tabs-active="<?php echo TaskCertView::e($activeTabIndex); ?>"
|
||||
data-csrf-token="<?php echo TaskCertView::e($csrfToken); ?>"
|
||||
data-reset-assignment-url="<?php echo TaskCertView::e($resetAssignmentUrl); ?>"
|
||||
data-reset-cycle-url="<?php echo TaskCertView::e($resetCycleUrl); ?>"
|
||||
data-approval-url="<?php echo TaskCertView::e($approvalDecisionUrl); ?>"
|
||||
data-task-id="<?php echo (int) $taskId; ?>"
|
||||
>
|
||||
<div id="task-cert-toast-container" class="task-cert-toast-container" aria-live="polite"></div>
|
||||
|
||||
<div class="task-cert-form-header">
|
||||
<div class="task-cert-title-wrap">
|
||||
<a
|
||||
href="<?php echo TaskCertView::e($taskListUrl); ?>"
|
||||
class="task-cert-back-link"
|
||||
aria-label="Zur Aufgabenliste"
|
||||
title="Zur Aufgabenliste"
|
||||
>←</a>
|
||||
<h2><?php echo TaskCertView::e($taskTitle); ?></h2>
|
||||
<?php
|
||||
$frequencyType = (string) ($task['frequency_type'] ?? '');
|
||||
$recurrenceBasis = (string) ($task['recurrence_basis'] ?? '');
|
||||
$recurrenceUnit = (string) ($task['recurrence_unit'] ?? '');
|
||||
$isCompletionBased = $frequencyType === 'recurring' && $recurrenceBasis === 'completion';
|
||||
$isCalendarBased = $frequencyType === 'recurring' && $recurrenceBasis !== 'completion';
|
||||
$isOneTime = $frequencyType !== 'recurring';
|
||||
$unitLabels = ['day' => 'Täglich', 'week' => 'Wöchentlich', 'month' => 'Monatlich', 'year' => 'Jährlich'];
|
||||
?>
|
||||
<?php if ($isCompletionBased): ?>
|
||||
<span class="task-cert-recurrence-badge is-completion"><?php echo TaskCertView::e($unitLabels[$recurrenceUnit] ?? 'Wiederkehrend'); ?> (erledigungsbasiert)</span>
|
||||
<?php elseif ($isCalendarBased): ?>
|
||||
<span class="task-cert-recurrence-badge is-calendar"><?php echo TaskCertView::e($unitLabels[$recurrenceUnit] ?? 'Wiederkehrend'); ?> (kalenderbasiert)</span>
|
||||
<?php else: ?>
|
||||
<span class="task-cert-recurrence-badge is-onetime">Einmalig</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="task-cert-toolbar-right">
|
||||
<?php if ($showCycleSelector && $cycleOptions !== []): ?>
|
||||
<form action="<?php echo TaskCertView::e($taskDetailUrl); ?>" method="get" class="task-cert-cycle-switch-inline" data-cycle-switch-form>
|
||||
<input type="hidden" name="id" value="<?php echo (int) $taskId; ?>">
|
||||
<input type="hidden" name="tab" value="<?php echo TaskCertView::e($activeTabKey); ?>" data-tabs-active-input>
|
||||
<select name="cycle_key" onchange="this.form.submit()">
|
||||
<?php foreach ($cycleOptions as $option): ?>
|
||||
<?php $cycleKey = (string) ($option['cycle_key'] ?? ''); ?>
|
||||
<?php if ($cycleKey === '') { continue; } ?>
|
||||
<option value="<?php echo TaskCertView::e($cycleKey); ?>" <?php echo $cycleKey === $selectedCycleKey ? 'selected' : ''; ?>>
|
||||
<?php echo TaskCertView::e((string) ($option['label'] ?? $cycleKey)); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<a href="<?php echo TaskCertView::e($taskListUrl); ?>" class="btn">Zur Liste</a>
|
||||
<?php if ($taskId > 0): ?>
|
||||
<a href="<?php echo TaskCertView::e($taskEditUrl); ?>" class="btn">Bearbeiten</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="task-cert-error"><?php echo TaskCertView::e($error); ?></div>
|
||||
<?php elseif ($task): ?>
|
||||
<?php if ($cycleSelectionWarning !== ''): ?>
|
||||
<div class="task-cert-warning"><?php echo TaskCertView::e($cycleSelectionWarning); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($hasCycle): ?>
|
||||
<div class="task-cert-wizard-tabs" role="tablist" aria-label="Reiter-Navigation">
|
||||
<button type="button" class="task-cert-wizard-tab" data-tabs-tab="0" data-tab-key="overview" role="tab">Übersicht</button>
|
||||
<button type="button" class="task-cert-wizard-tab" data-tabs-tab="1" data-tab-key="approvals" role="tab">Freigaben</button>
|
||||
<button type="button" class="task-cert-wizard-tab" data-tabs-tab="2" data-tab-key="answers" role="tab">Antworten</button>
|
||||
</div>
|
||||
|
||||
<!-- Panel 0: Übersicht (KPIs + zusammengeführte Tabelle) -->
|
||||
<section class="task-cert-wizard-step" data-tabs-panel="0" hidden>
|
||||
<?php
|
||||
$totalAssigned = (int) ($kpis['total_assigned'] ?? 0);
|
||||
$completedCount = (int) ($kpis['completed_count'] ?? 0);
|
||||
$completionRate = $totalAssigned > 0 ? round(($completedCount / $totalAssigned) * 100, 1) : 0;
|
||||
$overdueCount = (int) ($kpis['overdue_count'] ?? 0);
|
||||
$approvalCount = (int) ($kpis['awaiting_approval_count'] ?? 0);
|
||||
$escalatedCount = (int) ($kpis['escalated_count'] ?? 0);
|
||||
?>
|
||||
|
||||
<div class="task-cert-kpi-tiles">
|
||||
<div class="task-cert-kpi-tile">
|
||||
<span class="task-cert-kpi-value"><?php echo $completedCount; ?> / <?php echo $totalAssigned; ?></span>
|
||||
<span class="task-cert-kpi-label">Abgeschlossen (<?php echo TaskCertView::e((string) $completionRate); ?>%)</span>
|
||||
</div>
|
||||
<div class="task-cert-kpi-tile">
|
||||
<span class="task-cert-kpi-value"><?php echo $overdueCount; ?></span>
|
||||
<span class="task-cert-kpi-label">Überfällig</span>
|
||||
</div>
|
||||
<div class="task-cert-kpi-tile">
|
||||
<span class="task-cert-kpi-value"><?php echo $approvalCount; ?></span>
|
||||
<span class="task-cert-kpi-label">Freigaben offen</span>
|
||||
</div>
|
||||
<div class="task-cert-kpi-tile">
|
||||
<span class="task-cert-kpi-value"><?php echo $escalatedCount; ?></span>
|
||||
<span class="task-cert-kpi-label">Eskaliert</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="task-cert-overview-grid-toolbar">
|
||||
<input type="text" id="task-cert-overview-search" placeholder="Suchen..." autocomplete="off">
|
||||
|
||||
<select id="task-cert-overview-status-filter">
|
||||
<option value="">Alle Status</option>
|
||||
<?php foreach ($statusLabelMap as $statusKey => $statusLabel): ?>
|
||||
<option value="<?php echo TaskCertView::e($statusKey); ?>"><?php echo TaskCertView::e($statusLabel); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<button type="button" id="task-cert-overview-export" class="btn">CSV Export</button>
|
||||
|
||||
<?php if ($completedRows !== []): ?>
|
||||
<button
|
||||
type="button"
|
||||
class="btn task-cert-btn-danger task-cert-btn-reset-cycle"
|
||||
data-task-id="<?php echo (int) $taskId; ?>"
|
||||
>Alle im Zyklus zurücksetzen</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="task-cert-overview-grid"
|
||||
class="task-cert-table-wrap"
|
||||
data-initial-status-filter="<?php echo TaskCertView::e($initialStatusFilter); ?>"
|
||||
></div>
|
||||
|
||||
<script type="application/json" id="task-cert-overview-grid-data"><?php echo $mergedRowsJson; ?></script>
|
||||
</section>
|
||||
|
||||
<!-- Panel 1: Freigaben (unverändert) -->
|
||||
<section class="task-cert-wizard-step" data-tabs-panel="1" hidden>
|
||||
<div class="task-cert-block">
|
||||
<h3>Freigaben</h3>
|
||||
<div class="task-cert-table-wrap">
|
||||
<table class="task-cert-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Mitarbeiter</th>
|
||||
<th>Methode</th>
|
||||
<th>Einreichungsstatus</th>
|
||||
<th>Eingereicht am</th>
|
||||
<th>Zuweisungsstatus</th>
|
||||
<th>Fällig am</th>
|
||||
<th>Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($approvalRows as $row): ?>
|
||||
<?php
|
||||
$submissionStatus = (string) ($row['status'] ?? '');
|
||||
$assignmentStatus = (string) ($row['assignment_status'] ?? '');
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<?php echo TaskCertView::e((string) ($row['contact_name'] ?? '-')); ?><br>
|
||||
<small><?php echo TaskCertView::e((string) ($row['contact_email'] ?? '')); ?></small>
|
||||
</td>
|
||||
<?php $methodType = (string) ($row['method_type'] ?? ''); ?>
|
||||
<td><?php echo TaskCertView::e($methodLabelMap[$methodType] ?? $methodType); ?></td>
|
||||
<td><?php echo TaskCertView::e($submissionStatusLabelMap[$submissionStatus] ?? $submissionStatus); ?></td>
|
||||
<td><?php echo TaskCertView::e($formatDateTime($row['submitted_at'] ?? '')); ?></td>
|
||||
<td><span class="task-cert-status-badge status-<?php echo TaskCertView::e($assignmentStatus); ?>"><?php echo TaskCertView::e($statusLabelMap[$assignmentStatus] ?? $assignmentStatus); ?></span></td>
|
||||
<td><?php echo TaskCertView::e($formatDateTime($row['due_at'] ?? '')); ?></td>
|
||||
<td>
|
||||
<?php if ((int) ($row['id'] ?? 0) > 0): ?>
|
||||
<div
|
||||
class="task-cert-inline-approval-form"
|
||||
data-submission-id="<?php echo (int) ($row['id'] ?? 0); ?>"
|
||||
>
|
||||
<textarea class="task-cert-approval-note" rows="2" placeholder="Notiz (optional)"></textarea>
|
||||
<div class="task-cert-inline-approval-actions">
|
||||
<button type="button" class="btn task-cert-btn-success task-cert-btn-approval" data-decision="approve">Freigeben</button>
|
||||
<button type="button" class="btn task-cert-btn-danger task-cert-btn-approval" data-decision="reject">Ablehnen</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
-
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php if ($approvalRows === []): ?>
|
||||
<tr>
|
||||
<td colspan="7">Keine Freigaben im aktuellen Filter.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Panel 2: Antworten -->
|
||||
<section class="task-cert-wizard-step" data-tabs-panel="2" hidden>
|
||||
<div class="task-cert-overview-grid-toolbar">
|
||||
<input type="text" id="task-cert-answers-search" placeholder="Suchen..." autocomplete="off">
|
||||
<button type="button" id="task-cert-answers-export" class="btn">CSV Export</button>
|
||||
</div>
|
||||
|
||||
<div id="task-cert-answers-grid" class="task-cert-table-wrap"></div>
|
||||
<script type="application/json" id="task-cert-answers-grid-data"><?php echo $answersGridRowsJson; ?></script>
|
||||
</section>
|
||||
<?php else: ?>
|
||||
<div class="task-cert-warning"><?php echo TaskCertView::e($noCycleReason); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<dialog id="task-cert-mark-completed-dialog" class="task-cert-dialog"></dialog>
|
||||
<div
|
||||
id="task-cert-mark-completed-config"
|
||||
hidden
|
||||
data-methods-url="<?php echo TaskCertView::e($markCompletedMethodsUrl); ?>"
|
||||
data-submit-url="<?php echo TaskCertView::e($markCompletedUrl); ?>"
|
||||
data-task-id="<?php echo $taskId; ?>"
|
||||
></div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user