Добавлена обработка ValidationError в AJAX API и Bootstrap alert на странице списка заказов
Проблема:
На странице списка заказов (order_list) при изменении статуса 'на лету':
- ValidationError показывался через alert() - страшно
- Сообщение содержало служебные элементы
- Статус всё равно менялся визуально (нет отката)
Решение Backend (views.py):
- В set_order_status добавлена обработка ValidationError ПЕРЕД ValueError
- Извлекается чистое сообщение (e.messages[0] или str(e))
- Возвращается JSON: {success: false, error: 'чистое сообщение'}
Решение Frontend (order_list.html):
- Добавлен контейнер для динамических Bootstrap alert
- Создана функция showAlert() для показа красивых alert-danger
- При ошибке:
* Показывается Bootstrap alert с иконкой
* Прокрутка к верху страницы
* Автоскрытие через 5 секунд
* Возврат select к предыдущему значению (откат визуально)
- Больше НЕТ страшных alert()
Теперь пользователь видит:
[красный Bootstrap alert вверху страницы]
⚠️ Заказ 134 был отменён, товары проданы в другом заказе.
Невозможно изменить статус. Для новой продажи создайте новый заказ.
[X]
User-friendly на обеих страницах (форма редактирования + список)!
This commit is contained in:
@@ -8,6 +8,9 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Контейнер для динамических alert-сообщений -->
|
||||
<div id="js-alert-container"></div>
|
||||
|
||||
<!-- Фильтры сверху -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
@@ -242,6 +245,28 @@
|
||||
(function() {
|
||||
const csrfToken = '{{ csrf_token }}';
|
||||
|
||||
// Функция для показа Bootstrap alert
|
||||
function showAlert(message, type = 'danger') {
|
||||
const container = document.getElementById('js-alert-container');
|
||||
const alertDiv = document.createElement('div');
|
||||
alertDiv.className = `alert alert-${type} alert-dismissible fade show`;
|
||||
alertDiv.setAttribute('role', 'alert');
|
||||
alertDiv.innerHTML = `
|
||||
<i class="bi bi-exclamation-triangle-fill"></i> ${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
`;
|
||||
container.appendChild(alertDiv);
|
||||
|
||||
// Авто-скрытие через 5 секунд
|
||||
setTimeout(() => {
|
||||
alertDiv.classList.remove('show');
|
||||
setTimeout(() => alertDiv.remove(), 150);
|
||||
}, 5000);
|
||||
|
||||
// Прокрутка к верху страницы
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
async function updateStatus(orderNumber, statusId) {
|
||||
const body = new URLSearchParams({ status_id: statusId }).toString();
|
||||
const resp = await fetch(`/orders/api/${orderNumber}/set-status/`, {
|
||||
@@ -278,6 +303,7 @@
|
||||
select.addEventListener('change', async function() {
|
||||
const statusId = select.value || '';
|
||||
const selectedOption = select.options[select.selectedIndex];
|
||||
const originalIndex = select.selectedIndex;
|
||||
select.disabled = true;
|
||||
|
||||
try {
|
||||
@@ -294,10 +320,20 @@
|
||||
select.style.display = 'none';
|
||||
badge.style.display = 'inline-block';
|
||||
} else {
|
||||
alert(result.error || 'Не удалось обновить статус');
|
||||
// Показываем красивый Bootstrap alert
|
||||
showAlert(result.error || 'Не удалось обновить статус', 'danger');
|
||||
// Возвращаем предыдущее значение
|
||||
select.selectedIndex = originalIndex;
|
||||
// Прячем select, показываем badge
|
||||
select.style.display = 'none';
|
||||
badge.style.display = 'inline-block';
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Ошибка сервера при обновлении статуса');
|
||||
showAlert('Ошибка сервера при обновлении статуса', 'danger');
|
||||
// Возвращаем предыдущее значение
|
||||
select.selectedIndex = originalIndex;
|
||||
select.style.display = 'none';
|
||||
badge.style.display = 'inline-block';
|
||||
} finally {
|
||||
select.disabled = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user