Files
awo-hamburg-intranet/module/tasks/pages/user/task_detail.php
Moritz Weinmann 743476f64a 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>
2026-06-22 09:20:02 +02:00

734 lines
48 KiB
PHP

<?php
declare(strict_types=1);
if (!isset($assignment) && !isset($task) && !isset($error)) {
require_once dirname(dirname(__DIR__)) . '/actions/user/task_detail_action.php';
$viewData = task_cert_user_task_detail_action();
if (is_array($viewData)) {
extract($viewData, EXTR_SKIP);
}
}
$error = $error ?? null;
$assignment = $assignment ?? null;
$task = $task ?? null;
$methods = $methods ?? [];
$submissions = $submissions ?? [];
$taskCertificatesHistory = is_array($task_certificates_history ?? null) ? $task_certificates_history : [];
$taskResponsibles = is_array($responsibles ?? null) ? $responsibles : [];
$contactData = is_array($contact_data ?? null) ? $contact_data : [];
$csrfToken = $csrf_token ?? '';
$previewMode = $previewMode ?? false;
$inlineError = trim(TaskCertRequest::queryString('task_cert_error', ''));
$inlineWarning = trim(TaskCertRequest::queryString('task_cert_warning', ''));
$inlineNotice = trim(TaskCertRequest::queryString('task_cert_notice', ''));
$statusLabels = TaskCertConstants::assignmentStatusLabels();
$methodLabels = TaskCertConstants::methodLabels();
$submissionStatusLabels = TaskCertConstants::submissionStatusLabels();
$certificateStatusLabels = TaskCertConstants::certificateStatusLabels();
if (!function_exists('task_cert_render_styles')) {
require_once dirname(dirname(__DIR__)) . '/bootstrap.php';
}
task_cert_render_styles(['user']);
$taskCertBaseUrl = task_cert_base_url();
$myTasksUrl = task_cert_view_url('user.my_tasks');
$taskDetailViewUrl = $assignment
? task_cert_view_url('user.task_detail', ['assignment_id' => (int) $assignment['id']])
: task_cert_view_url('user.task_detail');
$wikiBasePath = '/' . trim(task_cert_site_code(), '/') . '/' . trim(task_cert_language_code(), '/') . '/wiki/';
$buildWikiPostUrl = static function (int $postId) use ($wikiBasePath): string {
if ($postId <= 0) {
return '';
}
return $wikiBasePath . '?action=Post&id=' . $postId . '&embedded=1';
};
$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;
}
};
$formatDueMessage = static function ($dueRaw): string {
$dueRaw = trim((string) $dueRaw);
if ($dueRaw === '') {
return 'Kein Fälligkeitsdatum hinterlegt.';
}
try {
$dueAt = new DateTimeImmutable($dueRaw);
$now = new DateTimeImmutable();
$days = (int) floor(($dueAt->getTimestamp() - $now->getTimestamp()) / 86400);
if ($days === 0) {
return 'Heute fällig (' . $dueAt->format('d.m.Y H:i') . ')';
}
if ($days > 0) {
return 'Fällig in ' . $days . ' Tag' . ($days === 1 ? '' : 'en') . ' (' . $dueAt->format('d.m.Y H:i') . ')';
}
$overdueDays = abs($days);
return 'Seit ' . $overdueDays . ' Tag' . ($overdueDays === 1 ? '' : 'en') . ' überfällig (' . $dueAt->format('d.m.Y H:i') . ')';
} catch (Throwable $throwable) {
return $dueRaw;
}
};
$formFieldLabelsByMethod = [];
foreach ($methods as $m) {
$mId = (int) ($m['id'] ?? 0);
if ((string) ($m['method_type'] ?? '') !== 'form_submit' || $mId <= 0) {
continue;
}
$map = [];
foreach ((array) ($m['form_fields'] ?? []) as $ff) {
$fk = trim((string) ($ff['field_key'] ?? ''));
if ($fk !== '') {
$map[$fk] = trim((string) ($ff['label'] ?? $fk));
}
}
$formFieldLabelsByMethod[$mId] = $map;
}
$latestSubmissionByMethod = [];
$allSubmissionsByMethod = [];
foreach ($submissions as $submission) {
$methodId = (int) ($submission['method_id'] ?? 0);
if ($methodId <= 0) {
continue;
}
if (!isset($latestSubmissionByMethod[$methodId])) {
$latestSubmissionByMethod[$methodId] = $submission;
}
$allSubmissionsByMethod[$methodId][] = $submission;
}
$resolveMethodProgress = static function (int $methodId) use ($latestSubmissionByMethod, $submissionStatusLabels): array {
$latest = $latestSubmissionByMethod[$methodId] ?? null;
if (!is_array($latest)) {
return [
'state' => 'open',
'label' => 'Noch offen',
'note' => 'Bitte diesen Schritt ausführen.',
];
}
$status = (string) ($latest['status'] ?? '');
if (in_array($status, ['auto_passed', 'approved'], true)) {
return [
'state' => 'done',
'label' => 'Erledigt',
'note' => 'Zuletzt am ' . ($latest['submitted_at'] ?? '-') . ' eingereicht.',
];
}
if ($status === 'awaiting_approval') {
return [
'state' => 'pending',
'label' => 'Warten auf Freigabe',
'note' => 'Ihre Einreichung wird geprüft.',
];
}
if ($status === 'rejected') {
return [
'state' => 'retry',
'label' => 'Erneut erforderlich',
'note' => 'Bitte Schritt erneut bearbeiten und abschicken.',
];
}
return [
'state' => 'open',
'label' => $submissionStatusLabels[$status] ?? 'Offen',
'note' => 'Bitte diesen Schritt abschließen.',
];
};
$isMethodCompleted = static function (int $methodId) use ($latestSubmissionByMethod): bool {
$latest = $latestSubmissionByMethod[$methodId] ?? null;
if (!is_array($latest)) {
return false;
}
$status = (string) ($latest['status'] ?? '');
return in_array($status, ['auto_passed', 'approved'], true);
};
$totalRequiredSteps = 0;
$completedRequiredSteps = 0;
foreach ($methods as $method) {
if ((int) ($method['is_required'] ?? 1) !== 1) {
continue;
}
$totalRequiredSteps++;
if ($isMethodCompleted((int) ($method['id'] ?? 0))) {
$completedRequiredSteps++;
}
}
$remainingRequiredSteps = max(0, $totalRequiredSteps - $completedRequiredSteps);
$methodCount = count($methods);
?>
<div class="task-cert task-cert-user-detail">
<?php if ($error): ?>
<div class="task-cert-error"><?php echo TaskCertView::e($error); ?></div>
<?php elseif ($assignment && $task): ?>
<?php if ($inlineError !== ''): ?>
<div class="task-cert-error"><?php echo TaskCertView::e($inlineError); ?></div>
<?php endif; ?>
<?php if ($inlineWarning !== ''): ?>
<div class="task-cert-warning"><?php echo TaskCertView::e($inlineWarning); ?></div>
<?php endif; ?>
<?php if ($inlineNotice !== ''): ?>
<div class="task-cert-success"><?php echo TaskCertView::e($inlineNotice); ?></div>
<?php endif; ?>
<?php $assignmentStatus = (string) ($assignment['status'] ?? ''); ?>
<?php $isAssignmentLocked = in_array($assignmentStatus, ['completed', 'expired'], true); ?>
<?php if ($previewMode): ?>
<div class="task-cert-warning task-cert-preview-banner">
Vorschau &ndash; so sehen Mitarbeiter diese Aufgabe. Eingaben sind hier deaktiviert.
</div>
<?php endif; ?>
<div class="task-cert-user-detail-header">
<div class="task-cert-user-title-row">
<a href="<?php echo TaskCertView::e($myTasksUrl); ?>" class="task-cert-back-link" aria-label="Zurück zu meinen Aufgaben" title="Zurück zu meinen Aufgaben">&larr;</a>
<h2><?php echo TaskCertView::e((string) ($task['title'] ?? 'Aufgabe')); ?></h2>
</div>
</div>
<div class="task-cert-user-layout">
<main class="task-cert-user-main">
<?php if (trim((string) ($task['description'] ?? '')) !== ''): ?>
<div class="task-cert-block">
<h3>Das müssen Sie tun</h3>
<div class="task-cert-description-content"><?php
$descContent = (string) ($task['description'] ?? '');
if (TaskCertHtml::isHtml($descContent)) {
// HTML content from Quill — already sanitized on save
echo TaskCertHtml::sanitize($descContent);
} else {
// Legacy plain text
echo nl2br(TaskCertView::e($descContent));
}
?></div>
</div>
<?php endif; ?>
<div class="task-cert-block">
<h3>So erledigen Sie die Aufgabe</h3>
<?php foreach ($methods as $index => $method): ?>
<?php
$methodId = (int) ($method['id'] ?? 0);
$methodType = (string) ($method['method_type'] ?? '');
$methodTitle = $methodLabels[$methodType] ?? $methodType;
$progress = $resolveMethodProgress($methodId);
$stepTitle = $methodCount > 1
? ('Schritt ' . ((int) $index + 1) . ': ' . $methodTitle)
: $methodTitle;
?>
<section class="task-cert-user-step">
<header class="task-cert-user-step-head">
<div>
<strong><?php echo TaskCertView::e($stepTitle); ?></strong>
<div class="task-cert-hint"><?php echo TaskCertView::e((string) ($progress['note'] ?? '')); ?></div>
</div>
<span class="task-cert-user-method-state state-<?php echo TaskCertView::e((string) ($progress['state'] ?? 'open')); ?>">
<?php echo TaskCertView::e((string) ($progress['label'] ?? 'Offen')); ?>
</span>
</header>
<?php if ($methodType === 'knowledge_read'): ?>
<form action="<?php echo TaskCertView::e($taskCertBaseUrl . '/endpoints/user/submit_read.php'); ?>" method="post">
<input type="hidden" name="csrf_token" value="<?php echo TaskCertView::e($csrfToken); ?>">
<input type="hidden" name="assignment_id" value="<?php echo (int) $assignment['id']; ?>">
<input type="hidden" name="method_id" value="<?php echo (int) $method['id']; ?>">
<input type="hidden" name="redirect_to" value="<?php echo TaskCertView::e($taskDetailViewUrl); ?>">
<?php
$readLatest = $latestSubmissionByMethod[$methodId] ?? null;
$readPostIds = [];
if (is_array($readLatest)) {
$readPayload = is_array($readLatest['payload'] ?? null) ? $readLatest['payload'] : [];
foreach ((array) ($readPayload['post_ids'] ?? []) as $rpId) {
$readPostIds[(int) $rpId] = true;
}
}
?>
<div class="task-cert-user-read-posts">
<?php foreach ((array) ($method['knowledge_posts'] ?? []) as $post): ?>
<?php
$postId = (int) ($post['knowledge_post_id'] ?? 0);
$postUrl = $buildWikiPostUrl($postId);
$selectId = 'post_read_state_' . (int) ($method['id'] ?? 0) . '_' . $postId;
$alreadyRead = isset($readPostIds[$postId]);
?>
<section class="task-cert-user-read-post">
<div class="task-cert-user-read-post-head">
<div class="task-cert-user-read-post-title">
<strong><?php echo TaskCertView::e((string) ($post['title'] ?? ('Beitrag #' . $postId))); ?></strong>
<?php if ($alreadyRead): ?>
<span class="task-cert-user-read-badge">Gelesen</span>
<?php endif; ?>
</div>
<?php if ($postUrl !== ''): ?>
<a href="<?php echo TaskCertView::e($postUrl); ?>" target="_blank" rel="noopener noreferrer">Beitrag im neuen Tab öffnen</a>
<?php endif; ?>
</div>
<?php if ($postUrl !== ''): ?>
<details class="task-cert-user-read-post-details">
<summary>Beitrag öffnen</summary>
<div class="task-cert-user-read-post-content">
<div class="task-cert-user-read-post-iframe-wrap">
<iframe
src="<?php echo TaskCertView::e($postUrl); ?>"
title="<?php echo TaskCertView::e((string) ($post['title'] ?? ('Beitrag #' . $postId))); ?>"
loading="lazy"
referrerpolicy="strict-origin-when-cross-origin"
></iframe>
</div>
<label for="<?php echo TaskCertView::e($selectId); ?>">Lesestatus</label>
<select id="<?php echo TaskCertView::e($selectId); ?>" name="post_read_state[<?php echo $postId; ?>]">
<option value="unread" <?php echo !$alreadyRead ? 'selected' : ''; ?>>Ich habe den Beitrag nicht gelesen</option>
<option value="read" <?php echo $alreadyRead ? 'selected' : ''; ?>>Ich habe den Beitrag gelesen und verstanden</option>
</select>
</div>
</details>
<?php else: ?>
<div class="task-cert-hint">Beitragslink konnte nicht erzeugt werden.</div>
<?php endif; ?>
</section>
<?php endforeach; ?>
</div>
<button type="submit" class="btn task-cert-btn-success" <?php echo ($isAssignmentLocked || $previewMode) ? 'disabled' : ''; ?>>Lesestatus senden</button>
</form>
<?php endif; ?>
<?php if ($methodType === 'form_submit'): ?>
<form action="<?php echo TaskCertView::e($taskCertBaseUrl . '/endpoints/user/submit_form.php'); ?>" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="<?php echo TaskCertView::e($csrfToken); ?>">
<input type="hidden" name="assignment_id" value="<?php echo (int) $assignment['id']; ?>">
<input type="hidden" name="method_id" value="<?php echo (int) $method['id']; ?>">
<input type="hidden" name="redirect_to" value="<?php echo TaskCertView::e($taskDetailViewUrl); ?>">
<?php
// Datei-Werte der letzten Einreichung — werden am Upload-Feld
// als "aktuell hochgeladen" gezeigt; ohne Neu-Upload bleiben sie
// beim erneuten Absenden erhalten (Carry-Forward im Service).
$formLatest = $latestSubmissionByMethod[$methodId] ?? null;
$formLatestId = is_array($formLatest) ? (int) ($formLatest['id'] ?? 0) : 0;
$formLatestValues = [];
if (is_array($formLatest)) {
$formLatestPayload = is_array($formLatest['payload'] ?? null) ? $formLatest['payload'] : [];
$formLatestValues = is_array($formLatestPayload['form_values'] ?? null) ? $formLatestPayload['form_values'] : [];
}
?>
<div class="task-cert-user-stack">
<?php foreach ((array) ($method['form_fields'] ?? []) as $field): ?>
<?php
$fieldKey = (string) ($field['field_key'] ?? '');
$fieldType = (string) ($field['field_type'] ?? 'text');
$fieldLabel = (string) ($field['label'] ?? $fieldKey);
$required = (int) ($field['is_required'] ?? 0) === 1;
$options = is_array($field['options'] ?? null) ? $field['options'] : [];
?>
<div>
<label><?php echo TaskCertView::e($fieldLabel); ?><?php echo $required ? ' *' : ''; ?></label>
<?php if ($fieldType === 'textarea'): ?>
<textarea name="values[<?php echo TaskCertView::e($fieldKey); ?>]" <?php echo $required ? 'required' : ''; ?>></textarea>
<?php elseif ($fieldType === 'select'): ?>
<select name="values[<?php echo TaskCertView::e($fieldKey); ?>]" <?php echo $required ? 'required' : ''; ?>>
<option value="">Bitte wählen</option>
<?php foreach ($options as $option): ?>
<?php
$optionValue = is_array($option) ? (string) ($option['value'] ?? '') : (string) $option;
$optionLabel = is_array($option) ? (string) ($option['label'] ?? $optionValue) : (string) $option;
?>
<option value="<?php echo TaskCertView::e($optionValue); ?>"><?php echo TaskCertView::e($optionLabel); ?></option>
<?php endforeach; ?>
</select>
<?php elseif ($fieldType === 'radio'): ?>
<div class="task-cert-user-stack">
<?php foreach ($options as $option): ?>
<?php
$optionValue = is_array($option) ? (string) ($option['value'] ?? '') : (string) $option;
$optionLabel = is_array($option) ? (string) ($option['label'] ?? $optionValue) : (string) $option;
?>
<label class="task-cert-user-checkbox-row">
<input type="radio" name="values[<?php echo TaskCertView::e($fieldKey); ?>]" value="<?php echo TaskCertView::e($optionValue); ?>" <?php echo $required ? 'required' : ''; ?>>
<span><?php echo TaskCertView::e($optionLabel); ?></span>
</label>
<?php endforeach; ?>
</div>
<?php elseif ($fieldType === 'checkbox'): ?>
<div class="task-cert-user-stack">
<?php foreach ($options as $option): ?>
<?php
$optionValue = is_array($option) ? (string) ($option['value'] ?? '') : (string) $option;
$optionLabel = is_array($option) ? (string) ($option['label'] ?? $optionValue) : (string) $option;
?>
<label class="task-cert-user-checkbox-row">
<input type="checkbox" name="values[<?php echo TaskCertView::e($fieldKey); ?>][]" value="<?php echo TaskCertView::e($optionValue); ?>">
<span><?php echo TaskCertView::e($optionLabel); ?></span>
</label>
<?php endforeach; ?>
</div>
<?php elseif ($fieldType === 'file'): ?>
<?php
$existingFiles = is_array($formLatestValues[$fieldKey] ?? null) ? $formLatestValues[$fieldKey] : [];
$hasExistingFiles = $existingFiles !== [] && is_array(reset($existingFiles))
&& array_key_exists('file_path', reset($existingFiles));
?>
<?php if ($hasExistingFiles): ?>
<div class="task-cert-hint task-cert-user-existing-files">
Aktuell hochgeladen:
<?php
$existingLinks = [];
foreach (array_values($existingFiles) as $i => $fileMeta) {
if (!is_array($fileMeta)) {
continue;
}
$fileName = trim((string) ($fileMeta['file_name'] ?? ''));
$url = $taskCertBaseUrl . '/endpoints/admin/download_submission_file.php?' . http_build_query([
'submission_id' => $formLatestId,
'field_key' => $fieldKey,
'index' => (int) $i,
'csrf_token' => $csrfToken,
]);
$existingLinks[] = '<a href="' . TaskCertView::e($url) . '" target="_blank" rel="noopener">'
. TaskCertView::e($fileName !== '' ? $fileName : 'Datei') . '</a>';
}
echo implode(', ', $existingLinks);
?>
</div>
<?php endif; ?>
<input
type="file"
name="files[<?php echo TaskCertView::e($fieldKey); ?>][]"
multiple
accept="<?php echo TaskCertView::e(TaskUploadPolicy::acceptAttribute()); ?>"
<?php echo ($required && !$hasExistingFiles) ? 'required' : ''; ?>
>
<small class="task-cert-hint">
Erlaubt: <?php echo TaskCertView::e(TaskUploadPolicy::HUMAN_READABLE_FORMATS_SHORT); ?>
&middot; max. <?php echo (int) TaskUploadPolicy::MAX_FILE_COUNT; ?> Dateien,
<?php echo TaskCertView::e(TaskUploadPolicy::formatBytes(TaskUploadPolicy::MAX_TOTAL_BYTES)); ?> gesamt
<?php if ($hasExistingFiles): ?>
&middot; Leer lassen behält die aktuelle Datei, neue Auswahl ersetzt sie.
<?php endif; ?>
</small>
<?php else: ?>
<?php
$prefillCol = trim((string) ($field['prefill_column'] ?? ''));
$prefillValue = ($prefillCol !== '' && $fieldType === 'text')
? ContactPrefillColumns::resolve($contactData, $prefillCol)
: '';
?>
<input
type="<?php echo TaskCertView::e($fieldType === 'number' ? 'number' : ($fieldType === 'date' ? 'date' : 'text')); ?>"
name="values[<?php echo TaskCertView::e($fieldKey); ?>]"
placeholder="<?php echo TaskCertView::e((string) ($field['placeholder'] ?? '')); ?>"
value="<?php echo TaskCertView::e($prefillValue); ?>"
<?php echo $required ? 'required' : ''; ?>
>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<button type="submit" class="btn task-cert-btn-success" <?php echo ($isAssignmentLocked || $previewMode) ? 'disabled' : ''; ?>>Formular absenden</button>
</form>
<?php endif; ?>
<?php if ($methodType === 'quiz'): ?>
<form action="<?php echo TaskCertView::e($taskCertBaseUrl . '/endpoints/user/submit_quiz.php'); ?>" method="post">
<input type="hidden" name="csrf_token" value="<?php echo TaskCertView::e($csrfToken); ?>">
<input type="hidden" name="assignment_id" value="<?php echo (int) $assignment['id']; ?>">
<input type="hidden" name="method_id" value="<?php echo (int) $method['id']; ?>">
<input type="hidden" name="redirect_to" value="<?php echo TaskCertView::e($taskDetailViewUrl); ?>">
<div class="task-cert-user-stack">
<?php foreach ((array) ($method['quiz_questions'] ?? []) as $question): ?>
<div class="task-cert-quiz-question">
<strong><?php echo TaskCertView::e((string) ($question['question_text'] ?? '')); ?></strong>
<?php
$questionOptions = array_values((array) ($question['options'] ?? []));
if (count($questionOptions) > 1) {
shuffle($questionOptions);
}
?>
<div class="task-cert-user-quiz-options">
<?php foreach ($questionOptions as $option): ?>
<label class="task-cert-user-checkbox-row task-cert-user-quiz-option">
<input type="checkbox" name="answers[<?php echo (int) ($question['id'] ?? 0); ?>][]" value="<?php echo (int) ($option['id'] ?? 0); ?>">
<span><?php echo TaskCertView::e((string) ($option['option_text'] ?? '')); ?></span>
</label>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<button type="submit" class="btn task-cert-btn-success" <?php echo ($isAssignmentLocked || $previewMode) ? 'disabled' : ''; ?>>Quiz einreichen</button>
</form>
<?php endif; ?>
<?php if ($methodType === 'manual_approval'): ?>
<?php
$manualLatest = $latestSubmissionByMethod[$methodId] ?? null;
$manualLatestStatus = is_array($manualLatest) ? (string) ($manualLatest['status'] ?? '') : '';
$manualIsPending = $manualLatestStatus === 'awaiting_approval';
$manualIsApproved = in_array($manualLatestStatus, ['approved', 'auto_passed'], true);
$manualSubmittedAt = is_array($manualLatest) ? $formatDateTime($manualLatest['submitted_at'] ?? '') : '-';
$manualReviewNote = trim((string) ($manualLatest['review_note'] ?? ''));
?>
<form action="<?php echo TaskCertView::e($taskCertBaseUrl . '/endpoints/user/request_manual_approval.php'); ?>" method="post">
<input type="hidden" name="csrf_token" value="<?php echo TaskCertView::e($csrfToken); ?>">
<input type="hidden" name="assignment_id" value="<?php echo (int) $assignment['id']; ?>">
<input type="hidden" name="method_id" value="<?php echo (int) $method['id']; ?>">
<input type="hidden" name="redirect_to" value="<?php echo TaskCertView::e($taskDetailViewUrl); ?>">
<?php if ($manualIsPending): ?>
<div class="task-cert-warning">
Ihr Freigabeantrag wurde am <?php echo TaskCertView::e($manualSubmittedAt); ?> gesendet und wird aktuell geprüft.
</div>
<?php elseif ($manualIsApproved): ?>
<div class="task-cert-success">
Ihr Freigabeantrag wurde am <?php echo TaskCertView::e($manualSubmittedAt); ?> bestätigt.
</div>
<?php else: ?>
<?php if ($manualLatestStatus === 'rejected'): ?>
<div class="task-cert-warning">
Ihr letzter Antrag wurde abgelehnt. Bitte reichen Sie ihn erneut ein.
<?php if ($manualReviewNote !== ''): ?>
Hinweis: <?php echo TaskCertView::e($manualReviewNote); ?>
<?php endif; ?>
</div>
<?php endif; ?>
<label for="manual_note_<?php echo (int) $method['id']; ?>">Kurzer Hinweis (optional)</label>
<textarea id="manual_note_<?php echo (int) $method['id']; ?>" name="note" rows="4" placeholder="Falls nötig, kurze Ergänzung für die Freigabe."></textarea>
<button type="submit" class="btn task-cert-btn-success" <?php echo ($isAssignmentLocked || $previewMode) ? 'disabled' : ''; ?>>Antrag senden</button>
<?php endif; ?>
</form>
<?php endif; ?>
</section>
<?php endforeach; ?>
<?php if ($methods === []): ?>
<div class="task-cert-hint">Für diese Aufgabe sind aktuell keine Schritte hinterlegt.</div>
<?php endif; ?>
</div>
</main>
<aside class="task-cert-user-aside">
<div class="task-cert-user-status-compact">
<span class="task-cert-user-status-badge status-<?php echo TaskCertView::e($assignmentStatus); ?>">
<?php echo TaskCertView::e($statusLabels[$assignmentStatus] ?? $assignmentStatus); ?>
</span>
<span class="task-cert-user-status-due <?php echo $assignmentStatus === 'overdue' ? 'is-overdue' : ''; ?>">
<?php echo TaskCertView::e($formatDueMessage($assignment['due_at'] ?? '')); ?>
</span>
<?php if ($totalRequiredSteps > 0): ?>
<span class="task-cert-user-status-steps">
<?php echo (int) $completedRequiredSteps; ?>/<?php echo (int) $totalRequiredSteps; ?> Schritte
</span>
<?php endif; ?>
</div>
<?php if ($taskResponsibles !== []): ?>
<div class="task-cert-block">
<h3>Ansprechpartner</h3>
<div class="task-cert-user-responsibles">
<?php foreach ($taskResponsibles as $responsible): ?>
<?php
$respName = trim((string) ($responsible['name'] ?? ''));
$respEmail = trim((string) ($responsible['email'] ?? ''));
?>
<div class="task-cert-user-responsible-item">
<strong><?php echo TaskCertView::e($respName !== '' ? $respName : 'Kontakt #' . (int) ($responsible['main_contact_id'] ?? 0)); ?></strong>
<?php if ($respEmail !== ''): ?>
<a href="mailto:<?php echo TaskCertView::e($respEmail); ?>"><?php echo TaskCertView::e($respEmail); ?></a>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<div class="task-cert-block">
<h3>Bisherige Einreichungen</h3>
<?php
$renderSubmissionCard = static function (
array $sub,
string $methodType,
array $fieldLabels,
array $submissionStatusLabels,
callable $formatDateTime
) use ($taskCertBaseUrl, $csrfToken): void {
$subStatus = (string) ($sub['status'] ?? '');
$submissionId = (int) ($sub['id'] ?? 0);
$downloadFileUrl = $taskCertBaseUrl . '/endpoints/admin/download_submission_file.php';
$statusLabel = $submissionStatusLabels[$subStatus] ?? $subStatus;
$submittedAt = $formatDateTime($sub['submitted_at'] ?? '');
$payload = is_array($sub['payload'] ?? null) ? $sub['payload'] : [];
$formValues = is_array($payload['form_values'] ?? null) ? $payload['form_values'] : [];
?>
<article class="task-cert-user-submission-card status-<?php echo TaskCertView::e($subStatus); ?>">
<div class="task-cert-user-submission-card-head">
<span class="task-cert-user-status-badge status-<?php echo TaskCertView::e($subStatus); ?>">
<?php echo TaskCertView::e($statusLabel); ?>
</span>
<span class="task-cert-user-submission-card-date">
<?php echo TaskCertView::e($submittedAt); ?>
</span>
</div>
<?php if ($methodType === 'form_submit' && $formValues !== []): ?>
<details class="task-cert-user-submission-details">
<summary>Antworten anzeigen</summary>
<dl class="task-cert-user-submission-answers">
<?php foreach ($formValues as $fKey => $fVal): ?>
<?php
$fKey = (string) $fKey;
$fLabel = trim((string) ($fieldLabels[$fKey] ?? $fKey));
$isFileVal = is_array($fVal) && $fVal !== [] && is_array(reset($fVal))
&& array_key_exists('file_path', reset($fVal));
?>
<div class="task-cert-user-submission-answer-row">
<dt><?php echo TaskCertView::e($fLabel); ?></dt>
<dd>
<?php if ($isFileVal): ?>
<?php $fileLinks = [];
foreach (array_values($fVal) as $i => $fileMeta):
if (!is_array($fileMeta)) { continue; }
$fileName = trim((string) ($fileMeta['file_name'] ?? ''));
$url = $downloadFileUrl . '?' . http_build_query([
'submission_id' => $submissionId,
'field_key' => $fKey,
'index' => (int) $i,
'csrf_token' => $csrfToken,
]);
$fileLinks[] = '<a href="' . TaskCertView::e($url) . '" target="_blank" rel="noopener">'
. TaskCertView::e($fileName !== '' ? $fileName : 'Datei') . '</a>';
endforeach;
echo $fileLinks === [] ? '-' : implode(', ', $fileLinks);
?>
<?php else: ?>
<?php $fDisplay = is_array($fVal) ? implode(', ', $fVal) : trim((string) $fVal); ?>
<?php echo TaskCertView::e($fDisplay !== '' ? $fDisplay : '-'); ?>
<?php endif; ?>
</dd>
</div>
<?php endforeach; ?>
</dl>
</details>
<?php endif; ?>
</article>
<?php
};
?>
<div class="task-cert-user-submissions-history">
<?php foreach ($methods as $method): ?>
<?php
$methodId = (int) ($method['id'] ?? 0);
$methodType = (string) ($method['method_type'] ?? '');
$methodSubs = $allSubmissionsByMethod[$methodId] ?? [];
$latest = $latestSubmissionByMethod[$methodId] ?? null;
$fieldLabels = $formFieldLabelsByMethod[$methodId] ?? [];
$olderSubs = array_slice($methodSubs, 1);
?>
<div class="task-cert-user-submission-group">
<strong><?php echo TaskCertView::e($methodLabels[$methodType] ?? $methodType); ?></strong>
<?php if (is_array($latest)): ?>
<?php $renderSubmissionCard($latest, $methodType, $fieldLabels, $submissionStatusLabels, $formatDateTime); ?>
<?php if ($olderSubs !== []): ?>
<details class="task-cert-user-older-submissions">
<summary>Ältere Einreichungen (<?php echo count($olderSubs); ?>)</summary>
<div class="task-cert-user-older-submissions-list">
<?php foreach ($olderSubs as $olderSub): ?>
<?php $renderSubmissionCard($olderSub, $methodType, $fieldLabels, $submissionStatusLabels, $formatDateTime); ?>
<?php endforeach; ?>
</div>
</details>
<?php endif; ?>
<?php else: ?>
<span class="task-cert-hint">Noch keine Einreichung.</span>
<?php endif; ?>
</div>
<?php endforeach; ?>
<?php if ($methods === []): ?>
<div class="task-cert-hint">Es gibt noch keine Einreichungen.</div>
<?php endif; ?>
</div>
</div>
<div class="task-cert-block">
<h3>Ihre Zertifikate zu dieser Aufgabe</h3>
<?php if ((int) ($task['certificate_enabled'] ?? 0) !== 1): ?>
<div class="task-cert-hint">Für diese Aufgabe sind keine Zertifikate vorgesehen.</div>
<?php elseif ($taskCertificatesHistory === []): ?>
<div class="task-cert-hint">Bisher keine Zertifikate zu dieser Aufgabe.</div>
<?php else: ?>
<?php
$certificateTitle = trim((string) ($task['certificate_title'] ?? ''));
if ($certificateTitle === '') {
$certificateTitle = 'Zertifikat';
}
?>
<div class="task-cert-user-certificate-cards">
<?php foreach ($taskCertificatesHistory as $certificate): ?>
<?php
$certificateStatus = (string) ($certificate['status'] ?? '');
$validUntilRaw = trim((string) ($certificate['valid_until'] ?? ''));
$cycleKey = trim((string) ($certificate['cycle_key'] ?? ''));
?>
<article class="task-cert-user-certificate-card status-<?php echo TaskCertView::e($certificateStatus); ?>">
<div class="task-cert-user-certificate-card-head">
<div class="task-cert-user-certificate-card-code">
<strong><?php echo TaskCertView::e($certificateTitle); ?></strong>
<small>Code: <?php echo TaskCertView::e((string) ($certificate['certificate_code'] ?? '-')); ?></small>
</div>
<span class="task-cert-user-status-badge status-<?php echo TaskCertView::e($certificateStatus); ?>">
<?php echo TaskCertView::e($certificateStatusLabels[$certificateStatus] ?? $certificateStatus); ?>
</span>
</div>
<div class="task-cert-user-certificate-card-meta">
<span>Ausgestellt: <?php echo TaskCertView::e($formatDateTime($certificate['issued_at'] ?? '')); ?></span>
<span>Gültig bis: <?php echo TaskCertView::e($validUntilRaw !== '' ? $formatDateTime($validUntilRaw) : 'dauerhaft'); ?></span>
<span>Zyklus: <?php echo TaskCertView::e($cycleKey !== '' ? $cycleKey : '-'); ?></span>
</div>
</article>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</aside>
</div>
<?php endif; ?>
</div>