170 lines
9.3 KiB
PHP
170 lines
9.3 KiB
PHP
<?php
|
||
$deadlineRank = function (array $task): int {
|
||
$deadline = taskDeadlineState($task['planned_at'] ?? null, $task['status'] ?? null);
|
||
|
||
return match ($deadline['code'] ?? '') {
|
||
'red' => 1,
|
||
'yellow' => 2,
|
||
'green' => 3,
|
||
default => 4,
|
||
};
|
||
};
|
||
|
||
foreach ($boards as &$board) {
|
||
if (empty($board['tasks']) || !is_array($board['tasks'])) {
|
||
continue;
|
||
}
|
||
|
||
usort($board['tasks'], function (array $a, array $b) use ($deadlineRank): int {
|
||
$rankA = $deadlineRank($a);
|
||
$rankB = $deadlineRank($b);
|
||
|
||
if ($rankA !== $rankB) {
|
||
return $rankA <=> $rankB;
|
||
}
|
||
|
||
return strtotime((string)($a['planned_at'] ?? '9999-12-31'))
|
||
<=> strtotime((string)($b['planned_at'] ?? '9999-12-31'));
|
||
});
|
||
}
|
||
unset($board);
|
||
?>
|
||
<div class="container-fluid py-3">
|
||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||
<h1 class="h3 mb-0">Главная</h1>
|
||
</div>
|
||
|
||
<?php if (empty($boards)): ?>
|
||
<div class="alert alert-light border text-muted">
|
||
Нет досок для отображения на главной.
|
||
</div>
|
||
<?php else: ?>
|
||
<div class="row g-3">
|
||
<?php foreach ($boards as $board): ?>
|
||
<div class="col-12 col-md-6 col-xl-4">
|
||
<div class="card shadow-sm h-100">
|
||
<div class="card-header bg-white">
|
||
<div class="d-flex justify-content-between align-items-start gap-2">
|
||
<div>
|
||
<a href="/boards/<?= htmlspecialchars((string)$board['code']) ?>"
|
||
class="text-decoration-none fw-semibold fs-5">
|
||
<?= htmlspecialchars((string)$board['name']) ?>
|
||
</a>
|
||
|
||
<?php if (!empty($board['description'])): ?>
|
||
<div class="text-muted small mt-1">
|
||
<?= htmlspecialchars((string)$board['description']) ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card-body">
|
||
<div class="d-flex flex-wrap gap-2 mb-3">
|
||
<span class="badge text-bg-secondary">
|
||
Всего: <?= (int)$board['stats']['total_count'] ?>
|
||
</span>
|
||
<span class="badge text-bg-secondary">
|
||
Новые: <?= (int)$board['stats']['new_count'] ?>
|
||
</span>
|
||
<span class="badge text-bg-primary">
|
||
В работе: <?= (int)$board['stats']['in_progress_count'] ?>
|
||
</span>
|
||
<span class="badge text-bg-success">
|
||
Завершено: <?= (int)$board['stats']['done_count'] ?>
|
||
</span>
|
||
</div>
|
||
|
||
<div class="list-group list-group-flush">
|
||
<?php if (empty($board['tasks'])): ?>
|
||
<div class="text-muted small">
|
||
В этой доске пока нет задач.
|
||
</div>
|
||
<?php else: ?>
|
||
<?php foreach ($board['tasks'] as $task): ?>
|
||
<?php
|
||
$deadline = taskDeadlineState($task['planned_at'] ?? null, $task['status'] ?? null);
|
||
|
||
$commentCount = (int)($task['comment_count'] ?? 0);
|
||
$lastCommentId = (int)($task['last_comment_id'] ?? 0);
|
||
$lastReadCommentId = (int)($task['last_read_comment_id'] ?? 0);
|
||
$isUnread = $commentCount > 0 && $lastCommentId > $lastReadCommentId;
|
||
$badgeClass = $isUnread ? 'bg-primary' : 'bg-secondary';
|
||
|
||
$statusClass = match ($task['status']) {
|
||
'NEW' => 'bg-secondary',
|
||
'IN_PROGRESS' => 'bg-primary',
|
||
'REVIEW' => 'bg-warning text-dark',
|
||
'DONE' => 'bg-success',
|
||
'CANCELED' => 'bg-dark',
|
||
'OVERDUE' => 'bg-danger',
|
||
default => 'bg-secondary',
|
||
};
|
||
?>
|
||
<div class="list-group-item px-2 <?= $deadline['class'] ?? '' ?>">
|
||
<div class="d-flex justify-content-between align-items-start gap-2">
|
||
<div class="flex-grow-1">
|
||
<a href="/tasks/show?id=<?= (int)$task['id'] ?>"
|
||
data-task-modal
|
||
data-task-id="<?= (int)$task['id'] ?>"
|
||
class="text-decoration-none fw-semibold">
|
||
<?= htmlspecialchars((string)$task['name']) ?>
|
||
</a>
|
||
|
||
<div class="small text-muted">
|
||
<?= htmlspecialchars((string)$task['crm_id']) ?>
|
||
</div>
|
||
|
||
<?php if (!empty($task['assignee_name'])): ?>
|
||
<div class="small text-muted">
|
||
Ответственный: <?= htmlspecialchars((string)$task['assignee_name']) ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
<?php if (!empty($task['planned_at'])): ?>
|
||
<div class="small mt-1">
|
||
<span class="text-muted">План:</span>
|
||
<?= htmlspecialchars(date('d.m.Y H:i', strtotime((string)$task['planned_at']))) ?>
|
||
|
||
<?php if ($deadline): ?>
|
||
<span class="badge deadline-badge-<?= htmlspecialchars($deadline['code']) ?> ms-1">
|
||
<?= htmlspecialchars($deadline['text']) ?>
|
||
</span>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<div class="d-flex flex-column align-items-end gap-1">
|
||
<span class="badge <?= $statusClass ?>">
|
||
<?= htmlspecialchars(task_status_label((string)$task['status'])) ?>
|
||
</span>
|
||
|
||
<span
|
||
id="comment-badge-<?= (int)$task['id'] ?>"
|
||
class="badge <?= $badgeClass ?>"
|
||
data-task-id="<?= (int)$task['id'] ?>"
|
||
data-last-read-comment-id="<?= $lastReadCommentId ?>"
|
||
data-last-comment-id="<?= $lastCommentId ?>"
|
||
>
|
||
<?= $commentCount ?>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card-footer bg-white">
|
||
<a href="/boards/<?= htmlspecialchars((string)$board['code']) ?>" class="btn btn-sm btn-outline-primary">
|
||
Открыть доску
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|