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 %}