Добавлено быстрое создание тегов на странице списка
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:
@@ -80,7 +80,7 @@ from .tag_views import (
|
||||
)
|
||||
|
||||
# API представления
|
||||
from .api_views import search_products_and_variants, validate_kit_cost, create_temporary_kit_api
|
||||
from .api_views import search_products_and_variants, validate_kit_cost, create_temporary_kit_api, create_tag_api
|
||||
|
||||
|
||||
__all__ = [
|
||||
@@ -149,4 +149,5 @@ __all__ = [
|
||||
'search_products_and_variants',
|
||||
'validate_kit_cost',
|
||||
'create_temporary_kit_api',
|
||||
'create_tag_api',
|
||||
]
|
||||
|
||||
@@ -620,3 +620,94 @@ def create_temporary_kit_api(request):
|
||||
'success': False,
|
||||
'error': f'Ошибка при создании комплекта: {str(e)}'
|
||||
}, status=500)
|
||||
|
||||
|
||||
def create_tag_api(request):
|
||||
"""
|
||||
AJAX endpoint для быстрого создания тега из списка тегов.
|
||||
|
||||
Принимает JSON:
|
||||
{
|
||||
"name": "Новый тег"
|
||||
}
|
||||
|
||||
Возвращает JSON:
|
||||
{
|
||||
"success": true,
|
||||
"tag": {
|
||||
"id": 1,
|
||||
"name": "Новый тег",
|
||||
"slug": "novyj-teg",
|
||||
"is_active": true,
|
||||
"products_count": 0,
|
||||
"kits_count": 0
|
||||
}
|
||||
}
|
||||
|
||||
Или при ошибке:
|
||||
{
|
||||
"success": false,
|
||||
"error": "Описание ошибки"
|
||||
}
|
||||
"""
|
||||
if request.method != 'POST':
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Метод не поддерживается'
|
||||
}, status=405)
|
||||
|
||||
try:
|
||||
import json
|
||||
from ..models import ProductTag
|
||||
|
||||
data = json.loads(request.body)
|
||||
name = data.get('name', '').strip()
|
||||
|
||||
# Валидация
|
||||
if not name:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Название тега не может быть пустым'
|
||||
}, status=400)
|
||||
|
||||
if len(name) > 100:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Название тега слишком длинное (максимум 100 символов)'
|
||||
}, status=400)
|
||||
|
||||
# Проверка уникальности (регистронезависимо)
|
||||
if ProductTag.objects.filter(name__iexact=name).exists():
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': f'Тег "{name}" уже существует'
|
||||
}, status=400)
|
||||
|
||||
# Создание тега (slug будет сгенерирован автоматически в модели)
|
||||
tag = ProductTag.objects.create(
|
||||
name=name,
|
||||
is_active=True
|
||||
)
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'tag': {
|
||||
'id': tag.id,
|
||||
'name': tag.name,
|
||||
'slug': tag.slug,
|
||||
'is_active': tag.is_active,
|
||||
'products_count': 0,
|
||||
'kits_count': 0
|
||||
}
|
||||
})
|
||||
|
||||
except json.JSONDecodeError:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Некорректный JSON'
|
||||
}, status=400)
|
||||
except Exception as e:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': f'Ошибка при создании тега: {str(e)}'
|
||||
}, status=500)
|
||||
|
||||
Reference in New Issue
Block a user