Redesign: Start/Stop buttons, locked settings, queue countdown, wb2 logo
- Replace tumbler with Start/Stop buttons (green/red) - Lock settings form with <fieldset disabled> when auto-reply active - Clear queue + reset fetch timer on settings save - Show 'Очередь подгрузится через X сек.' countdown in queue section - API /status returns next_fetch_seconds, queue_len, auto_reply_enabled - Login: price 15₽/день, up to 144 replies/day, wb2 logo, promo panel 25% width - Replace wb.png → wb2.png across all templates Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
<nav class="topbar">
|
||||
<div class="topbar__inner">
|
||||
<a href="{{ url_for('index') }}" class="topbar__brand">
|
||||
<img src="{{ url_for('static', filename='wb.png') }}" class="topbar__logo-img" alt="WB">
|
||||
<img src="{{ url_for('static', filename='wb2.png') }}" class="topbar__logo-img" alt="WB">
|
||||
<span class="topbar__name">Feedback</span>
|
||||
</a>
|
||||
<div class="topbar__nav">
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<nav class="topbar">
|
||||
<div class="topbar__inner">
|
||||
<a href="{{ url_for('index') }}" class="topbar__brand">
|
||||
<img src="{{ url_for('static', filename='wb.png') }}" class="topbar__logo-img" alt="WB">
|
||||
<img src="{{ url_for('static', filename='wb2.png') }}" class="topbar__logo-img" alt="WB">
|
||||
<span class="topbar__name">Feedback</span>
|
||||
</a>
|
||||
<div class="topbar__nav">
|
||||
|
||||
+45
-42
@@ -11,7 +11,7 @@
|
||||
<nav class="topbar">
|
||||
<div class="topbar__inner">
|
||||
<a href="{{ url_for('index') }}" class="topbar__brand">
|
||||
<img src="{{ url_for('static', filename='wb.png') }}" class="topbar__logo-img" alt="WB">
|
||||
<img src="{{ url_for('static', filename='wb2.png') }}" class="topbar__logo-img" alt="WB">
|
||||
<span class="topbar__name">Feedback</span>
|
||||
</a>
|
||||
<div class="topbar__nav">
|
||||
@@ -40,41 +40,32 @@
|
||||
<div class="alert alert-success">{{ success_message }}</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Автоответ -->
|
||||
<div class="card auto-reply">
|
||||
<div class="auto-reply__info">
|
||||
<div class="auto-reply__title">
|
||||
<h3>Автоответ</h3>
|
||||
{% if auto_reply_enabled %}
|
||||
<span class="status-dot status-dot--green"></span>
|
||||
<span class="badge badge-green">Включён</span>
|
||||
{% else %}
|
||||
<span class="status-dot status-dot--gray"></span>
|
||||
<span class="badge">Выключен</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<p>По требованию WB — <strong>1 ответ каждые 10 минут</strong>. Очередь обрабатывается автоматически.</p>
|
||||
{% if api_cooldown_seconds_left and api_cooldown_seconds_left > 0 %}
|
||||
<p style="margin-top:6px;color:var(--amber)">Следующий ответ через <span id="cooldown-counter" data-seconds="{{ api_cooldown_seconds_left }}">{{ api_cooldown_seconds_left }}</span> сек.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<form method="post" action="{{ url_for('auto_reply_toggle') }}" id="toggle-form">
|
||||
<input type="hidden" name="enabled" value="{{ 0 if auto_reply_enabled else 1 }}">
|
||||
<label class="tumbler" title="{{ 'Выключить автоответ' if auto_reply_enabled else 'Включить автоответ' }}">
|
||||
<input type="checkbox" class="tumbler__input" {{ 'checked' if auto_reply_enabled else '' }} onchange="document.getElementById('toggle-form').submit()">
|
||||
<span class="tumbler__track">
|
||||
<span class="tumbler__thumb"></span>
|
||||
</span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Настройки автоответа -->
|
||||
<div class="card">
|
||||
<div class="section-header">
|
||||
<h2>Настройки автоответа</h2>
|
||||
<form method="post" action="{{ url_for('auto_reply_toggle') }}">
|
||||
<input type="hidden" name="enabled" value="{{ 0 if auto_reply_enabled else 1 }}">
|
||||
{% if auto_reply_enabled %}
|
||||
<button type="submit" class="btn-stop">▮▮ Стоп</button>
|
||||
{% else %}
|
||||
<button type="submit" class="btn-start">▶ Старт</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if auto_reply_enabled %}
|
||||
<div class="autoreply-status">
|
||||
<span class="status-dot status-dot--green"></span>
|
||||
<span>Автоответ активен</span>
|
||||
{% if api_cooldown_seconds_left and api_cooldown_seconds_left > 0 %}
|
||||
· Следующий ответ через <span id="cooldown-counter" data-seconds="{{ api_cooldown_seconds_left }}">{{ api_cooldown_seconds_left }}</span> сек.
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" action="{{ url_for('auto_reply_settings') }}" id="settings-form">
|
||||
<fieldset {% if auto_reply_enabled %}disabled{% endif %} style="border:none;padding:0;margin:0">
|
||||
|
||||
<!-- Звёзды -->
|
||||
<div class="settings-section">
|
||||
@@ -130,6 +121,7 @@
|
||||
<div style="margin-top:20px">
|
||||
<button type="submit">Сохранить настройки</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -174,7 +166,13 @@
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-state">Очередь пуста — новые отзывы будут загружены автоматически.</div>
|
||||
{% if auto_reply_enabled and next_fetch_seconds_left > 0 %}
|
||||
<div class="empty-state">Очередь подгрузится через <span id="fetch-counter" data-seconds="{{ next_fetch_seconds_left }}">{{ next_fetch_seconds_left }}</span> сек.</div>
|
||||
{% elif auto_reply_enabled %}
|
||||
<div class="empty-state">Загрузка очереди…</div>
|
||||
{% else %}
|
||||
<div class="empty-state">Нажмите «Старт» для запуска автоответов.</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -267,23 +265,26 @@
|
||||
</nav>
|
||||
|
||||
<script>
|
||||
// ── Cooldown counter ───────────────────────────────────────────────
|
||||
(() => {
|
||||
const node = document.getElementById('cooldown-counter');
|
||||
// ── Tick-down helper ───────────────────────────────────────────────
|
||||
function startCountdown(id, onZero) {
|
||||
const node = document.getElementById(id);
|
||||
if (!node) return;
|
||||
let s = parseInt(node.dataset.seconds || '0', 10);
|
||||
const tick = setInterval(() => {
|
||||
s--;
|
||||
if (s <= 0) { clearInterval(tick); node.closest('p').remove(); return; }
|
||||
if (s <= 0) { clearInterval(tick); if (onZero) onZero(); else node.textContent = '0'; return; }
|
||||
node.textContent = s;
|
||||
}, 1000);
|
||||
})();
|
||||
return { update: (v) => { s = v; node.textContent = v; } };
|
||||
}
|
||||
|
||||
const cooldownCtrl = startCountdown('cooldown-counter');
|
||||
const fetchCtrl = startCountdown('fetch-counter', () => window.location.reload());
|
||||
|
||||
// ── Pool editor ────────────────────────────────────────────────────
|
||||
function syncHidden(star) {
|
||||
const items = document.querySelectorAll(`#pool-items-${star} .pool-item-input`);
|
||||
const lines = [...items].map(i => i.value.trim()).filter(Boolean);
|
||||
// inject as hidden inputs for form submission
|
||||
const container = document.getElementById(`pool-items-${star}`);
|
||||
container.querySelectorAll('input[name="pool_' + star + '_item"]').forEach(e => e.remove());
|
||||
lines.forEach(line => {
|
||||
@@ -326,7 +327,7 @@ document.getElementById('settings-form').addEventListener('submit', () => {
|
||||
|
||||
[1,2,3,4,5].forEach(initPool);
|
||||
|
||||
// ── API polling — reload when new log entry appears ────────────────
|
||||
// ── API polling ────────────────────────────────────────────────────
|
||||
(() => {
|
||||
let lastLogId = null;
|
||||
const poll = async () => {
|
||||
@@ -339,13 +340,15 @@ document.getElementById('settings-form').addEventListener('submit', () => {
|
||||
} else if (data.last_log_id !== lastLogId) {
|
||||
window.location.reload();
|
||||
}
|
||||
// sync cooldown if page wasn't reloaded
|
||||
const node = document.getElementById('cooldown-counter');
|
||||
if (node && data.cooldown > 0) node.textContent = data.cooldown;
|
||||
if (cooldownCtrl && data.cooldown > 0) cooldownCtrl.update(data.cooldown);
|
||||
if (fetchCtrl && data.next_fetch_seconds > 0) fetchCtrl.update(data.next_fetch_seconds);
|
||||
if (data.next_fetch_seconds === 0 && data.queue_len === 0 && data.auto_reply_enabled) {
|
||||
window.location.reload();
|
||||
}
|
||||
} catch(e) {}
|
||||
};
|
||||
poll();
|
||||
setInterval(poll, 15000);
|
||||
setInterval(poll, 10000);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<!-- Левая панель — маркетинг -->
|
||||
<div class="login-promo">
|
||||
<div class="login-promo__inner">
|
||||
<img src="{{ url_for('static', filename='wb.png') }}" class="login-promo__logo" alt="WB Feedback">
|
||||
<img src="{{ url_for('static', filename='wb2.png') }}" class="login-promo__logo" alt="WB Feedback">
|
||||
|
||||
<h1 class="login-promo__title">Автоответы на отзывы<br>Wildberries — на автопилоте</h1>
|
||||
<p class="login-promo__sub">Сервис сам отвечает на отзывы покупателей пока вы занимаетесь бизнесом. Никаких ручных ответов, никаких пропущенных оценок.</p>
|
||||
@@ -49,8 +49,8 @@
|
||||
</ul>
|
||||
|
||||
<div class="login-price">
|
||||
<div class="login-price__amount">7 ₽<span>/день</span></div>
|
||||
<div class="login-price__desc">За один магазин. Неограниченное количество отзывов</div>
|
||||
<div class="login-price__amount">15 ₽<span>/день</span></div>
|
||||
<div class="login-price__desc">За один магазин. До 144 ответов в день</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,7 +59,7 @@
|
||||
<div class="login-form-wrap">
|
||||
<section class="auth-card">
|
||||
<div class="auth-kicker">
|
||||
<img src="{{ url_for('static', filename='wb.png') }}" class="auth-kicker-logo-img" alt="WB">
|
||||
<img src="{{ url_for('static', filename='wb2.png') }}" class="auth-kicker-logo-img" alt="WB">
|
||||
<span class="auth-kicker-text">WB Feedback</span>
|
||||
</div>
|
||||
<h2 class="login-form-title">Войдите в кабинет</h2>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<div class="auth-shell">
|
||||
<section class="auth-card">
|
||||
<div class="auth-kicker">
|
||||
<img src="{{ url_for('static', filename='wb.png') }}" class="auth-kicker-logo-img" alt="WB">
|
||||
<img src="{{ url_for('static', filename='wb2.png') }}" class="auth-kicker-logo-img" alt="WB">
|
||||
<span class="auth-kicker-text">Wildberries Feedback</span>
|
||||
</div>
|
||||
<h1>Запрос доступа</h1>
|
||||
|
||||
Reference in New Issue
Block a user