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 = 'Karenz bis ' . TaskCertView::e($graceUntilDt->format('d.m.Y')) . ''; } } catch (Throwable $e) { // ignore } } $todoActionHtml = ''; if ($assignmentId > 0) { $todoActionHtml = ''; } $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 = ''; } $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 = ''; } $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 = '
'; foreach ($answerItems as $item) { $detailHtml .= '
' . TaskCertView::e((string) ($item['label'] ?? '')) . ': '; $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[] = '' . TaskCertView::e((string) ($file['name'] ?? 'Datei')) . ''; } $detailHtml .= implode(', ', $links); } else { $detailHtml .= TaskCertView::e((string) ($item['value'] ?? '')); } $detailHtml .= '
'; } $detailHtml .= '
'; } $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); } ?>

'Täglich', 'week' => 'Wöchentlich', 'month' => 'Monatlich', 'year' => 'Jährlich']; ?> (erledigungsbasiert) (kalenderbasiert) Einmalig