feat(products): улучшить интерфейс массовой синхронизации с Recommerce
- Добавить секцию маркетинговых флагов в модалку синхронизации - Добавить кнопки "Выбрать все" для групп полей - Улучшить UX отображения списка товаров Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<!-- Модальное окно для синхронизации с Recommerce -->
|
||||
<div class="modal fade" id="recommerceSyncModal" tabindex="-1" aria-labelledby="recommerceSyncModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="recommerceSyncModalLabel">
|
||||
@@ -9,52 +9,99 @@
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-info mb-3">
|
||||
<div class="alert alert-info mb-4">
|
||||
<i class="bi bi-info-circle"></i> <strong>Выбрано товаров:</strong> <span id="recommerceSyncCount">0</span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">Что обновлять?</label>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="price" id="syncPrice" checked>
|
||||
<label class="form-check-label" for="syncPrice">
|
||||
Цены
|
||||
</label>
|
||||
<!-- Основные данные -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header py-2 d-flex justify-content-between align-items-center">
|
||||
<strong><i class="bi bi-box-seam"></i> Основные данные</strong>
|
||||
<button type="button" class="btn btn-sm btn-link p-0" id="toggleBasicFields">Выбрать все</button>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="count" id="syncStock" checked>
|
||||
<label class="form-check-label" for="syncStock">
|
||||
Остатки
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="content" id="syncContent">
|
||||
<label class="form-check-label" for="syncContent">
|
||||
Название и описание
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="images" id="syncImages">
|
||||
<label class="form-check-label" for="syncImages">
|
||||
Изображения
|
||||
</label>
|
||||
<div class="card-body">
|
||||
<div class="row g-2">
|
||||
<div class="col-6">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="price" id="syncPrice" checked>
|
||||
<label class="form-check-label" for="syncPrice">
|
||||
<i class="bi bi-tag"></i> Цены
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="count" id="syncStock">
|
||||
<label class="form-check-label" for="syncStock">
|
||||
<i class="bi bi-stack"></i> Остатки
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="content" id="syncContent">
|
||||
<label class="form-check-label" for="syncContent">
|
||||
<i class="bi bi-card-text"></i> Название и описание
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="images" id="syncImages">
|
||||
<label class="form-check-label" for="syncImages">
|
||||
<i class="bi bi-images"></i> Изображения
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<!-- Маркетинговые флаги -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header py-2 d-flex justify-content-between align-items-center">
|
||||
<strong><i class="bi bi-megaphone"></i> Маркетинговые флаги</strong>
|
||||
<button type="button" class="btn btn-sm btn-link p-0" id="toggleMarketingFields">Выбрать все</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-2">
|
||||
<div class="col-6 col-md-4">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="is_new" id="syncIsNew">
|
||||
<label class="form-check-label" for="syncIsNew">
|
||||
<span class="badge bg-warning text-dark me-1"><i class="bi bi-stars"></i></span> Новинки
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-4">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="is_popular" id="syncIsPopular">
|
||||
<label class="form-check-label" for="syncIsPopular">
|
||||
<span class="badge bg-danger me-1"><i class="bi bi-fire"></i></span> Популярные
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-4">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="is_special" id="syncIsSpecial">
|
||||
<label class="form-check-label" for="syncIsSpecial">
|
||||
<span class="badge bg-success me-1"><i class="bi bi-percent"></i></span> Спецпредложения
|
||||
</label>
|
||||
<div class="form-text text-muted small">Также авто при скидке</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="syncCreateNew">
|
||||
<label class="form-check-label" for="syncCreateNew">
|
||||
Создавать товары, если не найдены
|
||||
</label>
|
||||
<div class="form-text text-muted">
|
||||
Если товар отсутствует в Recommerce, он будет создан (требуется полное заполнение).
|
||||
<!-- Дополнительно -->
|
||||
<div class="card">
|
||||
<div class="card-body py-2">
|
||||
<div class="form-check form-switch mb-0">
|
||||
<input class="form-check-input" type="checkbox" id="syncCreateNew">
|
||||
<label class="form-check-label" for="syncCreateNew">
|
||||
<strong>Создавать товары, если не найдены в Recommerce</strong>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,9 +109,9 @@
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||
<button type="button" class="btn btn-primary" id="startRecommerceSyncBtn">
|
||||
<i class="bi bi-play-fill"></i> Запустить
|
||||
<i class="bi bi-play-fill"></i> Запустить синхронизацию
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -37,30 +37,30 @@
|
||||
<hr class="my-3">
|
||||
|
||||
<form method="get" id="filterForm">
|
||||
<div class="row g-3">
|
||||
<div class="row g-2">
|
||||
<!-- Поиск -->
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="search" class="form-label"><i class="bi bi-search"></i> Поиск</label>
|
||||
<input type="text" class="form-control" id="search" name="search"
|
||||
placeholder="Название, артикул, описание..."
|
||||
<label for="search" class="form-label small mb-1">Поиск</label>
|
||||
<input type="text" class="form-control form-control-sm" id="search" name="search"
|
||||
placeholder="Название, артикул..."
|
||||
value="{{ filters.current.search|default:'' }}">
|
||||
</div>
|
||||
|
||||
<!-- Тип -->
|
||||
<div class="col-12 col-md-2">
|
||||
<label for="type" class="form-label"><i class="bi bi-box"></i> Тип</label>
|
||||
<select class="form-select" id="type" name="type">
|
||||
<div class="col-6 col-md-1">
|
||||
<label for="type" class="form-label small mb-1">Тип</label>
|
||||
<select class="form-select form-select-sm" id="type" name="type">
|
||||
<option value="all" {% if filters.current.type == 'all' %}selected{% endif %}>Все</option>
|
||||
<option value="products" {% if filters.current.type == 'products' %}selected{% endif %}>Только товары</option>
|
||||
<option value="kits" {% if filters.current.type == 'kits' %}selected{% endif %}>Только комплекты</option>
|
||||
<option value="products" {% if filters.current.type == 'products' %}selected{% endif %}>Товары</option>
|
||||
<option value="kits" {% if filters.current.type == 'kits' %}selected{% endif %}>Комплекты</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Категория -->
|
||||
<div class="col-12 col-md-3">
|
||||
<label for="category" class="form-label"><i class="bi bi-bookmark"></i> Категория</label>
|
||||
<select class="form-select" id="category" name="category">
|
||||
<option value="">Все категории</option>
|
||||
<div class="col-6 col-md-2">
|
||||
<label for="category" class="form-label small mb-1">Категория</label>
|
||||
<select class="form-select form-select-sm" id="category" name="category">
|
||||
<option value="">Все</option>
|
||||
{% for category in filters.categories %}
|
||||
<option value="{{ category.id }}" {% if filters.current.category == category.id|stringformat:"s" %}selected{% endif %}>
|
||||
{{ category.name }}
|
||||
@@ -70,10 +70,10 @@
|
||||
</div>
|
||||
|
||||
<!-- Статус -->
|
||||
<div class="col-12 col-md-3">
|
||||
<label for="status" class="form-label"><i class="bi bi-toggle-on"></i> Статус</label>
|
||||
<select class="form-select" id="status" name="status">
|
||||
<option value="">Все статусы</option>
|
||||
<div class="col-6 col-md-1">
|
||||
<label for="status" class="form-label small mb-1">Статус</label>
|
||||
<select class="form-select form-select-sm" id="status" name="status">
|
||||
<option value="">Все</option>
|
||||
{% for status_value, status_name in item_statuses %}
|
||||
<option value="{{ status_value }}" {% if filters.current.status == status_value %}selected{% endif %}>
|
||||
{{ status_name }}
|
||||
@@ -81,52 +81,101 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Со скидкой -->
|
||||
<div class="col-6 col-md-1">
|
||||
<label class="form-label small mb-1">Скидка</label>
|
||||
<select class="form-select form-select-sm" id="has_discount" name="has_discount">
|
||||
<option value="">—</option>
|
||||
<option value="1" {% if filters.current.has_discount == '1' %}selected{% endif %}>Да</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Маркетинг -->
|
||||
<div class="col-12 col-md-3">
|
||||
<label class="form-label small mb-1">Маркетинг</label>
|
||||
<div class="btn-group w-100 d-flex" role="group" style="flex-wrap: wrap;">
|
||||
<input type="checkbox" class="btn-check" id="is_new" name="is_new" value="1"
|
||||
{% if filters.current.is_new == '1' %}checked{% endif %}
|
||||
onchange="document.getElementById('filterForm').submit()">
|
||||
<label class="btn btn-outline-warning btn-sm flex-fill" for="is_new" title="Новинки"
|
||||
style="white-space: normal; text-align: center; font-size: 0.75rem;">
|
||||
<i class="bi bi-stars d-block"></i> Новинки
|
||||
</label>
|
||||
|
||||
<input type="checkbox" class="btn-check" id="is_popular" name="is_popular" value="1"
|
||||
{% if filters.current.is_popular == '1' %}checked{% endif %}
|
||||
onchange="document.getElementById('filterForm').submit()">
|
||||
<label class="btn btn-outline-danger btn-sm flex-fill" for="is_popular" title="Популярные"
|
||||
style="white-space: normal; text-align: center; font-size: 0.75rem;">
|
||||
<i class="bi bi-fire d-block"></i> Популярные
|
||||
</label>
|
||||
|
||||
<input type="checkbox" class="btn-check" id="is_special" name="is_special" value="1"
|
||||
{% if filters.current.is_special == '1' %}checked{% endif %}
|
||||
onchange="document.getElementById('filterForm').submit()">
|
||||
<label class="btn btn-outline-success btn-sm flex-fill" for="is_special" title="Акции"
|
||||
style="white-space: normal; text-align: center; font-size: 0.75rem;">
|
||||
<i class="bi bi-percent d-block"></i> Акции
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mt-2">
|
||||
<!-- В наличии (только для товаров) -->
|
||||
<div class="col-12 col-md-3">
|
||||
<label for="in_stock" class="form-label"><i class="bi bi-check-circle"></i> В наличии</label>
|
||||
<select class="form-select" id="in_stock" name="in_stock">
|
||||
<option value="">Все</option>
|
||||
<div class="row g-2">
|
||||
<!-- Цена -->
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label small mb-1">Цена</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="number" class="form-control" id="price_from" name="price_from" placeholder="От"
|
||||
value="{{ filters.current.price_from|default:'' }}" min="0" step="0.01">
|
||||
<span class="input-group-text">—</span>
|
||||
<input type="number" class="form-control" id="price_to" name="price_to" placeholder="До"
|
||||
value="{{ filters.current.price_to|default:'' }}" min="0" step="0.01">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- В наличии -->
|
||||
<div class="col-4 col-md-1">
|
||||
<label for="in_stock" class="form-label small mb-1">Наличие</label>
|
||||
<select class="form-select form-select-sm" id="in_stock" name="in_stock">
|
||||
<option value="">—</option>
|
||||
<option value="1" {% if filters.current.in_stock == '1' %}selected{% endif %}>Да</option>
|
||||
<option value="0" {% if filters.current.in_stock == '0' %}selected{% endif %}>Нет</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Теги -->
|
||||
<div class="col-12 col-md-3">
|
||||
<label for="tags" class="form-label"><i class="bi bi-tags"></i> Теги</label>
|
||||
<select class="form-select" id="tags" name="tags" multiple size="1">
|
||||
<div class="col-4 col-md-2">
|
||||
<label for="tags" class="form-label small mb-1">Теги</label>
|
||||
<select class="form-select form-select-sm" id="tags" name="tags" multiple size="1">
|
||||
{% for tag in filters.tags %}
|
||||
<option value="{{ tag.id }}" {% if tag.id in filters.current.tags %}selected{% endif %}>
|
||||
{{ tag.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% if filters.current.tags %}
|
||||
<small class="text-muted">Выбрано: {{ filters.current.tags|length }}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Показывать по -->
|
||||
<div class="col-12 col-md-2">
|
||||
<label for="per_page" class="form-label"><i class="bi bi-list-ol"></i> Показывать по:</label>
|
||||
<select class="form-select" id="per_page" name="per_page" onchange="this.form.submit()">
|
||||
<!-- per_page -->
|
||||
<div class="col-4 col-md-1">
|
||||
<label for="per_page" class="form-label small mb-1">На стр.</label>
|
||||
<select class="form-select form-select-sm" id="per_page" name="per_page" onchange="this.form.submit()">
|
||||
<option value="20" {% if filters.current.per_page == '20' %}selected{% endif %}>20</option>
|
||||
<option value="50" {% if filters.current.per_page == '50' %}selected{% endif %}>50</option>
|
||||
<option value="100" {% if filters.current.per_page == '100' %}selected{% endif %}>100</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-4">
|
||||
<label class="form-label d-none d-md-block"> </label>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-check-circle"></i> Применить
|
||||
<!-- Кнопки -->
|
||||
<div class="col-12 col-md-2">
|
||||
<label class="form-label small mb-1 d-none d-md-block"> </label>
|
||||
<div class="d-flex gap-1">
|
||||
<button type="submit" class="btn btn-primary btn-sm flex-fill">
|
||||
OK
|
||||
</button>
|
||||
<a href="{% url 'products:products-list' %}" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-x-circle"></i> Сбросить
|
||||
<a href="{% url 'products:products-list' %}" class="btn btn-outline-secondary btn-sm">
|
||||
X
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -175,15 +224,13 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-outline-secondary" id="batch-actions-btn" disabled>
|
||||
<div class="dropdown d-flex align-items-center gap-2" id="batch-actions-wrapper" style="position: relative;">
|
||||
<button type="button" class="btn btn-light dropdown-toggle border" id="batch-actions-btn" disabled
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-gear-fill"></i> Действия над выбранными (<span id="selection-count">0</span>)
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split"
|
||||
id="batch-actions-dropdown" data-bs-toggle="dropdown" aria-expanded="false" disabled>
|
||||
<span class="visually-hidden">Открыть меню</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="batch-actions-dropdown">
|
||||
<span class="badge bg-secondary" id="batch-actions-hint" style="position: absolute; top: 100%; left: 0; margin-top: 4px; z-index: 10; white-space: nowrap; display: none;">Сначала выберите товары</span>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="batch-actions-btn">
|
||||
<li>
|
||||
<a class="dropdown-item" href="#" id="bulk-category-action">
|
||||
<i class="bi bi-bookmark-fill"></i> Изменить категории
|
||||
@@ -495,11 +542,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#batch-actions-wrapper[data-hint="true"]:hover #batch-actions-hint {
|
||||
display: inline-block !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
{% load static %}
|
||||
<script src="{% static 'products/js/batch-selection.js' %}?v=1.2"></script>
|
||||
<script src="{% static 'products/js/batch-selection.js' %}?v=1.5"></script>
|
||||
<script src="{% static 'products/js/bulk-category-modal.js' %}?v=1.6"></script>
|
||||
<script src="{% static 'products/js/recommerce-sync.js' %}?v=1.1"></script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user