fix(products): improve bulk category modal and batch selection handling

- Add null check for selectAllCheckbox to avoid errors in batch-selection.js
- Replace clear existing categories toggle with radio buttons for add, replace, and clear modes
- Disable category search input and fade category list when 'clear' mode is selected
- Update mode hint text dynamically based on selected mode with explanatory messages
- Enable apply button when 'clear' mode is selected regardless of category selection
- Remove clear all categories button from modal footer
- Add event listeners for mode radio buttons to update UI and error states on change
- Initialize mode UI and apply button state on modal setup
- Bump static JS files versions for batch-selection and bulk-category-modal to 1.2 and 1.4 respectively
This commit is contained in:
2026-01-10 00:47:42 +03:00
parent b63162b1cb
commit 0d6d62d1ad
3 changed files with 101 additions and 21 deletions

View File

@@ -101,6 +101,11 @@
* Update the "Select All" checkbox state based on individual checkboxes * Update the "Select All" checkbox state based on individual checkboxes
*/ */
function updateSelectAllCheckbox() { function updateSelectAllCheckbox() {
// Проверяем наличие чекбокса перед работой с ним
if (!selectAllCheckbox) {
return;
}
const itemCheckboxes = document.querySelectorAll('.item-checkbox'); const itemCheckboxes = document.querySelectorAll('.item-checkbox');
const totalCheckboxes = itemCheckboxes.length; const totalCheckboxes = itemCheckboxes.length;
const checkedCheckboxes = document.querySelectorAll('.item-checkbox:checked').length; const checkedCheckboxes = document.querySelectorAll('.item-checkbox:checked').length;

View File

@@ -15,13 +15,13 @@
const modal = document.getElementById('bulkCategoryModal'); const modal = document.getElementById('bulkCategoryModal');
const bulkCategoryAction = document.getElementById('bulk-category-action'); const bulkCategoryAction = document.getElementById('bulk-category-action');
const applyBtn = document.getElementById('applyBulkCategoriesBtn'); const applyBtn = document.getElementById('applyBulkCategoriesBtn');
const clearAllBtn = document.getElementById('clearAllCategoriesBtn');
const categoryListContainer = document.getElementById('categoryListContainer'); const categoryListContainer = document.getElementById('categoryListContainer');
const categorySearchInput = document.getElementById('categorySearchInput'); const categorySearchInput = document.getElementById('categorySearchInput');
const clearExistingToggle = document.getElementById('clearExistingCategoriesToggle');
const errorAlert = document.getElementById('bulkCategoryError'); const errorAlert = document.getElementById('bulkCategoryError');
const selectedItemsCountSpan = document.getElementById('selectedItemsCount'); const selectedItemsCountSpan = document.getElementById('selectedItemsCount');
const selectedItemsBreakdownSpan = document.getElementById('selectedItemsBreakdown'); const selectedItemsBreakdownSpan = document.getElementById('selectedItemsBreakdown');
const modeHint = document.getElementById('bulkCategoryModeHint');
const categoryListSection = document.getElementById('bulkCategoryListSection');
/** /**
* Initialize the bulk category modal functionality * Initialize the bulk category modal functionality
@@ -38,12 +38,27 @@
// Event listeners // Event listeners
bulkCategoryAction.addEventListener('click', handleOpenModal); bulkCategoryAction.addEventListener('click', handleOpenModal);
applyBtn.addEventListener('click', handleApply); applyBtn.addEventListener('click', handleApply);
clearAllBtn.addEventListener('click', handleClearAll); if (categorySearchInput) {
categorySearchInput.addEventListener('input', handleCategorySearch); categorySearchInput.addEventListener('input', handleCategorySearch);
}
// Listen for mode changes
const modeRadios = document.querySelectorAll('input[name="bulkCategoryMode"]');
modeRadios.forEach(radio => {
radio.addEventListener('change', () => {
hideError();
updateModeUI();
updateApplyButtonState();
});
});
// Listen for modal close to reset state // Listen for modal close to reset state
modal.addEventListener('hidden.bs.modal', resetModalState); modal.addEventListener('hidden.bs.modal', resetModalState);
// Initial UI state
updateModeUI();
updateApplyButtonState();
console.log('Bulk category modal initialized'); console.log('Bulk category modal initialized');
} }
@@ -120,6 +135,43 @@
selectedItemsBreakdownSpan.textContent = breakdown; selectedItemsBreakdownSpan.textContent = breakdown;
} }
/**
* Get current mode: add | replace | clear
*/
function getCurrentMode() {
const checked = document.querySelector('input[name="bulkCategoryMode"]:checked');
return checked ? checked.value : 'add';
}
/**
* Update UI depending on selected mode
*/
function updateModeUI() {
const mode = getCurrentMode();
if (categorySearchInput) {
categorySearchInput.disabled = (mode === 'clear');
}
if (categoryListSection) {
if (mode === 'clear') {
categoryListSection.classList.add('opacity-50');
} else {
categoryListSection.classList.remove('opacity-50');
}
}
if (modeHint) {
if (mode === 'add') {
modeHint.textContent = 'Добавленные категории будут присоединены к уже существующим.';
} else if (mode === 'replace') {
modeHint.textContent = 'Существующие категории будут полностью заменены выбранными ниже.';
} else if (mode === 'clear') {
modeHint.textContent = 'Все категории будут удалены. Список ниже будет проигнорирован.';
}
}
}
/** /**
* Load categories from server * Load categories from server
*/ */
@@ -265,8 +317,15 @@
* Update apply button state * Update apply button state
*/ */
function updateApplyButtonState() { function updateApplyButtonState() {
// Кнопка Применить активна только если выбрана хотя бы одна категория const mode = getCurrentMode();
applyBtn.disabled = selectedCategoryIds.size === 0;
if (mode === 'clear') {
// В режиме очистки категории не требуются
applyBtn.disabled = false;
} else {
// Для add/replace нужна хотя бы одна выбранная категория
applyBtn.disabled = selectedCategoryIds.size === 0;
}
} }
/** /**

View File

@@ -415,22 +415,41 @@
<span id="selectedItemsBreakdown" class="ms-2 text-muted"></span> <span id="selectedItemsBreakdown" class="ms-2 text-muted"></span>
</div> </div>
<!-- Опции применения --> <!-- Режим изменения категорий -->
<div class="mb-4"> <div class="mb-4">
<div class="form-check form-switch"> <label class="form-label fw-bold">Что сделать с категориями выбранных товаров?</label>
<input class="form-check-input" type="checkbox" id="clearExistingCategoriesToggle" role="switch">
<label class="form-check-label" for="clearExistingCategoriesToggle"> <div class="form-check">
<i class="bi bi-trash"></i> Очистить существующие категории перед сохранением <input class="form-check-input" type="radio" name="bulkCategoryMode"
id="bulkCategoryModeAdd" value="add" checked>
<label class="form-check-label" for="bulkCategoryModeAdd">
Добавить выбранные категории к существующим
</label> </label>
<div class="form-text text-warning"> </div>
<i class="bi bi-exclamation-triangle"></i>
При включении все текущие категории товаров будут удалены, а затем применены только выбранные ниже <div class="form-check mt-2">
</div> <input class="form-check-input" type="radio" name="bulkCategoryMode"
id="bulkCategoryModeReplace" value="replace">
<label class="form-check-label" for="bulkCategoryModeReplace">
Заменить существующие категории выбранными
</label>
</div>
<div class="form-check mt-2">
<input class="form-check-input" type="radio" name="bulkCategoryMode"
id="bulkCategoryModeClear" value="clear">
<label class="form-check-label text-danger" for="bulkCategoryModeClear">
<strong>Удалить все категории</strong>
</label>
</div>
<div id="bulkCategoryModeHint" class="form-text text-muted mt-2">
Добавленные категории будут присоединены к уже существующим.
</div> </div>
</div> </div>
<!-- Выбор категорий --> <!-- Выбор категорий -->
<div class="mb-3"> <div class="mb-3" id="bulkCategoryListSection">
<label class="form-label fw-bold">Выберите категории:</label> <label class="form-label fw-bold">Выберите категории:</label>
<!-- Поиск по категориям --> <!-- Поиск по категориям -->
@@ -452,9 +471,6 @@
<div id="bulkCategoryError" class="alert alert-danger d-none" role="alert"></div> <div id="bulkCategoryError" class="alert alert-danger d-none" role="alert"></div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-danger me-auto" id="clearAllCategoriesBtn">
<i class="bi bi-trash3"></i> Очистить все категории
</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="bi bi-x-circle"></i> Отмена <i class="bi bi-x-circle"></i> Отмена
</button> </button>
@@ -469,6 +485,6 @@
{% block extra_js %} {% block extra_js %}
{% load static %} {% load static %}
<script src="{% static 'products/js/batch-selection.js' %}?v=1.1"></script> <script src="{% static 'products/js/batch-selection.js' %}?v=1.2"></script>
<script src="{% static 'products/js/bulk-category-modal.js' %}?v=1.3"></script> <script src="{% static 'products/js/bulk-category-modal.js' %}?v=1.4"></script>
{% endblock %} {% endblock %}