9b688b2af4
Цена со скидкой и срок акции на товаре; отображение в каталоге и корзине. Улучшенный UI промокодов с редактированием. Co-authored-by: Cursor <cursoragent@cursor.com>
116 lines
6.1 KiB
Plaintext
116 lines
6.1 KiB
Plaintext
<%- include('../partials/layout-start') %>
|
||
|
||
<div class="admin-header">
|
||
<h1>Промокоды и скидки</h1>
|
||
<nav class="admin-nav">
|
||
<a href="/admin" class="admin-nav__link">Обзор</a>
|
||
<a href="/admin/orders" class="admin-nav__link">Заказы</a>
|
||
<a href="/admin/users" class="admin-nav__link">Пользователи</a>
|
||
<a href="/admin/products" class="admin-nav__link">Товары</a>
|
||
<a href="/admin/promo-codes" class="admin-nav__link admin-nav__link--active">Промокоды</a>
|
||
<a href="/admin/reservations" class="admin-nav__link">Бронирования</a>
|
||
<a href="/" class="admin-nav__link">В магазин</a>
|
||
</nav>
|
||
</div>
|
||
|
||
<% if (created) { %><p class="alert alert--success">Промокод создан</p><% } %>
|
||
<% if (updated) { %><p class="alert alert--success">Промокод обновлён</p><% } %>
|
||
|
||
<section class="card account-section--narrow" style="margin-bottom:1.5rem">
|
||
<h2>Новый промокод</h2>
|
||
<form action="/admin/promo-codes" method="post" class="form form--grid">
|
||
<label class="label">Код <input type="text" name="code" class="input" required placeholder="SUMMER20"></label>
|
||
<label class="label">Описание <input type="text" name="description" class="input" placeholder="Летняя скидка"></label>
|
||
<label class="label">Тип скидки
|
||
<select name="discount_type" class="input" id="promo-type-new">
|
||
<option value="percent">Процент (%)</option>
|
||
<option value="fixed">Фиксированная сумма (₽)</option>
|
||
</select>
|
||
</label>
|
||
<label class="label">
|
||
<span id="promo-value-label-new">Размер скидки (%)</span>
|
||
<input type="number" name="discount_value" class="input" min="1" required placeholder="10">
|
||
</label>
|
||
<label class="label">Действует (дней с сегодня) <input type="number" name="valid_days" class="input" value="30" min="1"></label>
|
||
<label class="label">Мин. сумма заказа (₽) <input type="number" name="min_order_rub" class="input" value="0" min="0" step="1"></label>
|
||
<label class="label">Лимит использований <input type="number" name="max_uses" class="input" min="1" placeholder="без лимита"></label>
|
||
<button type="submit" class="btn btn--primary">Создать</button>
|
||
</form>
|
||
</section>
|
||
|
||
<table class="cart-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Код</th>
|
||
<th>Настройки скидки</th>
|
||
<th>Срок / лимит</th>
|
||
<th>Использовано</th>
|
||
<th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<% promos.forEach(p => { %>
|
||
<tr>
|
||
<td><strong><%= p.code %></strong></td>
|
||
<td>
|
||
<form action="/admin/promo-codes/<%= p.id %>/update" method="post" class="admin-promo-form">
|
||
<input type="text" name="description" class="input input--sm" value="<%= p.description || '' %>" placeholder="Описание">
|
||
<select name="discount_type" class="input input--sm promo-type-select">
|
||
<option value="percent"<%= p.discount_type === 'percent' ? ' selected' : '' %>>%</option>
|
||
<option value="fixed"<%= p.discount_type === 'fixed' ? ' selected' : '' %>>₽</option>
|
||
</select>
|
||
<input type="number" name="discount_value" class="input input--sm" min="1" required
|
||
value="<%= p.discount_type === 'percent' ? p.discount_value : (p.discount_value / 100) %>"
|
||
title="<%= p.discount_type === 'percent' ? 'Процент' : 'Рубли' %>">
|
||
<label class="label label--inline muted">мин. заказ ₽
|
||
<input type="number" name="min_order_rub" class="input input--sm" min="0"
|
||
value="<%= Math.round(p.min_order_cents / 100) %>">
|
||
</label>
|
||
<button type="submit" class="btn btn--ghost btn--sm">Сохранить</button>
|
||
</form>
|
||
</td>
|
||
<td>
|
||
<form action="/admin/promo-codes/<%= p.id %>/update" method="post" class="admin-promo-form">
|
||
<input type="hidden" name="description" value="<%= p.description || '' %>">
|
||
<input type="hidden" name="discount_type" value="<%= p.discount_type %>">
|
||
<input type="hidden" name="discount_value" value="<%= p.discount_type === 'percent' ? p.discount_value : (p.discount_value / 100) %>">
|
||
<input type="hidden" name="min_order_rub" value="<%= Math.round(p.min_order_cents / 100) %>">
|
||
<label class="label label--inline">ещё дней
|
||
<input type="number" name="valid_days" class="input input--sm" value="7" min="1">
|
||
</label>
|
||
<label class="label label--inline">лимит
|
||
<input type="number" name="max_uses" class="input input--sm" min="1"
|
||
value="<%= p.max_uses || '' %>" placeholder="∞">
|
||
</label>
|
||
<button type="submit" class="btn btn--ghost btn--sm">Продлить</button>
|
||
<p class="muted" style="margin:0.25rem 0 0;font-size:0.85rem">до <%= new Date(p.expires_at).toLocaleString('ru-RU') %></p>
|
||
</form>
|
||
</td>
|
||
<td><%= p.use_count %><% if (p.max_uses) { %> / <%= p.max_uses %><% } %></td>
|
||
<td>
|
||
<span class="badge<%= p.active ? '' : ' badge--muted' %>"><%= p.active ? 'Активен' : 'Выкл.' %></span>
|
||
<form action="/admin/promo-codes/<%= p.id %>/toggle" method="post" style="margin-top:0.35rem">
|
||
<button type="submit" class="btn btn--ghost btn--sm"><%= p.active ? 'Выключить' : 'Включить' %></button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
<% }) %>
|
||
</tbody>
|
||
</table>
|
||
|
||
<script>
|
||
function bindPromoType(selectId, labelId) {
|
||
const sel = document.getElementById(selectId);
|
||
const lab = document.getElementById(labelId);
|
||
if (!sel || !lab) return;
|
||
const sync = () => {
|
||
lab.textContent = sel.value === 'fixed' ? 'Скидка (₽)' : 'Размер скидки (%)';
|
||
};
|
||
sel.addEventListener('change', sync);
|
||
sync();
|
||
}
|
||
bindPromoType('promo-type-new', 'promo-value-label-new');
|
||
</script>
|
||
|
||
<%- include('../partials/layout-end') %>
|