Corp/backend/templates/requests_app/request_detail.html
Игорь Надеин a3ea37f824 feat: requests detail view - stage 5: функциональность изменения статуса
- Добавлен обработчик для смены статуса заявления в JavaScript
- Интегрирована форма смены статуса в шаблон request_detail.html
- Форма видна только администраторам (can_process=True)
- Поддерживаются переходы: pending->approved/rejected, *->cancelled
- Добавлена валидация и подтверждение перед изменением статуса
- Используются API endpoints: /api/v1/requests/{id}/approve/, reject/, cancel/
- Автоматическое обновление содержимого модали после смены статуса
- Отображение статуса обновления (loading, success, error) в UI
2025-12-26 16:27:09 +03:00

206 lines
9.6 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.

{% extends 'base.html' %}
{% load static django_bootstrap5 %}
{% block title %}Заявление{% endblock %}
{% block content %}
<div class="container">
<div class="row gx-0">
<div class="col-12">
{% if error %}
{# — Ошибка при загрузке — #}
<div class="alert alert-danger mb-3" role="alert">
<i class="bi-exclamation-circle"></i>
{% if error_code == 404 %}
<strong>Заявление не найдено.</strong> Возможно, оно было удалено.
{% elif error_code == 403 %}
<strong>Доступ запрещен.</strong> Вы не можете просмотреть это заявление.
{% else %}
<strong>Ошибка при загрузке.</strong> {{ error }}
{% endif %}
</div>
<a href="{% url 'requests:request_list' %}" class="btn btn-secondary">
<i class="bi-arrow-left"></i> К списку заявлений
</a>
{% elif request_obj %}
<div class="section-header">
<i class="bi-file-earmark-text fs-2 text-primary"></i>
<h2 class="title mb-0">Детали заявления</h2>
<div class="ms-auto d-flex gap-2">
<a href="{% url 'requests:request_list' %}" class="btn btn-ghost">
<i class="bi-arrow-left"></i> К списку
</a>
</div>
</div>
{# — Карточка заявки — #}
<article class="card mb-3">
<header class="card-header">
<div class="card-icon" style="width:48px;height:48px;">
{% if request_obj.employee.avatar %}
<img src="{{ request_obj.employee.avatar }}" alt="">
{% else %}<i class="bi-person"></i>{% endif %}
</div>
<div class="card-meta flex-grow-1">
<div class="d-flex align-items-center gap-2 flex-wrap">
<span class="card-title">
{{ request_obj.employee.get_full_name|default:request_obj.employee.username }}
</span>
{# статус — бейдж #}
{% if request_obj.status == 'pending' %}
<span class="badge bg-secondary-subtle text-secondary-emphasis border border-secondary-subtle">На рассмотрении</span>
{% elif request_obj.status == 'approved' %}
<span class="badge bg-success-subtle text-success-emphasis border border-success-subtle">Одобрено</span>
{% elif request_obj.status == 'rejected' %}
<span class="badge bg-danger-subtle text-danger-emphasis border border-danger-subtle">Отклонено</span>
{% elif request_obj.status == 'cancelled' %}
<span class="badge bg-dark-subtle text-dark-emphasis border border-dark-subtle">Отменено</span>
{% elif request_obj.status == 'draft' %}
<span class="badge bg-light text-body border">Черновик</span>
{% else %}
<span class="badge bg-light text-body border">Неизвестно</span>
{% endif %}
{# тип заявки #}
<span class="badge bg-primary-subtle text-primary-emphasis border border-primary-subtle">
{{ request_obj.get_type_display }}
</span>
</div>
<div class="card-subtitle mt-1">
<time datetime="{{ request_obj.created_at|date:'c' }}">{{ request_obj.created_at|date:"d.m.Y H:i" }}</time>
{% if request_obj.date_from or request_obj.date_to %}
· Период: {{ request_obj.date_from|date:'d.m.Y' }}{% if request_obj.date_to %} — {{ request_obj.date_to|date:'d.m.Y' }}{% endif %}
{% endif %}
</div>
</div>
{# Отозвать — только если можно (ожидание и не финальное) #}
{% if request_obj.status == 'pending' and not request_obj.is_final %}
<a href="{% url 'requests_app:request_cancel' request_obj.pk %}"
class="btn btn-ghost ms-auto">
<i class="bi-x-circle"></i> Отозвать
</a>
{% endif %}
{# Форма смены статуса — для администраторов #}
{% if can_process %}
<form class="request-status-form ms-auto d-flex gap-2" method="post">
{% csrf_token %}
<select class="form-select form-select-sm" name="status" style="max-width: 200px;">
<option value="">Изменить статус...</option>
{% if request_obj.status == 'pending' %}
<option value="approved">✓ Одобрить</option>
<option value="rejected">✗ Отклонить</option>
{% endif %}
{% if not request_obj.is_final %}
<option value="cancelled">⊗ Отменить</option>
{% endif %}
</select>
<button type="submit" class="btn btn-sm btn-primary" {% if not request_obj.status == 'pending' and request_obj.is_final %}disabled{% endif %}>
Применить
</button>
</form>
{% endif %}
</header>
<div class="card-body pt-0">
<div class="mb-2">
<span class="text-secondary small">Комментарий:</span>
<div>{{ request_obj.comment|default:"—" }}</div>
</div>
{% if request_obj.attachment_url %}
<a class="btn btn-ghost d-inline-flex align-items-center gap-1"
href="{{ request_obj.attachment_url }}" target="_blank" rel="noopener">
<i class="bi-paperclip"></i><span>Вложение</span>
</a>
{% endif %}
</div>
</article>
<!--
{# — Комментарии — #}
<article class="card" id="comments">
<header class="card-header">
<div class="card-icon" style="width:44px;height:44px;">
<i class="bi-chat-dots"></i>
</div>
<div class="card-meta">
<div class="card-title">Комментарии</div>
<div class="card-subtitle">Всего: <span class="comments-count">{{ comments|length }}</span></div>
</div>
</header>
<div class="card-body pt-0">
{% if comments %}
<div class="d-flex flex-column gap-2 mb-3">
{% for c in comments %}
<div class="bg-body-tertiary border rounded p-2">
<div class="small d-flex justify-content-between align-items-start">
<div>
<span class="fw-semibold">{{ c.author.full_name|default:c.author_name }}</span>
<span class="text-secondary">·</span>
<time class="text-muted">{{ c.created_at }}</time>
</div>
{# удалить — только автору комментария (права дополнительно проверяются во вьюхе) #}
{% if c.author_id == request.user.id %}
<form method="post" action="{% url 'requests_app:request_comment_delete' request_obj.pk c.id %}">
{% csrf_token %}
<button class="btn btn-sm btn-ghost text-danger" type="submit" title="Удалить">
<i class="bi-trash"></i>
</button>
</form>
{% endif %}
</div>
<div class="small mt-1">{{ c.text|linebreaksbr }}</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-secondary small mb-3">Комментариев пока нет.</div>
{% endif %}
{# Добавить комментарий — #}
<form class="mt-2 request-comment-form" method="post" action="{% url 'requests_app:request_comment_add' request_obj.pk %}">
{% csrf_token %}
<div class="message-field message-field--compact">
<!-- Кнопка выбора эмодзи -->
<div class="dropdown message-emoji">
<button type="button" class="btn btn-ghost btn-emoji message-icon-btn"
data-bs-toggle="dropdown"
aria-expanded="false"
title="Вставить смайлик">
<i class="bi-emoji-smile"></i>
</button>
<div class="dropdown-menu dropdown-menu-start message-emoji-menu">
<emoji-picker data-emoji-picker class="chat-emoji-picker"></emoji-picker>
</div>
</div>
<textarea name="text" class="form-control message-input" rows="1" placeholder="Оставить комментарий…" maxlength="1000"></textarea>
<button class="btn btn-primary message-send" type="submit" title="Отправить"><i class="bi-send"></i></button>
</div>
</form>
</div>
</article>
{% else %}
<div class="alert alert-info mb-3" role="alert">
Загрузка...
</div>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
{{ block.super }}
<script src="{% static 'js/components/requestComments.js' %}"></script>
<!-- Emoji Picker Library -->
<script type="module" src="https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js"></script>
{% endblock %}