Реализовано управление активностью тегов товаров и комплектов

## Что сделано:

### 1. Фильтрация тегов по активности
- ProductForm и ProductKitForm показывают только активные теги в селектах
- ProductListView, ProductKitListView передают только активные теги в контекст фильтров

### 2. Отображение тегов на страницах товаров/комплектов
- product_detail.html отображает только активные теги
- productkit_detail.html отображает только активные теги

### 3. Логика отображения в деталях тега
- Для активного тега: показываются только активные товары/комплекты
- Для неактивного тега: показываются ВСЕ товары/комплекты (для возможности ручной очистки)

### 4. API endpoint для переключения статуса
- Новый endpoint toggle_tag_status_api в api_views.py
- POST /products/api/tags/<id>/toggle/
- Переключает is_active и возвращает новый статус

### 5. AJAX toggle switch в таблице тегов
- Заменены бейджи на toggle switch в tag_list.html
- Переключатель в 1.3x больше (масштабирование через CSS)
- Мгновенное обновление без перезагрузки страницы
- Показывает сообщение об успехе/ошибке

### 6. Связь в БД остаётся неизменной
- При деактивации тег остаётся привязан к товарам в БД
- Просто скрывается в интерфейсе
- При реактивации вновь становится видимым

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-12 11:33:12 +03:00
parent a1f5733fde
commit 77064a274f
9 changed files with 252 additions and 20 deletions

View File

@@ -154,7 +154,9 @@
<td>
{% if product.tags.all %}
{% for tag in product.tags.all %}
<span class="badge bg-secondary">{{ tag.name }}</span>
{% if tag.is_active %}
<span class="badge bg-secondary">{{ tag.name }}</span>
{% endif %}
{% endfor %}
{% else %}
-

View File

@@ -104,7 +104,9 @@
<h6>Теги:</h6>
<div class="d-flex flex-wrap gap-2">
{% for tag in kit.tags.all %}
{% if tag.is_active %}
<span class="badge bg-secondary">{{ tag.name }}</span>
{% endif %}
{% endfor %}
</div>
</div>

View File

@@ -87,11 +87,13 @@
<span class="badge bg-info">{{ tag.kits_count }}</span>
</td>
<td>
{% if tag.is_active %}
<span class="badge bg-success">Активен</span>
{% else %}
<span class="badge bg-secondary">Неактивен</span>
{% endif %}
<div class="form-check form-switch" style="transform: scale(1.3); transform-origin: left;">
<input class="form-check-input tag-status-switch"
type="checkbox"
id="tag-status-{{ tag.pk }}"
data-tag-id="{{ tag.pk }}"
{% if tag.is_active %}checked{% endif %}>
</div>
</td>
<td>
<div class="btn-group btn-group-sm" role="group">
@@ -241,6 +243,45 @@ document.addEventListener('DOMContentLoaded', function() {
}
});
});
// Обработчик переключения статуса тега
document.querySelectorAll('.tag-status-switch').forEach(toggle => {
toggle.addEventListener('click', async function(e) {
e.preventDefault(); // Предотвращаем стандартное поведение checkbox
const tagId = this.dataset.tagId;
const toggleSwitch = this;
const wasChecked = toggleSwitch.checked; // Сохраняем ТЕКУЩЕЕ состояние
try {
// Отправляем AJAX запрос
const apiUrl = `/products/api/tags/${tagId}/toggle/`;
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
}
});
const data = await response.json();
if (data.success) {
// Устанавливаем переключатель в НОВОЕ состояние согласно ответу сервера
toggleSwitch.checked = data.is_active;
// Показываем сообщение об успехе
showMessage(data.message, 'success');
} else {
// Переключатель остаётся в исходном состоянии (мы его не меняли)
showMessage(data.error || 'Ошибка при обновлении тега', 'danger');
}
} catch (error) {
// При ошибке сети - переключатель остаётся как был
showMessage('Ошибка сети: ' + error.message, 'danger');
}
});
});
</script>
{% endblock %}