refactor(products): extract Order Item Select2 to reusable module

Moved order item selection logic from order_form.html to select2-product-search.js
for better code reusability and maintainability.

Changes:
- Extended select2-product-search.js with initOrderItemSelect2() function (~87 lines)
  - Wraps initProductSelect2 for order item context
  - Handles product/kit selection and form field updates
  - Manages custom price indicators
  - Supports data-ajax-url attribute for URL configuration

- Updated order_form.html:
  - Added data-ajax-url to order item select elements
  - Removed inline initOrderItemSelect2 function (~73 lines)
  - Updated dependency check to use initOrderItemSelect2

Benefits:
- No code duplication - reuses existing initProductSelect2
- Cleaner template (79 lines removed)
- Consistent with existing patterns
- Easy to maintain in one place

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-23 15:30:09 +03:00
parent fb4f14f475
commit 98501c1c26
2 changed files with 97 additions and 79 deletions

View File

@@ -244,7 +244,9 @@
<div class="col-md-5">
<div class="mb-2">
<label class="form-label">Товар или комплект</label>
<select class="form-select select2-order-item" data-form-index="{{ forloop.counter0 }}">
<select class="form-select select2-order-item"
data-form-index="{{ forloop.counter0 }}"
data-ajax-url="{% url 'products:api-search-products-variants' %}">
<option value=""></option>
{% if item_form.instance.product %}
<option value="product_{{ item_form.instance.product.id }}" selected data-type="product" data-price="{{ item_form.instance.product.actual_price }}">
@@ -329,7 +331,10 @@
<div class="col-md-5">
<div class="mb-2">
<label class="form-label">Товар или комплект</label>
<select class="form-select select2-order-item" data-form-index="__prefix__" id="id_items-__prefix__-select">
<select class="form-select select2-order-item"
data-form-index="__prefix__"
data-ajax-url="{% url 'products:api-search-products-variants' %}"
id="id_items-__prefix__-select">
<option value=""></option>
</select>
</div>
@@ -1167,80 +1172,6 @@ document.addEventListener('DOMContentLoaded', function() {
});
</script>
<script>
// Глобально определяем initOrderItemSelect2
window.initOrderItemSelect2 = function(element) {
const $element = $(element);
const formIndex = element.dataset.formIndex;
// Инициализируем Select2 с AJAX поиском
window.initProductSelect2(
element,
'all', // Искать и товары, и комплекты
'{% url "products:api-search-products-variants" %}'
);
// Обработка выбора элемента
$element.on('select2:select', function(e) {
// Проверяем наличие params (может не быть при программном вызове)
if (!e.params || !e.params.data) {
return;
}
const data = e.params.data;
const idParts = data.id.split('_');
const type = idParts[0]; // 'product' или 'kit'
const id = idParts[1];
// Найти скрытые поля product и product_kit
const form = element.closest('.order-item-form');
const productField = form.querySelector('[name$="-product"]');
const kitField = form.querySelector('[name$="-product_kit"]');
const priceField = form.querySelector('[name$="-price"]');
const isCustomPriceField = form.querySelector('[name$="-is_custom_price"]');
const originalPrice = data.actual_price || data.price || '';
// Установить значение в правильное поле
if (type === 'product') {
productField.value = id;
kitField.value = '';
priceField.value = originalPrice;
} else if (type === 'kit') {
kitField.value = id;
productField.value = '';
priceField.value = originalPrice;
}
// Сохраняем оригинальную цену в data-атрибуте
priceField.dataset.originalPrice = originalPrice;
// Сбрасываем флаг кастомной цены
isCustomPriceField.value = 'false';
// Скрываем индикатор
const badge = form.querySelector('.custom-price-badge');
const priceInfo = form.querySelector('.original-price-info');
if (badge) badge.style.display = 'none';
if (priceInfo) priceInfo.style.display = 'none';
});
// Очистка при удалении выбора
$element.on('select2:clear', function() {
console.log('[Select2 Clear] Очистка товара в форме');
const form = element.closest('.order-item-form');
const formIndex = Array.from(document.querySelectorAll('.order-item-form')).indexOf(form);
console.log(`[Select2 Clear] Индекс формы: ${formIndex}`);
form.querySelector('[name$="-product"]').value = '';
form.querySelector('[name$="-product_kit"]').value = '';
form.querySelector('[name$="-price"]').value = '';
form.querySelector('[name$="-quantity"]').value = '';
console.log('[Select2 Clear] Все поля очищены, форма осталась в DOM');
});
};
</script>
<!-- Customer Select2 Widget -->
<script src="{% static 'orders/js/customer_select2.js' %}" defer></script>
@@ -1923,12 +1854,12 @@ document.addEventListener('DOMContentLoaded', function() {
return;
}
if (typeof window.initProductSelect2 !== 'function') {
console.log('[Order Items] Ожидание загрузки initProductSelect2 из select2-product-search.js...');
if (typeof window.initOrderItemSelect2 !== 'function') {
console.log('[Order Items] Ожидание загрузки initOrderItemSelect2 из select2-product-search.js...');
setTimeout(initExistingOrderItems, 100);
return;
}
// Все зависимости готовы
console.log('[Order Items] Все зависимости готовы, запуск инициализации...');
const items = document.querySelectorAll('.select2-order-item');