285 lines
12 KiB
PHP
285 lines
12 KiB
PHP
|
|
<?php
|
|||
|
|
declare(strict_types=1);
|
|||
|
|
|
|||
|
|
if (!isset($assignments) && !isset($error)) {
|
|||
|
|
require_once dirname(dirname(__DIR__)) . '/actions/user/my_tasks_action.php';
|
|||
|
|
$viewData = task_cert_user_my_tasks_action();
|
|||
|
|
if (is_array($viewData)) {
|
|||
|
|
extract($viewData, EXTR_SKIP);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$error = $error ?? null;
|
|||
|
|
$assignments = $assignments ?? [];
|
|||
|
|
$statusLabels = TaskCertConstants::assignmentStatusLabels();
|
|||
|
|
$completedStatuses = TaskCertConstants::COMPLETED_STATUSES;
|
|||
|
|
|
|||
|
|
if (!function_exists('task_cert_render_styles')) {
|
|||
|
|
require_once dirname(dirname(__DIR__)) . '/bootstrap.php';
|
|||
|
|
}
|
|||
|
|
task_cert_render_styles(['user']);
|
|||
|
|
$taskDetailUrl = task_cert_view_url('user.task_detail');
|
|||
|
|
$myTasksUrl = task_cert_view_url('user.my_tasks');
|
|||
|
|
$now = new DateTimeImmutable('now');
|
|||
|
|
|
|||
|
|
$statusFilter = TaskCertRequest::queryString('status', '');
|
|||
|
|
if ($statusFilter !== '' && !array_key_exists($statusFilter, $statusLabels)) {
|
|||
|
|
$statusFilter = '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($statusFilter !== '') {
|
|||
|
|
$assignments = array_values(array_filter($assignments, static function (array $a) use ($statusFilter): bool {
|
|||
|
|
return ($a['status'] ?? '') === $statusFilter;
|
|||
|
|
}));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Active & completed assignments: alle Zyklen separat anzeigen.
|
|||
|
|
// Aktive: älteste zuerst (überfällige Rückstände oben).
|
|||
|
|
// Abgeschlossene: neueste zuerst (unter <details> im UI eingeklappt).
|
|||
|
|
$byTaskId = [];
|
|||
|
|
foreach ($assignments as $a) {
|
|||
|
|
$byTaskId[(int) ($a['task_id'] ?? 0)][] = $a;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$assignments = [];
|
|||
|
|
foreach ($byTaskId as $taskAssignments) {
|
|||
|
|
$active = [];
|
|||
|
|
$completed = [];
|
|||
|
|
foreach ($taskAssignments as $a) {
|
|||
|
|
if (in_array($a['status'] ?? '', $completedStatuses, true)) {
|
|||
|
|
$completed[] = $a;
|
|||
|
|
} else {
|
|||
|
|
$active[] = $a;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($active !== []) {
|
|||
|
|
usort($active, static function (array $a, array $b): int {
|
|||
|
|
$aDue = strtotime((string) ($a['due_at'] ?? '9999-12-31'));
|
|||
|
|
$bDue = strtotime((string) ($b['due_at'] ?? '9999-12-31'));
|
|||
|
|
return $aDue <=> $bDue;
|
|||
|
|
});
|
|||
|
|
foreach ($active as $a) {
|
|||
|
|
$assignments[] = $a;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($completed !== []) {
|
|||
|
|
usort($completed, static function (array $a, array $b): int {
|
|||
|
|
return strtotime((string) ($b['completed_at'] ?? '0')) - strtotime((string) ($a['completed_at'] ?? '0'));
|
|||
|
|
});
|
|||
|
|
foreach ($completed as $a) {
|
|||
|
|
$assignments[] = $a;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$formatDateShort = static function ($value): string {
|
|||
|
|
$value = trim((string) $value);
|
|||
|
|
if ($value === '') {
|
|||
|
|
return '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
return (new DateTimeImmutable($value))->format('d.m.Y');
|
|||
|
|
} catch (Throwable $throwable) {
|
|||
|
|
return $value;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
$getCardMeta = static function (array $assignment) use ($formatDateShort): string {
|
|||
|
|
$parts = [];
|
|||
|
|
|
|||
|
|
$dueAt = trim((string) ($assignment['due_at'] ?? ''));
|
|||
|
|
if ($dueAt !== '') {
|
|||
|
|
$parts[] = 'Fällig bis ' . $formatDateShort($dueAt);
|
|||
|
|
} else {
|
|||
|
|
$parts[] = 'Kein Fälligkeitsdatum';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Zyklus-Zeitraum nur anzeigen, wenn die Aufgabe wiederkehrend ist
|
|||
|
|
// (bei 'once' irrelevant). So versteht der User bei Rückständen,
|
|||
|
|
// welcher Zyklus welcher Karte zugeordnet ist.
|
|||
|
|
$cycleKey = (string) ($assignment['cycle_key'] ?? '');
|
|||
|
|
$cycleStart = trim((string) ($assignment['cycle_start_at'] ?? ''));
|
|||
|
|
$cycleEnd = trim((string) ($assignment['cycle_end_at'] ?? ''));
|
|||
|
|
$showCyclePeriod = (int) ($assignment['show_cycle_period'] ?? 0) === 1;
|
|||
|
|
if ($showCyclePeriod && $cycleKey !== '' && $cycleKey !== 'once' && $cycleStart !== '' && $cycleEnd !== '') {
|
|||
|
|
$parts[] = 'Zeitraum ' . $formatDateShort($cycleStart) . '–' . $formatDateShort($cycleEnd);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return implode(' · ', $parts);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
$isAssignmentOverdueByDate = static function (array $assignment, DateTimeImmutable $now): bool {
|
|||
|
|
$status = (string) ($assignment['status'] ?? '');
|
|||
|
|
if ($status === 'completed') {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$dueAt = trim((string) ($assignment['due_at'] ?? ''));
|
|||
|
|
if ($dueAt === '') {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
return (new DateTimeImmutable($dueAt)) < $now;
|
|||
|
|
} catch (Throwable $throwable) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
$groupedAssignments = [];
|
|||
|
|
$categoryOrder = [];
|
|||
|
|
foreach ($assignments as $assignment) {
|
|||
|
|
$categoryName = trim((string) ($assignment['task_category_name'] ?? ''));
|
|||
|
|
if ($categoryName === '') {
|
|||
|
|
$categoryName = 'Allgemein';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!array_key_exists($categoryName, $groupedAssignments)) {
|
|||
|
|
$groupedAssignments[$categoryName] = [];
|
|||
|
|
$categoryOrder[] = $categoryName;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$groupedAssignments[$categoryName][] = $assignment;
|
|||
|
|
}
|
|||
|
|
?>
|
|||
|
|
<div class="task-cert task-cert-user-my-tasks">
|
|||
|
|
<?php if ($error): ?>
|
|||
|
|
<div class="task-cert-error"><?php echo TaskCertView::e($error); ?></div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<form method="get" action="<?php echo TaskCertView::e($myTasksUrl); ?>" class="task-cert-user-filter-bar">
|
|||
|
|
<label for="task-cert-user-status-filter" class="task-cert-sr-only">Status filtern</label>
|
|||
|
|
<select name="status" id="task-cert-user-status-filter" aria-label="Status filtern" onchange="this.form.submit()">
|
|||
|
|
<option value="">Alle Status</option>
|
|||
|
|
<?php foreach ($statusLabels as $key => $label): ?>
|
|||
|
|
<option value="<?php echo TaskCertView::e($key); ?>" <?php echo $statusFilter === $key ? 'selected' : ''; ?>>
|
|||
|
|
<?php echo TaskCertView::e($label); ?>
|
|||
|
|
</option>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</select>
|
|||
|
|
<?php if ($statusFilter !== ''): ?>
|
|||
|
|
<a href="<?php echo TaskCertView::e($myTasksUrl); ?>" class="task-cert-user-filter-reset">Zurücksetzen</a>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</form>
|
|||
|
|
|
|||
|
|
<?php if ($assignments === []): ?>
|
|||
|
|
<div class="task-cert-block">
|
|||
|
|
<?php if ($statusFilter !== ''): ?>
|
|||
|
|
Keine Aufgaben mit Status „<?php echo TaskCertView::e($statusLabels[$statusFilter] ?? $statusFilter); ?>“ vorhanden.
|
|||
|
|
<?php else: ?>
|
|||
|
|
Aktuell sind keine Aufgaben vorhanden.
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
<?php else: ?>
|
|||
|
|
<?php foreach ($categoryOrder as $categoryName): ?>
|
|||
|
|
<?php
|
|||
|
|
$categoryAssignments = $groupedAssignments[$categoryName] ?? [];
|
|||
|
|
if ($categoryAssignments === []) { continue; }
|
|||
|
|
|
|||
|
|
$activeAssignments = [];
|
|||
|
|
$completedAssignments = [];
|
|||
|
|
foreach ($categoryAssignments as $a) {
|
|||
|
|
if (in_array($a['status'] ?? '', $completedStatuses, true)) {
|
|||
|
|
$completedAssignments[] = $a;
|
|||
|
|
} else {
|
|||
|
|
$activeAssignments[] = $a;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
usort($completedAssignments, static function (array $a, array $b): int {
|
|||
|
|
return strtotime((string) ($b['completed_at'] ?? '0')) - strtotime((string) ($a['completed_at'] ?? '0'));
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
$activeCount = count($activeAssignments);
|
|||
|
|
$completedCount = count($completedAssignments);
|
|||
|
|
?>
|
|||
|
|
|
|||
|
|
<section class="task-cert-user-category-group">
|
|||
|
|
<header class="task-cert-user-category-head">
|
|||
|
|
<h3><?php echo TaskCertView::e($categoryName); ?></h3>
|
|||
|
|
<span><?php echo $activeCount; ?> offen<?php echo $completedCount > 0 ? ', ' . $completedCount . ' abgeschlossen' : ''; ?></span>
|
|||
|
|
</header>
|
|||
|
|
|
|||
|
|
<?php if ($activeAssignments !== []): ?>
|
|||
|
|
<div class="task-cert-user-task-cards">
|
|||
|
|
<?php foreach ($activeAssignments as $assignment): ?>
|
|||
|
|
<?php
|
|||
|
|
$status = (string) ($assignment['status'] ?? '');
|
|||
|
|
$title = (string) ($assignment['title'] ?? 'Aufgabe');
|
|||
|
|
$metaText = $getCardMeta($assignment);
|
|||
|
|
$isOverdueByDate = $isAssignmentOverdueByDate($assignment, $now);
|
|||
|
|
$isEscalated = (int) ($assignment['escalation_level'] ?? 0) > 0;
|
|||
|
|
$cardClasses = [
|
|||
|
|
'task-cert-user-task-card',
|
|||
|
|
'status-' . $status,
|
|||
|
|
];
|
|||
|
|
if ($isOverdueByDate) {
|
|||
|
|
$cardClasses[] = 'is-overdue-date';
|
|||
|
|
}
|
|||
|
|
if ($isEscalated) {
|
|||
|
|
$cardClasses[] = 'is-escalated';
|
|||
|
|
}
|
|||
|
|
?>
|
|||
|
|
<a
|
|||
|
|
href="<?php echo TaskCertView::e($taskDetailUrl . '?assignment_id=' . (int) $assignment['id']); ?>"
|
|||
|
|
class="<?php echo TaskCertView::e(implode(' ', $cardClasses)); ?>"
|
|||
|
|
>
|
|||
|
|
<div class="task-cert-user-task-card-head">
|
|||
|
|
<strong><?php echo TaskCertView::e($title); ?></strong>
|
|||
|
|
<span class="task-cert-user-status-badge status-<?php echo TaskCertView::e($status); ?>">
|
|||
|
|
<?php echo TaskCertView::e($statusLabels[$status] ?? $status); ?>
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="task-cert-user-task-card-meta">
|
|||
|
|
<?php echo TaskCertView::e($metaText); ?>
|
|||
|
|
</div>
|
|||
|
|
</a>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<?php if ($completedAssignments !== []): ?>
|
|||
|
|
<details class="task-cert-user-completed-group">
|
|||
|
|
<summary>
|
|||
|
|
<?php echo $completedCount; ?> abgeschlossene Aufgabe<?php echo $completedCount === 1 ? '' : 'n'; ?> anzeigen
|
|||
|
|
</summary>
|
|||
|
|
<div class="task-cert-user-task-cards">
|
|||
|
|
<?php foreach ($completedAssignments as $assignment): ?>
|
|||
|
|
<?php
|
|||
|
|
$status = (string) ($assignment['status'] ?? '');
|
|||
|
|
$title = (string) ($assignment['title'] ?? 'Aufgabe');
|
|||
|
|
$metaText = $getCardMeta($assignment);
|
|||
|
|
$cardClasses = ['task-cert-user-task-card', 'status-' . $status];
|
|||
|
|
?>
|
|||
|
|
<a
|
|||
|
|
href="<?php echo TaskCertView::e($taskDetailUrl . '?assignment_id=' . (int) $assignment['id']); ?>"
|
|||
|
|
class="<?php echo TaskCertView::e(implode(' ', $cardClasses)); ?>"
|
|||
|
|
>
|
|||
|
|
<div class="task-cert-user-task-card-head">
|
|||
|
|
<strong><?php echo TaskCertView::e($title); ?></strong>
|
|||
|
|
<span class="task-cert-user-status-badge status-<?php echo TaskCertView::e($status); ?>">
|
|||
|
|
<?php echo TaskCertView::e($statusLabels[$status] ?? $status); ?>
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="task-cert-user-task-card-meta">
|
|||
|
|
<?php echo TaskCertView::e($metaText); ?>
|
|||
|
|
</div>
|
|||
|
|
</a>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</div>
|
|||
|
|
</details>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<?php if ($activeAssignments === [] && $completedAssignments === []): ?>
|
|||
|
|
<div class="task-cert-block">Keine Aufgaben in dieser Kategorie.</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</section>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|