Добавлено быстрое создание тегов на странице списка

API функционал:
- Создан endpoint /api/tags/create/ для AJAX создания тегов
- Валидация: пустое имя, длина, уникальность (регистронезависимо)
- Автоматическая генерация slug через модель
- Возврат JSON с данными созданного тега

UI функционал на странице списка тегов:
- Панель быстрого создания с крупным полем ввода
- Автофокус на поле при загрузке страницы
- Создание тега по нажатию Enter или клику на кнопку
- Индикатор загрузки (спиннер) во время создания
- Блокировка поля/кнопки во время запроса
- Автоматическая перезагрузка страницы после создания
- Возврат фокуса в поле при ошибке
- Красивые alert-сообщения об успехе/ошибке
- CSRF защита

Workflow: ввёл название → Enter → тег создан → фокус снова в поле → можно вводить следующий

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-11 23:26:40 +03:00
parent 1a0360f8c0
commit 1daee88cbd
4 changed files with 193 additions and 1 deletions

View File

@@ -11,6 +11,24 @@
</a>
</div>
<!-- Панель быстрого создания тега -->
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title mb-3">
<i class="bi bi-lightning-charge"></i> Быстрое создание тега
</h5>
<div class="input-group">
<input type="text" id="quick-tag-input" class="form-control form-control-lg"
placeholder="Введите название тега и нажмите Enter..."
autocomplete="off">
<button class="btn btn-success" id="quick-tag-btn" type="button">
<i class="bi bi-plus-circle"></i> Создать
</button>
</div>
<div id="quick-tag-message" class="mt-2"></div>
</div>
</div>
<!-- Панель поиска и фильтров -->
<div class="card mb-4">
<div class="card-body">
@@ -144,4 +162,85 @@
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const input = document.getElementById('quick-tag-input');
const btn = document.getElementById('quick-tag-btn');
const messageDiv = document.getElementById('quick-tag-message');
// Автофокус на поле при загрузке страницы
input.focus();
function createTag() {
const name = input.value.trim();
if (!name) {
showMessage('Введите название тега', 'warning');
return;
}
// Блокируем кнопку и поле во время создания
btn.disabled = true;
input.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Создание...';
fetch('{% url "products:api-tag-create" %}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
},
body: JSON.stringify({name: name})
})
.then(response => response.json())
.then(data => {
if (data.success) {
showMessage(`Тег "${data.tag.name}" успешно создан!`, 'success');
input.value = '';
// Перезагружаем страницу через 1 секунду для обновления списка
setTimeout(() => {
window.location.reload();
}, 1000);
} else {
showMessage('Ошибка: ' + data.error, 'danger');
// Разблокируем поле и кнопку при ошибке
btn.disabled = false;
input.disabled = false;
btn.innerHTML = '<i class="bi bi-plus-circle"></i> Создать';
input.focus();
}
})
.catch(error => {
console.error('Error:', error);
showMessage('Ошибка сети при создании тега', 'danger');
// Разблокируем поле и кнопку при ошибке
btn.disabled = false;
input.disabled = false;
btn.innerHTML = '<i class="bi bi-plus-circle"></i> Создать';
input.focus();
});
}
function showMessage(text, type) {
messageDiv.innerHTML = `<div class="alert alert-${type} alert-dismissible fade show mb-0" role="alert">
${text}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>`;
}
// Обработчик клика по кнопке
btn.addEventListener('click', createTag);
// Обработчик нажатия Enter
input.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
createTag();
}
});
});
</script>
{% endblock %}