bp/app/Views/components/calendar/calendar.twig

213 lines
7.2 KiB
Twig
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{#
calendar.twig - Универсальный компонент календаря
Параметры:
- events: Массив событий
Пример:
events: [
{
id: 1,
title: 'Событие 1',
date: '2026-01-15',
color: '#3B82F6',
url: '/path/to/event'
}
]
- currentMonth: Текущий месяц в формате YYYY-MM
- prevMonth: URL или параметр для предыдущего месяца
- nextMonth: URL или параметр для следующего месяца
- eventComponent: Имя Twig компонента для рендеринга событий (опционально)
- onEventClick: JavaScript функция при клике на событие (опционально)
- showLegend: Показывать легенду (опционально, по умолчанию true)
- legend: Массив для легенды (опционально)
Пример:
legend: [
{ name: 'Этап 1', color: '#3B82F6' }
]
#}
{# Навигация по месяцам #}
{% if showNavigation|default(true) %}
<div class="card shadow-sm mb-4">
<div class="card-body d-flex justify-content-between align-items-center">
<a href="{{ prevMonth }}" class="btn btn-outline-secondary">
<i class="fa-solid fa-chevron-left me-1"></i>Предыдущий
</a>
<h5 class="mb-0">{{ monthName }}</h5>
<a href="{{ nextMonth }}" class="btn btn-outline-secondary">
Следующий<i class="fa-solid fa-chevron-right ms-1"></i>
</a>
</div>
</div>
{% endif %}
{# Календарь #}
<div class="card shadow-sm">
<div class="card-body p-0">
<div class="calendar">
{# Дни недели #}
<div class="calendar-header bg-light">
{% for day in ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'] %}
<div class="calendar-header-cell text-center py-2 text-muted small fw-normal">
{{ day }}
</div>
{% endfor %}
</div>
{# Сетка календаря #}
<div class="calendar-grid">
{# Пустые ячейки до первого дня #}
{% for i in 0..(firstDayOfWeek - 1) %}
<div class="calendar-cell bg-light"></div>
{% endfor %}
{# Дни месяца #}
{% for day in 1..daysInMonth %}
{% set dateStr = currentMonth ~ '-' ~ (day < 10 ? '0' ~ day : day) %}
{% set dayEvents = eventsByDate[dateStr]|default([]) %}
{% set isToday = dateStr == today %}
<div class="calendar-cell {{ isToday ? 'calendar-cell-today' : '' }}">
<div class="calendar-day-number {{ isToday ? 'text-primary fw-bold' : '' }}">
{{ day }}
</div>
<div class="calendar-events">
{% for event in dayEvents|slice(0, 3) %}
{% if eventComponent is defined %}
{{ include(eventComponent, {event: event}) }}
{% else %}
{{ include('@components/calendar/default_event.twig', {event: event, onEventClick: onEventClick|default('')}) }}
{% endif %}
{% endfor %}
{% if dayEvents|length > 3 %}
<div class="calendar-events-more text-muted small">
+{{ dayEvents|length - 3 }} ещё
</div>
{% endif %}
</div>
</div>
{% endfor %}
{# Пустые ячейки после последнего дня #}
{% set remainingCells = 7 - ((firstDayOfWeek + daysInMonth) % 7) %}
{% if remainingCells < 7 %}
{% for i in 1..remainingCells %}
<div class="calendar-cell bg-light"></div>
{% endfor %}
{% endif %}
</div>
</div>
</div>
</div>
{% block stylesheets %}
<style>
.calendar {
width: 100%;
}
.calendar .calendar-header {
display: grid !important;
grid-template-columns: repeat(7, 1fr) !important;
border-bottom: 1px solid #e5e7eb;
}
.calendar .calendar-header-cell {
padding: 0.75rem 0.5rem;
font-weight: 500;
color: #6b7280;
font-size: 0.875rem;
text-align: center;
}
.calendar .calendar-grid {
display: grid !important;
grid-template-columns: repeat(7, 1fr) !important;
}
.calendar .calendar-cell {
min-height: 100px;
padding: 0.5rem;
border-right: 1px solid #e5e7eb;
border-bottom: 1px solid #e5e7eb;
display: block !important;
}
.calendar .calendar-cell:nth-child(7n) {
border-right: none;
}
.calendar .calendar-cell.bg-light {
background-color: #f9fafb;
}
.calendar .calendar-cell-today {
background-color: #eff6ff;
}
.calendar .calendar-day-number {
font-size: 0.875rem;
margin-bottom: 0.5rem;
}
.calendar .calendar-events {
display: flex !important;
flex-direction: column;
gap: 0.25rem;
}
.calendar .calendar-event {
display: block !important;
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
background-color: #f3f4f6;
border-left: 3px solid #6b7280;
border-radius: 0.25rem;
text-decoration: none;
color: #374151;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.calendar .calendar-event:hover {
background-color: #e5e7eb;
}
.calendar .calendar-events-more {
padding: 0.125rem 0.5rem;
font-size: 0.75rem;
}
</style>
{% endblock %}
{% if showLegend|default(true) and (legend is defined or events is defined) %}
<div class="card shadow-sm mt-4">
<div class="card-body">
<h6 class="card-title">Легенда</h6>
<div class="d-flex flex-wrap gap-2">
{% if legend is defined %}
{% for item in legend %}
<span class="badge"
style="background-color: {{ item.color }}20; color: {{ item.color }}; border: 1px solid {{ item.color }}40;">
{{ item.name }}
</span>
{% endfor %}
{% else %}
{# Автоматическая легенда из типов событий #}
{% set uniqueColors = {} %}
{% for event in events %}
{% if event.color is defined and event.color not in uniqueColors %}
<span class="badge"
style="background-color: {{ event.color }}20; color: {{ event.color }}; border: 1px solid {{ event.color }}40;">
{{ event.title }}
</span>
{% set uniqueColors = uniqueColors|merge([event.color]) %}
{% endif %}
{% endfor %}
{% endif %}
</div>
</div>
</div>
{% endif %}