Исправление ошибки POS: разрешено добавление в корзину для PlatformAdmin (использование session_id вместо пользователя). Включены изменения по AI названиям букетов.
This commit is contained in:
@@ -86,6 +86,62 @@
|
||||
|
||||
<!-- ПРАВАЯ КОЛОНКА: Настройки -->
|
||||
<div class="col-lg-4">
|
||||
<!-- Генератор названия -->
|
||||
<div class="card border-0 shadow-sm mb-3">
|
||||
<div class="card-header bg-transparent border-bottom-0 p-3 d-flex justify-content-between align-items-center cursor-pointer"
|
||||
data-bs-toggle="collapse" data-bs-target="#nameGeneratorCollapse" aria-expanded="false">
|
||||
<h6 class="mb-0 text-muted"><i class="bi bi-lightbulb me-1"></i>Генератор названия</h6>
|
||||
<i class="bi bi-chevron-down text-muted"></i>
|
||||
</div>
|
||||
<div id="nameGeneratorCollapse" class="collapse">
|
||||
<div class="card-body p-3">
|
||||
<p class="small text-muted mb-3">
|
||||
Сгенерируйте привлекательное название для вашего букета автоматически
|
||||
<br><span class="badge bg-secondary mt-1">В базе: <span id="bouquetNamesCount">{{ bouquet_names_count }}</span> названий</span>
|
||||
</p>
|
||||
<div class="d-flex gap-2 mb-4">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm">
|
||||
<i class="bi bi-magic"></i> Пополнить базу названиями
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm">
|
||||
<i class="bi bi-refresh"></i> Дать три варианта
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Предложения названий -->
|
||||
<div class="name-suggestions">
|
||||
<!-- Строка 1 -->
|
||||
<div class="d-flex justify-content-between align-items-center py-2 border-bottom">
|
||||
<span class="text-muted small">Романтический букет роз</span>
|
||||
<div class="d-flex gap-1">
|
||||
<button type="button" class="btn btn-success btn-xs">Взять</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-xs">Потом</button>
|
||||
<button type="button" class="btn btn-outline-danger btn-xs">Убрать</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Строка 2 -->
|
||||
<div class="d-flex justify-content-between align-items-center py-2 border-bottom">
|
||||
<span class="text-muted small">Солнечный букет подсолнухов</span>
|
||||
<div class="d-flex gap-1">
|
||||
<button type="button" class="btn btn-success btn-xs">Взять</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-xs">Потом</button>
|
||||
<button type="button" class="btn btn-outline-danger btn-xs">Убрать</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Строка 3 -->
|
||||
<div class="d-flex justify-content-between align-items-center py-2">
|
||||
<span class="text-muted small">Элегантный букет лотосов</span>
|
||||
<div class="d-flex gap-1">
|
||||
<button type="button" class="btn btn-success btn-xs">Взять</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-xs">Потом</button>
|
||||
<button type="button" class="btn btn-outline-danger btn-xs">Убрать</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Расчет цены -->
|
||||
<div class="card border-0 shadow-sm mb-3">
|
||||
<div class="card-body p-3">
|
||||
@@ -410,6 +466,50 @@
|
||||
color: #0d6efd;
|
||||
}
|
||||
|
||||
/* Стили для генератора названий */
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.card-header[data-bs-toggle="collapse"]:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.card-header[data-bs-toggle="collapse"] .bi-chevron-down {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.collapse.show .card-header[data-bs-toggle="collapse"] .bi-chevron-down {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* Кнопки очень маленького размера */
|
||||
.btn-xs {
|
||||
padding: 0.125rem 0.25rem;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1;
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.btn-xs:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Стили для списка предложений */
|
||||
.name-suggestions {
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 0.375rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.name-suggestions .text-muted {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.name-suggestions .border-bottom {
|
||||
border-color: #e9ecef !important;
|
||||
}
|
||||
|
||||
/* Стили для полей корректировки цены */
|
||||
#id_increase_percent:disabled,
|
||||
#id_increase_amount:disabled,
|
||||
@@ -1311,6 +1411,173 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
}
|
||||
|
||||
// ========== ГЕНЕРАТОР НАЗВАНИЙ ==========
|
||||
// Обработчик для кнопок "Взять"
|
||||
document.querySelectorAll('.name-suggestions .btn-success').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const suggestionText = this.closest('.d-flex').querySelector('.text-muted').textContent;
|
||||
const nameInput = document.getElementById('id_name');
|
||||
if (nameInput) {
|
||||
nameInput.value = suggestionText;
|
||||
// Улучшаем визуальный эффект
|
||||
nameInput.style.borderColor = '#198754';
|
||||
nameInput.style.boxShadow = '0 0 0 0.25rem rgba(25, 135, 84, 0.15)';
|
||||
setTimeout(() => {
|
||||
nameInput.style.borderColor = '';
|
||||
nameInput.style.boxShadow = '';
|
||||
}, 2000);
|
||||
}
|
||||
// Закрываем collapse
|
||||
const collapse = document.getElementById('nameGeneratorCollapse');
|
||||
const bsCollapse = new bootstrap.Collapse(collapse, { toggle: false });
|
||||
bsCollapse.hide();
|
||||
});
|
||||
});
|
||||
|
||||
// Обработчик для кнопок "Убрать"
|
||||
document.querySelectorAll('.name-suggestions .btn-outline-danger').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
this.closest('.d-flex').remove();
|
||||
});
|
||||
});
|
||||
|
||||
// Обработчик для кнопок "Потом"
|
||||
document.querySelectorAll('.name-suggestions .btn-outline-secondary').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const row = this.closest('.d-flex');
|
||||
row.style.opacity = '0.5';
|
||||
row.style.textDecoration = 'line-through';
|
||||
setTimeout(() => {
|
||||
row.style.opacity = '1';
|
||||
row.style.textDecoration = 'none';
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
// ========== ГЕНЕРАТОР НАЗВАНИЙ - НОВЫЕ ОБРАБОТЧИКИ ==========
|
||||
|
||||
// Обработчик для кнопки "Сгенерировать" (LLM)
|
||||
const generateBtn = document.querySelector('#nameGeneratorCollapse .btn-outline-primary');
|
||||
if (generateBtn) {
|
||||
generateBtn.addEventListener('click', async function() {
|
||||
const originalHTML = generateBtn.innerHTML;
|
||||
generateBtn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> Генерация...';
|
||||
generateBtn.disabled = true;
|
||||
|
||||
try {
|
||||
const response = await fetch("{% url 'products:api-generate-bouquet-names' %}", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: new URLSearchParams({'count': 100})
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
// Обновляем счётчик
|
||||
updateBouquetNamesCount();
|
||||
// Загружаем случайные 3
|
||||
await loadRandomNames();
|
||||
alert(data.message);
|
||||
} else {
|
||||
alert('Ошибка: ' + (data.error || 'Неизвестная ошибка'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('Ошибка при генерации названий. Проверьте, что настроена AI-интеграция.');
|
||||
} finally {
|
||||
generateBtn.innerHTML = originalHTML;
|
||||
generateBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Обработчик для кнопки "Случайное"
|
||||
const randomBtn = document.querySelector('#nameGeneratorCollapse .btn-outline-secondary');
|
||||
if (randomBtn) {
|
||||
randomBtn.addEventListener('click', loadRandomNames);
|
||||
}
|
||||
|
||||
async function loadRandomNames() {
|
||||
const originalHTML = randomBtn.innerHTML;
|
||||
randomBtn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> Загрузка...';
|
||||
randomBtn.disabled = true;
|
||||
|
||||
try {
|
||||
const response = await fetch("{% url 'products:api-random-bouquet-names' %}?count=3");
|
||||
const data = await response.json();
|
||||
|
||||
if (data.names && data.names.length > 0) {
|
||||
updateNameSuggestions(data.names);
|
||||
} else {
|
||||
alert('В базе пока нет названий. Сначала запустите загрузку из JSON или нажмите "Сгенерировать"');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('Ошибка при загрузке названий');
|
||||
} finally {
|
||||
randomBtn.innerHTML = originalHTML;
|
||||
randomBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateBouquetNamesCount() {
|
||||
try {
|
||||
const response = await fetch("{% url 'products:api-random-bouquet-names' %}?count=999");
|
||||
const data = await response.json();
|
||||
const countEl = document.getElementById('bouquetNamesCount');
|
||||
if (countEl) countEl.textContent = data.names.length;
|
||||
} catch (error) {
|
||||
console.error('Error updating count:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function updateNameSuggestions(names) {
|
||||
const container = document.querySelector('.name-suggestions');
|
||||
container.innerHTML = '';
|
||||
|
||||
names.forEach((name, index) => {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'd-flex justify-content-between align-items-center py-2' +
|
||||
(index < names.length - 1 ? ' border-bottom' : '');
|
||||
row.innerHTML = `
|
||||
<span class="text-muted small">${name}</span>
|
||||
<div class="d-flex gap-1">
|
||||
<button type="button" class="btn btn-success btn-xs btn-apply-name">Взять</button>
|
||||
<button type="button" class="btn btn-outline-danger btn-xs btn-remove-name">Убрать</button>
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(row);
|
||||
});
|
||||
attachNameButtonHandlers();
|
||||
}
|
||||
|
||||
function attachNameButtonHandlers() {
|
||||
document.querySelectorAll('.btn-apply-name').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const name = this.closest('.d-flex').querySelector('.text-muted').textContent;
|
||||
const nameInput = document.getElementById('id_name');
|
||||
if (nameInput) {
|
||||
nameInput.value = name;
|
||||
nameInput.style.borderColor = '#198754';
|
||||
nameInput.style.boxShadow = '0 0 0 0.25rem rgba(25, 135, 84, 0.15)';
|
||||
setTimeout(() => {
|
||||
nameInput.style.borderColor = '';
|
||||
nameInput.style.boxShadow = '';
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.btn-remove-name').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
this.closest('.d-flex').remove();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ========== ВАЛИДАЦИЯ ПЕРЕД ОТПРАВКОЙ ==========
|
||||
const kitForm = document.querySelector('form[method="post"]');
|
||||
if (kitForm) {
|
||||
|
||||
Reference in New Issue
Block a user