bober-bbq-bot/bober_bbq/templates/admin/product_form.html
byrsapty 71210b6bdb Add independent on/off toggles for the "≈" estimate badge
Previously the badge always showed next to the price for any per_100g
product and never next to the weight at all. Adds two per-product
checkboxes (show_estimate_badge_price, default on; show_estimate_badge_weight,
default off) so the admin can turn either one on or off independently —
covers a shashlik-style product where the weight itself is also just an
estimate, not only the price.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 03:28:05 +03:00

145 lines
7.4 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "admin/base.html" %}
{% block content %}
<h1>{{ 'Редагувати товар' if product else 'Новий товар' }}</h1>
<div class="card" style="max-width:520px;margin:0 auto;">
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label>Категорія</label>
<select name="category_id">
{% for c in categories %}
<option value="{{ c.id }}" {{ 'selected' if product and product.category_id == c.id }}>{{ c.name }}</option>
{% endfor %}
</select>
<label>Назва</label>
<input type="text" name="name" value="{{ product.name if product else '' }}" required>
<label>Опис</label>
<textarea name="description" rows="3">{{ product.description if product else '' }}</textarea>
<label>Назва EN <span class="muted">(необов'язково — для перемикача мов у Mini App)</span></label>
<input type="text" name="name_en" value="{{ product.name_en if product and product.name_en else '' }}">
<label>Опис EN <span class="muted">(необов'язково)</span></label>
<textarea name="description_en" rows="3">{{ product.description_en if product and product.description_en else '' }}</textarea>
<div class="row">
<div>
<label>Вага / обʼєм (напр. 300 г)</label>
<input type="text" name="weight" value="{{ product.weight if product else '' }}">
</div>
<div>
<label>Вага/обʼєм EN <span class="muted">(напр. 300 g)</span></label>
<input type="text" name="weight_en" value="{{ product.weight_en if product and product.weight_en else '' }}">
</div>
</div>
<label>Тип ціни</label>
<select name="pricing_mode" id="pricing-mode" onchange="syncPricingMode()">
<option value="fixed" {{ 'selected' if not product or product.pricing_mode == 'fixed' }}>Фіксована ціна за порцію</option>
<option value="per_100g" {{ 'selected' if product and product.pricing_mode == 'per_100g' }}>Ціна за 100 г (страва "на вагу")</option>
</select>
<label id="price-label">Ціна, грн</label>
<input type="number" name="price" id="price-input" value="{{ product.price if product else '' }}" required min="0" oninput="recalcEstimate()">
<div id="reference-weight-row" class="row">
<div>
<label>Орієнтовна вага порції, г</label>
<input type="number" name="reference_weight_g" id="reference-weight-input"
value="{{ product.reference_weight_g if product and product.reference_weight_g else '' }}"
min="1" oninput="recalcEstimate()">
</div>
<div>
<label>Орієнтовна ціна, грн</label>
<input type="number" name="estimated_price_override" id="estimate-input" min="0"
value="{{ product.estimated_price_override if product and product.estimated_price_override else '' }}"
{% if product and product.estimated_price_override %}data-touched="1"{% endif %}
oninput="this.dataset.touched='1';" placeholder="напр. 450">
</div>
</div>
<p class="muted field-hint" id="estimate-hint" style="margin-top:-10px;"></p>
<div id="estimate-badges-row" class="row">
<label style="display:flex;align-items:center;gap:8px;">
<input type="checkbox" name="show_estimate_badge_price" class="admin-checkbox"
{{ 'checked' if not product or product.show_estimate_badge_price }}>
Показувати «≈» біля ціни
</label>
<label style="display:flex;align-items:center;gap:8px;">
<input type="checkbox" name="show_estimate_badge_weight" class="admin-checkbox"
{{ 'checked' if product and product.show_estimate_badge_weight }}>
Показувати «≈» біля ваги
</label>
</div>
<label>Фото</label>
{% if product and product.image_url %}
<img class="thumb" src="{{ product.image_url }}" style="width:80px;height:80px;margin-bottom:8px;">
{% endif %}
<input type="file" name="image" accept="image/*">
<label>Алергени</label>
<p class="muted field-hint" style="margin-top:0;">Позначте, що є у складі — покажеться клієнту іконками прямо на картці страви в меню.</p>
<div class="allergen-picker">
{% for code, (label, emoji, label_en) in allergens.items() %}
<label class="allergen-check">
<input type="checkbox" name="allergens" value="{{ code }}" class="admin-checkbox" {{ 'checked' if product and code in product.allergen_codes }}>
<span>{{ emoji }} {{ label }}</span>
</label>
{% endfor %}
</div>
<label>Порядок сортування</label>
<input type="number" name="sort_order" value="{{ product.sort_order if product else 0 }}">
<label style="display:flex;align-items:center;gap:8px;">
<input type="checkbox" name="is_active" class="admin-checkbox" {{ 'checked' if not product or product.is_active }}>
Активний (показувати в меню)
</label>
<div style="margin-top:16px;display:flex;gap:10px;">
<button class="btn" type="submit">Зберегти</button>
<a class="btn secondary" href="{{ url_for('admin.products_list') }}">Скасувати</a>
</div>
</form>
</div>
{% if inventory_enabled and product %}
<a class="btn secondary small" style="margin-top:14px;" href="{{ url_for('admin.inventory_recipe_edit', product_id=product.id) }}">🧾 Рецептура (склад страви)</a>
{% endif %}
<script>
function syncPricingMode() {
var isPer100g = document.getElementById('pricing-mode').value === 'per_100g';
document.getElementById('reference-weight-row').style.display = isPer100g ? 'flex' : 'none';
document.getElementById('estimate-hint').style.display = isPer100g ? 'block' : 'none';
document.getElementById('estimate-badges-row').style.display = isPer100g ? 'flex' : 'none';
document.getElementById('price-label').textContent = isPer100g ? 'Ціна за 100 г, грн' : 'Ціна, грн';
recalcEstimate();
}
function recalcEstimate() {
var estimateInput = document.getElementById('estimate-input');
var hint = document.getElementById('estimate-hint');
if (document.getElementById('pricing-mode').value !== 'per_100g') {
hint.textContent = '';
return;
}
var price100 = parseFloat(document.getElementById('price-input').value || '0');
var refWeight = parseFloat(document.getElementById('reference-weight-input').value || '0');
var computed = refWeight ? Math.round(price100 * refWeight / 100) : null;
// Auto-fill the estimate from price×weight only until the admin types
// their own value directly — same "touched" pattern as the order-line
// price override, so a deliberate manual number never gets clobbered.
if (!estimateInput.dataset.touched) {
estimateInput.value = computed !== null ? computed : '';
}
hint.textContent = computed !== null
? 'Розрахунково: ≈ ' + computed + ' грн — можна вписати своє значення вручну.'
: 'Вкажіть вагу для автоматичного розрахунку, або впишіть ціну вручну.';
}
syncPricingMode();
</script>
{% endblock %}