Files
ACORE/templates/pages/calendar.html
T
lokses 3e969e29dc visit_manager: события календаря
Модель и сервис событий календаря, вью и страница календаря.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-09 18:03:44 +03:00

205 lines
17 KiB
HTML
Raw 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.
{% load i18n %}
<div class="page-content" data-cal-scope="{{ cal_scope }}">
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:1.5rem; flex-wrap:wrap; gap:0.75rem;">
<div>
<h1 style="font-size:1.25rem; font-weight:500; color:var(--color-foreground);">{% trans "Календарь" %}</h1>
<p style="font-size:0.875rem; color:var(--color-muted-foreground); margin-top:0.25rem;">{% trans "Праздники, отпуска, смены, дни рождения и события" %}</p>
</div>
<button type="button" class="btn btn-primary" onclick="calEventFormToggle(true)">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="12" x2="12" y1="5" y2="19"/><line x1="5" x2="19" y1="12" y2="12"/>
</svg>
{% trans "Новое событие" %}
</button>
</div>
{% if cal_form_message %}
<div style="margin-bottom:1rem; padding:0.75rem 1rem; border-radius:0.5rem; border:1px solid hsl(152 40% 70%); background:color-mix(in srgb, var(--color-success) 12%, var(--color-card)); color:var(--color-foreground); font-size:0.875rem;">
{{ cal_form_message }}
</div>
{% endif %}
{% if cal_form_errors %}
<div style="margin-bottom:1rem; padding:0.75rem 1rem; border-radius:0.5rem; background:hsl(0 70% 96%); border:1px solid hsl(0 70% 85%); color:hsl(0 55% 35%); font-size:0.8125rem;">
{% for err in cal_form_errors %}<div>{{ err }}</div>{% endfor %}
</div>
{% endif %}
<div id="cal-event-form-wrap" style="{% if not cal_form_open %}display:none;{% endif %} margin-bottom:1.25rem;">
<form method="post" action="/calendar/events/"
hx-post="/calendar/events/" hx-target="#main-content" hx-indicator="#loader, find .btn-label-loading" hx-disabled-elt="find button[type=submit]"
style="background:var(--color-card); border:1px solid var(--color-border); border-radius:0.75rem; padding:1.25rem; display:flex; flex-direction:column; gap:0.75rem;">
{% csrf_token %}
<input type="hidden" name="month" value="{{ cal_month_str }}">
<input type="hidden" name="scope" value="{{ cal_scope }}">
<div style="display:flex; align-items:center; justify-content:space-between;">
<span style="font-size:0.9375rem; font-weight:600; color:var(--color-foreground);">{% trans "Новое событие" %}</span>
<button type="button" class="btn btn-ghost btn-icon" onclick="calEventFormToggle(false)" aria-label="{% trans 'Закрыть' %}">×</button>
</div>
<div style="display:grid; grid-template-columns:2fr 1fr 1fr; gap:0.75rem;">
<div>
<label style="display:block; font-size:0.75rem; color:var(--color-muted-foreground); margin-bottom:0.25rem;">{% trans "Название" %} *</label>
<input type="text" name="title" required class="input" style="width:100%;" value="{{ cal_form_data.title|default:'' }}">
</div>
<div>
<label style="display:block; font-size:0.75rem; color:var(--color-muted-foreground); margin-bottom:0.25rem;">{% trans "Тип" %}</label>
<select name="event_type" class="input" style="width:100%;">
{% for value, label in cal_event_types %}
<option value="{{ value }}" {% if cal_form_data.event_type == value %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
<div>
<label style="display:block; font-size:0.75rem; color:var(--color-muted-foreground); margin-bottom:0.25rem;">{% trans "Видимость" %}</label>
<select name="visibility" class="input" style="width:100%;">
<option value="company" {% if cal_form_data.visibility != 'personal' %}selected{% endif %}>{% trans "Компания" %}</option>
<option value="personal" {% if cal_form_data.visibility == 'personal' %}selected{% endif %}>{% trans "Личное" %}</option>
</select>
</div>
</div>
<div style="display:grid; grid-template-columns:repeat(4,1fr); gap:0.75rem;">
<div>
<label style="display:block; font-size:0.75rem; color:var(--color-muted-foreground); margin-bottom:0.25rem;">{% trans "Дата с" %} *</label>
<input type="date" name="date_from" required class="input" style="width:100%;" value="{{ cal_form_data.date_from|default:cal_default_date }}">
</div>
<div>
<label style="display:block; font-size:0.75rem; color:var(--color-muted-foreground); margin-bottom:0.25rem;">{% trans "Дата по" %}</label>
<input type="date" name="date_to" class="input" style="width:100%;" value="{{ cal_form_data.date_to|default:'' }}">
</div>
<div>
<label style="display:block; font-size:0.75rem; color:var(--color-muted-foreground); margin-bottom:0.25rem;">{% trans "Время с" %}</label>
<input type="time" name="time_from" class="input" style="width:100%;" value="{{ cal_form_data.time_from|default:'' }}">
</div>
<div>
<label style="display:block; font-size:0.75rem; color:var(--color-muted-foreground); margin-bottom:0.25rem;">{% trans "Время по" %}</label>
<input type="time" name="time_to" class="input" style="width:100%;" value="{{ cal_form_data.time_to|default:'' }}">
</div>
</div>
<div style="display:grid; grid-template-columns:1fr 2fr; gap:0.75rem;">
<div>
<label style="display:block; font-size:0.75rem; color:var(--color-muted-foreground); margin-bottom:0.25rem;">{% trans "Место" %}</label>
<input type="text" name="location" class="input" style="width:100%;" value="{{ cal_form_data.location|default:'' }}">
</div>
<div>
<label style="display:block; font-size:0.75rem; color:var(--color-muted-foreground); margin-bottom:0.25rem;">{% trans "Примечания" %}</label>
<input type="text" name="notes" class="input" style="width:100%;" value="{{ cal_form_data.notes|default:'' }}">
</div>
</div>
<div style="display:flex; justify-content:flex-end; gap:0.5rem;">
<button type="button" class="btn btn-secondary" onclick="calEventFormToggle(false)">{% trans "Отмена" %}</button>
<button type="submit" class="btn btn-primary">
{% trans "Сохранить" %}
<span class="btn-label-loading htmx-indicator">…</span>
</button>
</div>
</form>
</div>
<div style="background:var(--color-card); border-radius:0.75rem; border:1px solid var(--color-border); box-shadow:0 1px 2px rgb(0 0 0/0.05); padding:1.5rem;">
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:1.25rem; flex-wrap:wrap; gap:0.75rem;">
<div style="display:flex; align-items:center; gap:0.5rem;">
<a href="/calendar/?month={{ cal_prev_month }}&scope={{ cal_scope }}"
hx-get="/calendar/?month={{ cal_prev_month }}&scope={{ cal_scope }}"
hx-target="#main-content" hx-push-url="true" hx-indicator="#loader"
class="btn btn-ghost btn-icon">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg>
</a>
<span style="font-size:1rem; font-weight:600; color:var(--color-foreground); min-width:10rem; text-align:center;">{{ cal_month_label }}</span>
<a href="/calendar/?month={{ cal_next_month }}&scope={{ cal_scope }}"
hx-get="/calendar/?month={{ cal_next_month }}&scope={{ cal_scope }}"
hx-target="#main-content" hx-push-url="true" hx-indicator="#loader"
class="btn btn-ghost btn-icon">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>
</a>
</div>
<div style="display:flex; gap:0.25rem; background:var(--color-muted); border-radius:var(--radius); padding:0.25rem;">
<a href="/calendar/?month={{ cal_month_str }}&scope=mine"
hx-get="/calendar/?month={{ cal_month_str }}&scope=mine"
hx-target="#main-content" hx-push-url="true" hx-indicator="#loader"
class="btn" style="font-size:0.8125rem; padding:0.3rem 0.875rem; border:none; text-decoration:none;{% if cal_scope == 'mine' %} background:var(--color-card); color:var(--color-card-foreground); box-shadow:0 1px 2px rgb(0 0 0/0.08);{% else %} background:transparent; color:var(--color-muted-foreground);{% endif %}">{% trans "Мои" %}</a>
<a href="/calendar/?month={{ cal_month_str }}&scope=team"
hx-get="/calendar/?month={{ cal_month_str }}&scope=team"
hx-target="#main-content" hx-push-url="true" hx-indicator="#loader"
class="btn" style="font-size:0.8125rem; padding:0.3rem 0.875rem; border:none; text-decoration:none;{% if cal_scope == 'team' %} background:var(--color-card); color:var(--color-card-foreground); box-shadow:0 1px 2px rgb(0 0 0/0.08);{% else %} background:transparent; color:var(--color-muted-foreground);{% endif %}">{% trans "Команда" %}</a>
</div>
</div>
<div style="display:grid; grid-template-columns:repeat(7,1fr); gap:1px; margin-bottom:0.5rem;">
<div style="text-align:center; font-size:0.75rem; font-weight:600; color:var(--color-muted-foreground); padding:0.5rem;">{% trans "Пн" %}</div>
<div style="text-align:center; font-size:0.75rem; font-weight:600; color:var(--color-muted-foreground); padding:0.5rem;">{% trans "Вт" %}</div>
<div style="text-align:center; font-size:0.75rem; font-weight:600; color:var(--color-muted-foreground); padding:0.5rem;">{% trans "Ср" %}</div>
<div style="text-align:center; font-size:0.75rem; font-weight:600; color:var(--color-muted-foreground); padding:0.5rem;">{% trans "Чт" %}</div>
<div style="text-align:center; font-size:0.75rem; font-weight:600; color:var(--color-muted-foreground); padding:0.5rem;">{% trans "Пт" %}</div>
<div style="text-align:center; font-size:0.75rem; font-weight:600; color:var(--color-destructive); padding:0.5rem;">{% trans "Сб" %}</div>
<div style="text-align:center; font-size:0.75rem; font-weight:600; color:var(--color-destructive); padding:0.5rem;">{% trans "Нд" %}</div>
</div>
<div style="display:grid; grid-template-columns:repeat(7,1fr); gap:4px;" id="cal-grid">
{% for week in cal_weeks %}
{% for cell in week %}
{% if cell %}
<div style="min-height:6.5rem; padding:0.375rem; border:1px solid {% if cell.is_today %}var(--color-primary){% else %}var(--color-border){% endif %}; border-radius:0.375rem; background:{% if cell.is_today %}color-mix(in srgb, var(--color-primary) 6%, var(--color-background)){% elif cell.is_holiday or cell.is_weekend %}var(--color-muted){% else %}var(--color-background){% endif %};{% if cell.is_today %} border-width:2px;{% endif %}">
<span style="font-size:0.8125rem; font-weight:{% if cell.is_today %}600{% else %}500{% endif %}; color:{% if cell.is_today %}var(--color-primary){% elif cell.is_weekend or cell.is_holiday %}var(--color-muted-foreground){% else %}var(--color-foreground){% endif %};">{{ cell.day }}</span>
<div style="margin-top:0.25rem; display:flex; flex-direction:column; gap:2px;">
{% for ev in cell.events %}
<div class="{{ ev.css }}" title="{{ ev.label }}"
style="font-size:0.6875rem; padding:2px 5px; border-radius:3px; line-height:1.25; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; display:flex; align-items:center; gap:0.25rem;">
<span style="min-width:0; overflow:hidden; text-overflow:ellipsis;">{{ ev.label }}</span>
{% if ev.kind == 'event' and ev.meta.can_delete %}
<form method="post" action="/calendar/events/{{ ev.meta.id }}/delete/"
hx-post="/calendar/events/{{ ev.meta.id }}/delete/"
hx-target="#main-content" hx-indicator="#loader"
style="margin:0; flex:none;"
onsubmit="return confirm('{% trans "Удалить событие?" %}');">
{% csrf_token %}
<input type="hidden" name="month" value="{{ cal_month_str }}">
<input type="hidden" name="scope" value="{{ cal_scope }}">
<button type="submit" style="border:none; background:transparent; cursor:pointer; padding:0; font-size:0.75rem; line-height:1; color:inherit; opacity:0.7;" title="{% trans 'Удалить' %}">×</button>
</form>
{% endif %}
</div>
{% endfor %}
</div>
</div>
{% else %}
<div style="min-height:6.5rem;"></div>
{% endif %}
{% endfor %}
{% endfor %}
</div>
<div style="display:flex; align-items:center; gap:1.25rem; margin-top:1.25rem; padding-top:1rem; border-top:1px solid var(--color-border); flex-wrap:wrap;">
<div style="display:flex; align-items:center; gap:0.375rem; font-size:0.75rem; color:var(--color-muted-foreground);">
<span class="cal-chip-holiday" style="display:inline-block; width:10px; height:10px; border-radius:2px;"></span>{% trans "Праздник" %}
</div>
<div style="display:flex; align-items:center; gap:0.375rem; font-size:0.75rem; color:var(--color-muted-foreground);">
<span class="cal-chip-leave" style="display:inline-block; width:10px; height:10px; border-radius:2px;"></span>{% trans "Отпуск" %}
</div>
<div style="display:flex; align-items:center; gap:0.375rem; font-size:0.75rem; color:var(--color-muted-foreground);">
<span class="cal-chip-birthday" style="display:inline-block; width:10px; height:10px; border-radius:2px;"></span>{% trans "День рождения" %}
</div>
<div style="display:flex; align-items:center; gap:0.375rem; font-size:0.75rem; color:var(--color-muted-foreground);">
<span class="cal-chip-shift" style="display:inline-block; width:10px; height:10px; border-radius:2px;"></span>{% trans "Смена" %}
</div>
<div style="display:flex; align-items:center; gap:0.375rem; font-size:0.75rem; color:var(--color-muted-foreground);">
<span class="cal-chip-interview" style="display:inline-block; width:10px; height:10px; border-radius:2px;"></span>{% trans "Собеседование" %}
</div>
<div style="display:flex; align-items:center; gap:0.375rem; font-size:0.75rem; color:var(--color-muted-foreground);">
<span class="cal-chip-event" style="display:inline-block; width:10px; height:10px; border-radius:2px;"></span>{% trans "Событие" %}
</div>
</div>
</div>
</div>
<style>
.cal-chip-holiday { background:#fef2f2; color:#b91c1c; border:1px solid #fecaca; }
.cal-chip-leave { background:#f0fdf4; color:#15803d; border:1px solid #bbf7d0; }
.cal-chip-leave-pending { background:#fff7ed; color:#c2410c; border:1px solid #fed7aa; }
.cal-chip-birthday { background:#fdf4ff; color:#a21caf; border:1px solid #f5d0fe; }
.cal-chip-shift { background:#f8fafc; color:#475569; border:1px solid #e2e8f0; }
.cal-chip-interview { background:#eff6ff; color:#1d4ed8; border:1px solid #bfdbfe; }
.cal-chip-meeting { background:#ecfeff; color:#0e7490; border:1px solid #a5f3fc; }
.cal-chip-event { background:#fef9c3; color:#a16207; border:1px solid #fde68a; }
</style>