From 240f734c92ca6913d09da107563898925c865185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=B3=D0=BE=D1=80=D1=8C=20=D0=9D=D0=B0=D0=B4=D0=B5?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Fri, 26 Dec 2025 16:27:09 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20requests=20detail=20view=20-=20stage=20?= =?UTF-8?q?5:=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE=D0=BD=D0=B0?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D0=B8=D0=B7=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=81=D1=82=D0=B0=D1=82?= =?UTF-8?q?=D1=83=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлен обработчик для смены статуса заявления в JavaScript - Интегрирована форма смены статуса в шаблон request_detail.html - Форма видна только администраторам (can_process=True) - Поддерживаются переходы: pending->approved/rejected, *->cancelled - Добавлена валидация и подтверждение перед изменением статуса - Используются API endpoints: /api/v1/requests/{id}/approve/, reject/, cancel/ - Автоматическое обновление содержимого модали после смены статуса - Отображение статуса обновления (loading, success, error) в UI --- backend/static/js/modules/requestDetail.js | 108 ++++++++++++++++++ .../requests_app/request_detail.html | 20 ++++ 2 files changed, 128 insertions(+) diff --git a/backend/static/js/modules/requestDetail.js b/backend/static/js/modules/requestDetail.js index b35413ee..f487124a 100644 --- a/backend/static/js/modules/requestDetail.js +++ b/backend/static/js/modules/requestDetail.js @@ -120,6 +120,12 @@ export class RequestDetailModal { }); } + // Обработчик для кнопок смены статуса + const statusForm = this.container?.querySelector('.request-status-form'); + if (statusForm) { + statusForm.addEventListener('submit', (e) => this.handleStatusChange(e)); + } + // Инициализация emoji picker если доступна this.initEmojiPicker(); @@ -280,6 +286,108 @@ export class RequestDetailModal { } } + /** + * Обработчик изменения статуса заявления + */ + async handleStatusChange(e) { + e.preventDefault(); + + const form = e.target; + const selectField = form.querySelector('select[name="status"]'); + const newStatus = selectField?.value; + + if (!newStatus) { + alert('Пожалуйста, выберите новый статус'); + return; + } + + const confirmMsg = form.dataset.confirmMsg || + `Вы уверены, что хотите изменить статус на "${form.querySelector(`option[value="${newStatus}"]`)?.textContent}"?`; + + if (!confirm(confirmMsg)) { + return; + } + + // Показать состояние загрузки + const submitBtn = form.querySelector('button[type="submit"]'); + const originalBtnText = submitBtn?.textContent || 'Применить'; + if (submitBtn) { + submitBtn.disabled = true; + submitBtn.innerHTML = 'Обновление...'; + } + + try { + // Использовать правильный endpoint для изменения статуса + const action = newStatus === 'approved' ? 'approve' : + newStatus === 'rejected' ? 'reject' : + newStatus === 'cancelled' ? 'cancel' : null; + + if (!action) { + throw new Error('Неподдерживаемый статус'); + } + + const response = await fetch( + `/api/v1/requests/${this.currentRequestId}/${action}/`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': form.querySelector('[name="csrfmiddlewaretoken"]')?.value || '' + }, + credentials: 'same-origin' + } + ); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.detail || `HTTP ${response.status}`); + } + + // Показать успешное сообщение + if (submitBtn) { + const originalClass = submitBtn.className; + submitBtn.className = submitBtn.className.replace('btn-primary', 'btn-success'); + submitBtn.innerHTML = 'Статус обновлен!'; + + // Вернуть кнопку в исходное состояние + setTimeout(() => { + submitBtn.className = originalClass; + submitBtn.innerHTML = originalBtnText; + submitBtn.disabled = false; + }, 2000); + } + + // Перезагрузить содержимое модали + if (this.currentRequestId) { + setTimeout(() => { + this.loadData(this.currentRequestId); + }, 500); + } + } catch (error) { + console.error('Error changing status:', error); + + // Восстановить кнопку при ошибке + if (submitBtn) { + submitBtn.disabled = false; + submitBtn.innerHTML = originalBtnText; + } + + // Показать ошибку в UI + const alertDiv = form.querySelector('.alert'); + if (alertDiv) { + alertDiv.remove(); + } + + const errorAlert = document.createElement('div'); + errorAlert.className = 'alert alert-danger alert-dismissible fade show mb-2'; + errorAlert.innerHTML = ` + Ошибка: ${error.message} + + `; + form.parentNode.insertBefore(errorAlert, form); + } + } + /** * Закрыть модальное окно */ diff --git a/backend/templates/requests_app/request_detail.html b/backend/templates/requests_app/request_detail.html index ab0edaa7..2ebae118 100644 --- a/backend/templates/requests_app/request_detail.html +++ b/backend/templates/requests_app/request_detail.html @@ -85,6 +85,26 @@ Отозвать {% endif %} + + {# Форма смены статуса — для администраторов #} + {% if can_process %} +
+ {% csrf_token %} + + +
+ {% endif %}