87cb9de005
- Task.STATUS_CHOICES/PRIORITY_CHOICES переведены на русские msgid
(консистентно с ru-базой); миграция choices
- task_detail_view: метки ролей через gettext
- Шаблоны tasks/task-detail/task-edit размечены {% trans %}; статусы,
приоритеты, чек-лист, комментарии, участники, кнопки действий — uk/en
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
151 lines
13 KiB
HTML
151 lines
13 KiB
HTML
{% load i18n %}
|
||
<div class="page-content">
|
||
|
||
<!-- Заголовок -->
|
||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:1.25rem; 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>
|
||
</div>
|
||
|
||
{{ department_tree|json_script:"department-tree-data" }}
|
||
|
||
{% if form_error %}
|
||
<div style="background:color-mix(in srgb, var(--color-destructive) 8%, transparent); border:1px solid color-mix(in srgb, var(--color-destructive) 30%, transparent); border-radius:0.75rem; padding:0.875rem 1.25rem; margin-bottom:1rem; font-size:0.875rem; color:var(--color-destructive);">
|
||
{{ form_error }}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Створення задачі -->
|
||
<details style="margin-bottom:1.25rem;" {% if form_error or form_open %}open{% endif %}>
|
||
<summary class="btn btn-primary" style="display:inline-flex; align-items:center; gap:0.375rem; cursor:pointer; list-style:none; width:fit-content;">
|
||
<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 "Новая задача" %}
|
||
</summary>
|
||
<form hx-post="/tasks/" hx-target="#main-content" hx-indicator="#loader"
|
||
style="margin-top:0.75rem; background:var(--color-card); border:1px solid var(--color-border); border-radius:0.75rem; padding:1.25rem; display:flex; flex-direction:column; gap:0.875rem;">
|
||
{% csrf_token %}
|
||
<input type="text" name="title" required maxlength="250" placeholder="{% trans 'Название задачи' %}" class="input" style="width:100%; font-weight:500;">
|
||
<textarea name="description" placeholder="{% trans 'Описание задачи (необязательно)...' %}"
|
||
style="width:100%; min-height:5rem; resize:vertical; background:var(--color-card); border:1px solid var(--color-input); border-radius:var(--radius); padding:0.4375rem 0.75rem; font-size:0.875rem; color:var(--color-foreground); font-family:var(--font-sans); outline:none; box-sizing:border-box;"></textarea>
|
||
|
||
{% trans 'Ответственный' as l_resp %}{% trans '+ Добавить ответственного' as p_resp %}{% trans 'Соисполнители' as l_co %}{% trans '+ Добавить соисполнителя' as p_co %}{% trans 'Наблюдатели' as l_obs %}{% trans '+ Добавить наблюдателя' as p_obs %}
|
||
<div style="display:grid; grid-template-columns:1fr 1fr; gap:1rem;">
|
||
{% include 'partials/people-picker-field.html' with label=l_resp field_name='responsible' options=employee_options selected_ids=preselect_responsible_ids placeholder=p_resp %}
|
||
{% include 'partials/people-picker-field.html' with label=l_co field_name='co_executors' options=employee_options selected_ids=empty_ids placeholder=p_co %}
|
||
{% include 'partials/people-picker-field.html' with label=l_obs field_name='observers' options=employee_options selected_ids=empty_ids placeholder=p_obs %}
|
||
</div>
|
||
|
||
<div style="display:flex; align-items:center; gap:1rem; flex-wrap:wrap;">
|
||
<select name="priority" class="input" style="width:auto; font-size:0.8125rem;">
|
||
{% for value, label in priorities %}
|
||
<option value="{{ value }}" {% if value == 'medium' %}selected{% endif %}>{{ label }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
<input type="date" name="due_date" class="input" style="width:auto; font-size:0.8125rem;">
|
||
<button type="submit" class="btn btn-primary" style="margin-left:auto;">{% trans "Создать задачу" %}</button>
|
||
</div>
|
||
</form>
|
||
</details>
|
||
<script>initPeoplePickers(document);</script>
|
||
|
||
<!-- Таби -->
|
||
<div style="display:flex; gap:0.25rem; margin-bottom:1rem; background:var(--color-muted); border-radius:var(--radius); padding:0.25rem; width:fit-content;">
|
||
{% with base="display:inline-flex; align-items:center; gap:0.375rem; padding:0.375rem 0.75rem; border-radius:calc(var(--radius) - 2px); font-size:0.8125rem; font-weight:500; text-decoration:none; cursor:pointer;" %}
|
||
<a href="/tasks/" hx-get="/tasks/" hx-target="#main-content" hx-push-url="/tasks/" hx-indicator="#loader"
|
||
style="{{ base }} {% if not active_status %}background:var(--color-primary); color:var(--color-primary-foreground);{% else %}background:transparent; color:var(--color-muted-foreground);{% endif %}">
|
||
{% trans "Все" %} <span style="background:{% if not active_status %}rgba(255,255,255,0.25){% else %}var(--color-border){% endif %}; border-radius:999px; padding:0 0.375rem; font-size:0.6875rem; line-height:1.6;">{{ counts.all }}</span>
|
||
</a>
|
||
<a href="/tasks/?status=pending" hx-get="/tasks/?status=pending" hx-target="#main-content" hx-push-url="/tasks/?status=pending" hx-indicator="#loader"
|
||
style="{{ base }} {% if active_status == 'pending' %}background:var(--color-primary); color:var(--color-primary-foreground);{% else %}background:transparent; color:var(--color-muted-foreground);{% endif %}">
|
||
{% trans "Ожидает" %} <span style="background:{% if active_status == 'pending' %}rgba(255,255,255,0.25){% else %}var(--color-border){% endif %}; border-radius:999px; padding:0 0.375rem; font-size:0.6875rem; line-height:1.6;">{{ counts.pending }}</span>
|
||
</a>
|
||
<a href="/tasks/?status=in_progress" hx-get="/tasks/?status=in_progress" hx-target="#main-content" hx-push-url="/tasks/?status=in_progress" hx-indicator="#loader"
|
||
style="{{ base }} {% if active_status == 'in_progress' %}background:var(--color-primary); color:var(--color-primary-foreground);{% else %}background:transparent; color:var(--color-muted-foreground);{% endif %}">
|
||
{% trans "В работе" %} <span style="background:{% if active_status == 'in_progress' %}rgba(255,255,255,0.25){% else %}var(--color-border){% endif %}; border-radius:999px; padding:0 0.375rem; font-size:0.6875rem; line-height:1.6;">{{ counts.in_progress }}</span>
|
||
</a>
|
||
<a href="/tasks/?status=done" hx-get="/tasks/?status=done" hx-target="#main-content" hx-push-url="/tasks/?status=done" hx-indicator="#loader"
|
||
style="{{ base }} {% if active_status == 'done' %}background:var(--color-primary); color:var(--color-primary-foreground);{% else %}background:transparent; color:var(--color-muted-foreground);{% endif %}">
|
||
{% trans "Выполнено" %} <span style="background:{% if active_status == 'done' %}rgba(255,255,255,0.25){% else %}var(--color-border){% endif %}; border-radius:999px; padding:0 0.375rem; font-size:0.6875rem; line-height:1.6;">{{ counts.done }}</span>
|
||
</a>
|
||
{% endwith %}
|
||
</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); overflow:hidden;">
|
||
|
||
{% for task in tasks %}
|
||
<div class="task-row li-hover" style="{% if not forloop.last %}border-bottom:1px solid var(--color-border);{% endif %} {% if task.status == 'done' %}background:var(--color-muted);{% endif %}">
|
||
<form hx-post="/tasks/{{ task.pk }}/toggle/" hx-target="#main-content" hx-indicator="#loader" style="margin:0; display:flex;">
|
||
{% csrf_token %}
|
||
<input type="checkbox" {% if task.status == 'done' %}checked{% endif %}
|
||
onchange="htmx.trigger(this.form, 'submit')"
|
||
style="width:1rem; height:1rem; flex-shrink:0; accent-color:var(--color-primary); cursor:pointer; margin-top:2px;">
|
||
</form>
|
||
<div style="flex:1; min-width:0; cursor:pointer;"
|
||
hx-get="/tasks/{{ task.pk }}/" hx-target="#main-content" hx-push-url="/tasks/{{ task.pk }}/" hx-indicator="#loader">
|
||
<div style="font-size:0.875rem; font-weight:500; color:{% if task.status == 'done' %}var(--color-muted-foreground); text-decoration:line-through;{% else %}var(--color-card-foreground);{% endif %}">{{ task.title }}</div>
|
||
<div style="font-size:0.75rem; color:var(--color-muted-foreground); margin-top:0.0625rem;">
|
||
{% if task.description %}{{ task.description|truncatechars:90 }}{% endif %}
|
||
{% if task.display_people %}
|
||
<span style="opacity:0.8;">
|
||
→
|
||
{% for a in task.display_people %}{{ a.get_full_name }}{% if not forloop.last %}, {% endif %}{% endfor %}
|
||
</span>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
<div class="task-meta">
|
||
{% if task.status == 'done' %}
|
||
<span style="display:inline-flex; align-items:center; gap:0.25rem; font-size:0.75rem; color:hsl(152 55% 35%);">
|
||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>{% trans "Выполнено" %}
|
||
</span>
|
||
{% elif task.status == 'in_progress' %}
|
||
<span style="display:inline-flex; align-items:center; gap:0.25rem; font-size:0.75rem; color:hsl(152 55% 35%);">
|
||
<span style="width:6px; height:6px; border-radius:50%; background:hsl(152 55% 45%); flex-shrink:0;"></span>{% trans "В работе" %}
|
||
</span>
|
||
{% elif task.status == 'cancelled' %}
|
||
<span style="display:inline-flex; align-items:center; gap:0.25rem; font-size:0.75rem; color:var(--color-muted-foreground);">
|
||
<span style="width:6px; height:6px; border-radius:50%; background:var(--color-muted-foreground); flex-shrink:0;"></span>{% trans "Отменено" %}
|
||
</span>
|
||
{% else %}
|
||
<span style="display:inline-flex; align-items:center; gap:0.25rem; font-size:0.75rem; color:hsl(38 70% 35%);">
|
||
<span style="width:6px; height:6px; border-radius:50%; background:hsl(38 92% 50%); flex-shrink:0;"></span>{% trans "Ожидает" %}
|
||
</span>
|
||
{% endif %}
|
||
{% if task.due_date %}
|
||
<span class="task-date" style="display:inline-flex; align-items:center; gap:0.25rem; font-size:0.75rem; color:{% if task.is_overdue %}var(--color-destructive); font-weight:600;{% else %}var(--color-muted-foreground);{% endif %}">
|
||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>{{ task.due_date|date:"d.m.Y" }}
|
||
</span>
|
||
{% endif %}
|
||
<span class="badge {{ task.priority_badge }}">{{ task.get_priority_display }}</span>
|
||
{% if task.can_edit %}
|
||
<a hx-get="/tasks/{{ task.pk }}/edit/" hx-target="#main-content" hx-push-url="/tasks/{{ task.pk }}/edit/" hx-indicator="#loader"
|
||
class="btn btn-ghost btn-icon" title="{% trans 'Редактировать' %}" style="padding:0.25rem; color:var(--color-muted-foreground); margin-left:auto; cursor:pointer;">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||
</a>
|
||
{% endif %}
|
||
{% if task.can_delete %}
|
||
<form hx-post="/tasks/{{ task.pk }}/delete/" hx-target="#main-content" hx-push-url="/tasks/" hx-indicator="#loader"
|
||
hx-confirm="{% trans 'Удалить задачу' %} «{{ task.title }}»?" style="margin:0; display:flex;">
|
||
{% csrf_token %}
|
||
<button type="submit" class="btn btn-ghost btn-icon" title="{% trans 'Удалить' %}" style="padding:0.25rem; color:var(--color-muted-foreground);">
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg>
|
||
</button>
|
||
</form>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% empty %}
|
||
<div style="padding:2.5rem; text-align:center; color:var(--color-muted-foreground); font-size:0.875rem;">
|
||
{% if active_status %}{% trans "Задач с таким статусом нет" %}{% else %}{% trans "Задач пока нет — создайте первую" %}{% endif %}
|
||
</div>
|
||
{% endfor %}
|
||
|
||
</div>
|
||
|
||
</div>
|