171 lines
7.1 KiB
Twig
171 lines
7.1 KiB
Twig
{% extends 'layouts/base.twig' %}
|
||
|
||
{% block title %}{{ title }} — Бизнес.Точка{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||
<h1 class="h3 mb-0">{{ title }}</h1>
|
||
<div class="btn-group">
|
||
<a href="{{ base_url('/tasks') }}" class="btn btn-outline-secondary">
|
||
<i class="fa-solid fa-list me-2"></i>Список
|
||
</a>
|
||
<a href="{{ base_url('/tasks/kanban') }}" class="btn btn-outline-primary">
|
||
<i class="fa-solid fa-table-columns me-2"></i>Канбан
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
{# Статистика #}
|
||
<div class="row g-3 mb-4">
|
||
<div class="col-md-3">
|
||
<div class="card h-100 border-0 shadow-sm">
|
||
<div class="card-body text-center">
|
||
<h5 class="card-title text-muted mb-0">Всего</h5>
|
||
<h2 class="mb-0">{{ stats.total }}</h2>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<div class="card h-100 border-0 shadow-sm">
|
||
<div class="card-body text-center">
|
||
<h5 class="card-title text-muted mb-0">Выполнено</h5>
|
||
<h2 class="mb-0 text-success">{{ stats.completed }}</h2>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<div class="card h-100 border-0 shadow-sm">
|
||
<div class="card-body text-center">
|
||
<h5 class="card-title text-muted mb-0">В ожидании</h5>
|
||
<h2 class="mb-0 text-primary">{{ stats.pending }}</h2>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<div class="card h-100 border-0 shadow-sm">
|
||
<div class="card-body text-center">
|
||
<h5 class="card-title text-muted mb-0">Просрочено</h5>
|
||
<h2 class="mb-0 text-danger">{{ stats.overdue }}</h2>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card border-0 shadow-sm">
|
||
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
||
<div class="btn-group">
|
||
<a href="{{ base_url('/tasks/calendar?month=' ~ prevMonth) }}" class="btn btn-outline-secondary btn-sm">
|
||
<i class="fa-solid fa-chevron-left"></i>
|
||
</a>
|
||
<a href="{{ base_url('/tasks/calendar?month=' ~ nextMonth) }}" class="btn btn-outline-secondary btn-sm">
|
||
<i class="fa-solid fa-chevron-right"></i>
|
||
</a>
|
||
</div>
|
||
<h5 class="mb-0">{{ monthName }}</h5>
|
||
<a href="{{ base_url('/tasks/calendar') }}" class="btn btn-outline-secondary btn-sm">
|
||
Сегодня
|
||
</a>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="calendar-container">
|
||
{# Дни недели #}
|
||
<div class="calendar-weekdays mb-2">
|
||
{% for day in ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'] %}
|
||
<div class="calendar-weekday text-center text-muted fw-bold" style="flex: 1;">{{ day }}</div>
|
||
{% endfor %}
|
||
</div>
|
||
|
||
{# Календарная сетка #}
|
||
<div class="calendar-grid">
|
||
{% set firstDay = firstDayOfWeek %}
|
||
{% set daysInMonth = daysInMonth %}
|
||
|
||
{# Пустые ячейки до первого дня #}
|
||
{% for i in 0..(firstDay - 1) %}
|
||
<div class="calendar-day p-2 border bg-light"></div>
|
||
{% endfor %}
|
||
|
||
{# Дни месяца #}
|
||
{% for day in 1..daysInMonth %}
|
||
{% set dateStr = currentMonth ~ '-' ~ (day < 10 ? '0' ~ day : day) %}
|
||
{% set isToday = dateStr == today %}
|
||
{% set isPast = dateStr < today %}
|
||
{% set dayEvents = eventsByDate[dateStr]|default([]) %}
|
||
|
||
<div class="calendar-day p-2 border {% if isToday %}bg-primary bg-opacity-10{% endif %}">
|
||
<div class="d-flex justify-content-between align-items-start mb-1">
|
||
<span class="calendar-day-number fw-bold {% if isToday %}text-primary{% endif %}">{{ day }}</span>
|
||
{% if dayEvents|length > 0 %}
|
||
<span class="badge bg-primary" style="font-size: 0.6rem;">{{ dayEvents|length }}</span>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="calendar-events">
|
||
{% for event in dayEvents|slice(0, 3) %}
|
||
<a href="{{ base_url(event.url) }}"
|
||
class="calendar-event d-block text-decoration-none mb-1 px-1 py-1 rounded small"
|
||
style="font-size: 0.75rem; background-color: {{ event.column_color }}20; border-left: 3px solid {{ event.column_color }}; color: #333;"
|
||
title="{{ event.title }}">
|
||
<i class="fa-solid fa-circle me-1" style="font-size: 0.5rem; color: {{ event.column_color }};"></i>
|
||
{{ event.title|length > 15 ? event.title|slice(0, 15) ~ '...' : event.title }}
|
||
{% if event.priority == 'urgent' or event.priority == 'high' %}
|
||
<i class="fa-solid fa-flag text-danger ms-1" style="font-size: 0.5rem;"></i>
|
||
{% endif %}
|
||
</a>
|
||
{% endfor %}
|
||
|
||
{% if dayEvents|length > 3 %}
|
||
<div class="text-muted small text-center">
|
||
+{{ dayEvents|length - 3 }} ещё
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
|
||
{# Пустые ячейки после последнего дня #}
|
||
{% set remaining = 7 - ((firstDay + daysInMonth) % 7) %}
|
||
{% if remaining < 7 %}
|
||
{% for i in 1..remaining %}
|
||
<div class="calendar-day p-2 border bg-light"></div>
|
||
{% endfor %}
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<style>
|
||
.calendar-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(7, 1fr);
|
||
gap: 1px;
|
||
background-color: #dee2e6;
|
||
border: 1px solid #dee2e6;
|
||
}
|
||
|
||
.calendar-day {
|
||
min-height: 100px;
|
||
background-color: white;
|
||
}
|
||
|
||
.calendar-day.bg-light {
|
||
background-color: #f8f9fa;
|
||
}
|
||
|
||
.calendar-event:hover {
|
||
background-color: {{ event.column_color }}40 !important;
|
||
}
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block scripts %}
|
||
<script>
|
||
// Навигация по месяцам при клике на заголовок
|
||
document.querySelector('h5.mb-0').style.cursor = 'pointer';
|
||
document.querySelector('h5.mb-0').addEventListener('click', function() {
|
||
window.location.href = '{{ base_url('/tasks/calendar') }}';
|
||
});
|
||
</script>
|
||
{% endblock %}
|