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:
@@ -1051,6 +1051,17 @@ def _next_auto_reply_meta() -> Tuple[Optional[datetime], Optional[int]]:
|
|||||||
return next_dt, seconds_left
|
return next_dt, seconds_left
|
||||||
|
|
||||||
|
|
||||||
|
def _next_fetch_seconds_left() -> int:
|
||||||
|
raw = db.get_setting(AUTO_REPLY_LAST_FETCH_KEY)
|
||||||
|
if not raw:
|
||||||
|
return 0
|
||||||
|
try:
|
||||||
|
last_fetch = float(raw)
|
||||||
|
except ValueError:
|
||||||
|
return 0
|
||||||
|
return max(0, int(last_fetch + AUTO_REPLY_FETCH_INTERVAL_SECONDS - time.time()))
|
||||||
|
|
||||||
|
|
||||||
def auto_reply_loop() -> None:
|
def auto_reply_loop() -> None:
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
@@ -1275,6 +1286,8 @@ def index():
|
|||||||
success_message = f"Отправлено ответов: {count}"
|
success_message = f"Отправлено ответов: {count}"
|
||||||
elif status == "pools_saved":
|
elif status == "pools_saved":
|
||||||
success_message = "Пулы автоответов сохранены."
|
success_message = "Пулы автоответов сохранены."
|
||||||
|
elif status == "settings_saved":
|
||||||
|
success_message = "Настройки сохранены. Очередь будет обновлена."
|
||||||
elif status == "reply_failed":
|
elif status == "reply_failed":
|
||||||
error_text = request.args.get("error") or "Не удалось отправить ответы."
|
error_text = request.args.get("error") or "Не удалось отправить ответы."
|
||||||
error_message = error_text
|
error_message = error_text
|
||||||
@@ -1297,11 +1310,10 @@ def index():
|
|||||||
next_auto_reply_at=next_auto_reply_at,
|
next_auto_reply_at=next_auto_reply_at,
|
||||||
next_auto_reply_in_seconds=next_auto_reply_in_seconds,
|
next_auto_reply_in_seconds=next_auto_reply_in_seconds,
|
||||||
api_cooldown_seconds_left=api_cooldown_seconds_left,
|
api_cooldown_seconds_left=api_cooldown_seconds_left,
|
||||||
|
next_fetch_seconds_left=_next_fetch_seconds_left(),
|
||||||
enabled_stars=list(_load_enabled_stars()),
|
enabled_stars=list(_load_enabled_stars()),
|
||||||
filter_mode=_load_filter_mode(),
|
filter_mode=_load_filter_mode(),
|
||||||
reply_pools={n: _pool_to_multiline_text(_load_reply_pool(n)) for n in range(1, 6)},
|
reply_pools={n: _pool_to_multiline_text(_load_reply_pool(n)) for n in range(1, 6)},
|
||||||
reply_pool_5_list=_load_reply_pool(5),
|
|
||||||
reply_pool_4_list=_load_reply_pool(4),
|
|
||||||
auto_reply_queue=_load_auto_reply_queue(),
|
auto_reply_queue=_load_auto_reply_queue(),
|
||||||
auto_reply_logs=db.list_auto_reply_logs(limit=100),
|
auto_reply_logs=db.list_auto_reply_logs(limit=100),
|
||||||
current_user=g.user,
|
current_user=g.user,
|
||||||
@@ -1319,7 +1331,15 @@ def api_status():
|
|||||||
logs = db.list_auto_reply_logs(limit=1)
|
logs = db.list_auto_reply_logs(limit=1)
|
||||||
last_id = logs[0]["id"] if logs else None
|
last_id = logs[0]["id"] if logs else None
|
||||||
cooldown = _get_api_cooldown_seconds_left()
|
cooldown = _get_api_cooldown_seconds_left()
|
||||||
return jsonify({"last_log_id": last_id, "cooldown": cooldown})
|
next_fetch = _next_fetch_seconds_left()
|
||||||
|
queue_len = len(_load_auto_reply_queue())
|
||||||
|
return jsonify({
|
||||||
|
"last_log_id": last_id,
|
||||||
|
"cooldown": cooldown,
|
||||||
|
"next_fetch_seconds": next_fetch,
|
||||||
|
"queue_len": queue_len,
|
||||||
|
"auto_reply_enabled": is_auto_reply_enabled(),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@app.route("/auto-reply-toggle", methods=["POST"])
|
@app.route("/auto-reply-toggle", methods=["POST"])
|
||||||
@@ -1327,7 +1347,7 @@ def api_status():
|
|||||||
def auto_reply_toggle():
|
def auto_reply_toggle():
|
||||||
enabled = request.form.get("enabled") == "1"
|
enabled = request.form.get("enabled") == "1"
|
||||||
set_auto_reply_enabled(enabled)
|
set_auto_reply_enabled(enabled)
|
||||||
return redirect(url_for("index", action="unanswered", stars=[5, 4]))
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/auto-reply-pools", methods=["POST"])
|
@app.route("/auto-reply-pools", methods=["POST"])
|
||||||
@@ -1358,18 +1378,22 @@ def auto_reply_pools():
|
|||||||
@app.route("/auto-reply-settings", methods=["POST"])
|
@app.route("/auto-reply-settings", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def auto_reply_settings():
|
def auto_reply_settings():
|
||||||
|
if is_auto_reply_enabled():
|
||||||
|
return redirect(url_for("index"))
|
||||||
stars = [int(s) for s in request.form.getlist("stars") if s.isdigit() and 1 <= int(s) <= 5]
|
stars = [int(s) for s in request.form.getlist("stars") if s.isdigit() and 1 <= int(s) <= 5]
|
||||||
filter_mode = request.form.get("filter_mode", "no_text")
|
filter_mode = request.form.get("filter_mode", "no_text")
|
||||||
if filter_mode not in ("no_text", "empty", "all"):
|
if filter_mode not in ("no_text", "empty", "all"):
|
||||||
filter_mode = "no_text"
|
filter_mode = "no_text"
|
||||||
db.set_setting(AUTO_REPLY_STARS_KEY, json.dumps(stars))
|
db.set_setting(AUTO_REPLY_STARS_KEY, json.dumps(stars))
|
||||||
db.set_setting(AUTO_REPLY_FILTER_KEY, filter_mode)
|
db.set_setting(AUTO_REPLY_FILTER_KEY, filter_mode)
|
||||||
# Save pools for each enabled star
|
|
||||||
for star in range(1, 6):
|
for star in range(1, 6):
|
||||||
items = [i.strip() for i in request.form.getlist(f"pool_{star}_item") if i.strip()]
|
items = [i.strip() for i in request.form.getlist(f"pool_{star}_item") if i.strip()]
|
||||||
if items:
|
if items:
|
||||||
db.set_setting(f"auto_reply_pool_{star}", _pool_to_multiline_text(items))
|
db.set_setting(f"auto_reply_pool_{star}", _pool_to_multiline_text(items))
|
||||||
return redirect(url_for("index", status="pools_saved"))
|
# Clear queue and reset fetch window so next cycle rebuilds immediately
|
||||||
|
db.set_setting("auto_reply_queue_json", "[]")
|
||||||
|
db.set_setting(AUTO_REPLY_LAST_FETCH_KEY, "0")
|
||||||
|
return redirect(url_for("index", status="settings_saved"))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/reply", methods=["POST"])
|
@app.route("/reply", methods=["POST"])
|
||||||
|
|||||||
+41
-35
@@ -897,7 +897,8 @@ textarea:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.login-promo {
|
.login-promo {
|
||||||
flex: 0 0 300px;
|
flex: 0 0 25%;
|
||||||
|
min-width: 260px;
|
||||||
background: linear-gradient(145deg, #3D0066 0%, #6B0FA8 45%, #CB11AB 100%);
|
background: linear-gradient(145deg, #3D0066 0%, #6B0FA8 45%, #CB11AB 100%);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1024,7 +1025,7 @@ textarea:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.login-form-wrap {
|
.login-form-wrap {
|
||||||
flex: 0 0 420px;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -1374,47 +1375,52 @@ textarea:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ── Тумблер автоответа ─────────────────────────────────── */
|
/* ── Тумблер автоответа ─────────────────────────────────── */
|
||||||
.tumbler {
|
.btn-start,
|
||||||
|
.btn-stop {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
transition: opacity var(--t), transform var(--t);
|
||||||
}
|
}
|
||||||
.tumbler__input {
|
.btn-start {
|
||||||
position: absolute;
|
background: #22C55E;
|
||||||
width: 0;
|
color: #fff;
|
||||||
height: 0;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
}
|
||||||
.tumbler__track {
|
.btn-stop {
|
||||||
position: relative;
|
background: #EF4444;
|
||||||
display: inline-block;
|
color: #fff;
|
||||||
width: 52px;
|
|
||||||
height: 28px;
|
|
||||||
background: var(--c-border);
|
|
||||||
border-radius: 14px;
|
|
||||||
transition: background var(--t);
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
.tumbler__thumb {
|
.btn-start:hover,
|
||||||
position: absolute;
|
.btn-stop:hover {
|
||||||
top: 3px;
|
opacity: 0.88;
|
||||||
left: 3px;
|
transform: translateY(-1px);
|
||||||
width: 22px;
|
|
||||||
height: 22px;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
transition: transform var(--t);
|
|
||||||
box-shadow: 0 1px 4px rgba(0,0,0,.25);
|
|
||||||
}
|
}
|
||||||
.tumbler__input:checked ~ .tumbler__track {
|
|
||||||
background: #CB11AB;
|
.autoreply-status {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--c-text-muted);
|
||||||
|
padding: 8px 0 12px;
|
||||||
|
border-bottom: 1px solid var(--c-border);
|
||||||
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
.tumbler__input:checked ~ .tumbler__track .tumbler__thumb {
|
|
||||||
transform: translateX(24px);
|
fieldset:disabled .star-toggle,
|
||||||
}
|
fieldset:disabled .filter-option,
|
||||||
.tumbler__track:hover {
|
fieldset:disabled .pool-item-input,
|
||||||
filter: brightness(0.92);
|
fieldset:disabled .btn-add-item,
|
||||||
|
fieldset:disabled button[type="submit"] {
|
||||||
|
opacity: 0.45;
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Тег артикула ───────────────────────────────────────── */
|
/* ── Тег артикула ───────────────────────────────────────── */
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<nav class="topbar">
|
<nav class="topbar">
|
||||||
<div class="topbar__inner">
|
<div class="topbar__inner">
|
||||||
<a href="{{ url_for('index') }}" class="topbar__brand">
|
<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>
|
<span class="topbar__name">Feedback</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="topbar__nav">
|
<div class="topbar__nav">
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<nav class="topbar">
|
<nav class="topbar">
|
||||||
<div class="topbar__inner">
|
<div class="topbar__inner">
|
||||||
<a href="{{ url_for('index') }}" class="topbar__brand">
|
<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>
|
<span class="topbar__name">Feedback</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="topbar__nav">
|
<div class="topbar__nav">
|
||||||
|
|||||||
+45
-42
@@ -11,7 +11,7 @@
|
|||||||
<nav class="topbar">
|
<nav class="topbar">
|
||||||
<div class="topbar__inner">
|
<div class="topbar__inner">
|
||||||
<a href="{{ url_for('index') }}" class="topbar__brand">
|
<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>
|
<span class="topbar__name">Feedback</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="topbar__nav">
|
<div class="topbar__nav">
|
||||||
@@ -40,41 +40,32 @@
|
|||||||
<div class="alert alert-success">{{ success_message }}</div>
|
<div class="alert alert-success">{{ success_message }}</div>
|
||||||
{% endif %}
|
{% 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="card">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h2>Настройки автоответа</h2>
|
<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>
|
</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">
|
<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">
|
<div class="settings-section">
|
||||||
@@ -130,6 +121,7 @@
|
|||||||
<div style="margin-top:20px">
|
<div style="margin-top:20px">
|
||||||
<button type="submit">Сохранить настройки</button>
|
<button type="submit">Сохранить настройки</button>
|
||||||
</div>
|
</div>
|
||||||
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -174,7 +166,13 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% 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 %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -267,23 +265,26 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// ── Cooldown counter ───────────────────────────────────────────────
|
// ── Tick-down helper ───────────────────────────────────────────────
|
||||||
(() => {
|
function startCountdown(id, onZero) {
|
||||||
const node = document.getElementById('cooldown-counter');
|
const node = document.getElementById(id);
|
||||||
if (!node) return;
|
if (!node) return;
|
||||||
let s = parseInt(node.dataset.seconds || '0', 10);
|
let s = parseInt(node.dataset.seconds || '0', 10);
|
||||||
const tick = setInterval(() => {
|
const tick = setInterval(() => {
|
||||||
s--;
|
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;
|
node.textContent = s;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
})();
|
return { update: (v) => { s = v; node.textContent = v; } };
|
||||||
|
}
|
||||||
|
|
||||||
|
const cooldownCtrl = startCountdown('cooldown-counter');
|
||||||
|
const fetchCtrl = startCountdown('fetch-counter', () => window.location.reload());
|
||||||
|
|
||||||
// ── Pool editor ────────────────────────────────────────────────────
|
// ── Pool editor ────────────────────────────────────────────────────
|
||||||
function syncHidden(star) {
|
function syncHidden(star) {
|
||||||
const items = document.querySelectorAll(`#pool-items-${star} .pool-item-input`);
|
const items = document.querySelectorAll(`#pool-items-${star} .pool-item-input`);
|
||||||
const lines = [...items].map(i => i.value.trim()).filter(Boolean);
|
const lines = [...items].map(i => i.value.trim()).filter(Boolean);
|
||||||
// inject as hidden inputs for form submission
|
|
||||||
const container = document.getElementById(`pool-items-${star}`);
|
const container = document.getElementById(`pool-items-${star}`);
|
||||||
container.querySelectorAll('input[name="pool_' + star + '_item"]').forEach(e => e.remove());
|
container.querySelectorAll('input[name="pool_' + star + '_item"]').forEach(e => e.remove());
|
||||||
lines.forEach(line => {
|
lines.forEach(line => {
|
||||||
@@ -326,7 +327,7 @@ document.getElementById('settings-form').addEventListener('submit', () => {
|
|||||||
|
|
||||||
[1,2,3,4,5].forEach(initPool);
|
[1,2,3,4,5].forEach(initPool);
|
||||||
|
|
||||||
// ── API polling — reload when new log entry appears ────────────────
|
// ── API polling ────────────────────────────────────────────────────
|
||||||
(() => {
|
(() => {
|
||||||
let lastLogId = null;
|
let lastLogId = null;
|
||||||
const poll = async () => {
|
const poll = async () => {
|
||||||
@@ -339,13 +340,15 @@ document.getElementById('settings-form').addEventListener('submit', () => {
|
|||||||
} else if (data.last_log_id !== lastLogId) {
|
} else if (data.last_log_id !== lastLogId) {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
// sync cooldown if page wasn't reloaded
|
if (cooldownCtrl && data.cooldown > 0) cooldownCtrl.update(data.cooldown);
|
||||||
const node = document.getElementById('cooldown-counter');
|
if (fetchCtrl && data.next_fetch_seconds > 0) fetchCtrl.update(data.next_fetch_seconds);
|
||||||
if (node && data.cooldown > 0) node.textContent = data.cooldown;
|
if (data.next_fetch_seconds === 0 && data.queue_len === 0 && data.auto_reply_enabled) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
};
|
};
|
||||||
poll();
|
poll();
|
||||||
setInterval(poll, 15000);
|
setInterval(poll, 10000);
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<!-- Левая панель — маркетинг -->
|
<!-- Левая панель — маркетинг -->
|
||||||
<div class="login-promo">
|
<div class="login-promo">
|
||||||
<div class="login-promo__inner">
|
<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>
|
<h1 class="login-promo__title">Автоответы на отзывы<br>Wildberries — на автопилоте</h1>
|
||||||
<p class="login-promo__sub">Сервис сам отвечает на отзывы покупателей пока вы занимаетесь бизнесом. Никаких ручных ответов, никаких пропущенных оценок.</p>
|
<p class="login-promo__sub">Сервис сам отвечает на отзывы покупателей пока вы занимаетесь бизнесом. Никаких ручных ответов, никаких пропущенных оценок.</p>
|
||||||
@@ -49,8 +49,8 @@
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="login-price">
|
<div class="login-price">
|
||||||
<div class="login-price__amount">7 ₽<span>/день</span></div>
|
<div class="login-price__amount">15 ₽<span>/день</span></div>
|
||||||
<div class="login-price__desc">За один магазин. Неограниченное количество отзывов</div>
|
<div class="login-price__desc">За один магазин. До 144 ответов в день</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
<div class="login-form-wrap">
|
<div class="login-form-wrap">
|
||||||
<section class="auth-card">
|
<section class="auth-card">
|
||||||
<div class="auth-kicker">
|
<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>
|
<span class="auth-kicker-text">WB Feedback</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 class="login-form-title">Войдите в кабинет</h2>
|
<h2 class="login-form-title">Войдите в кабинет</h2>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<div class="auth-shell">
|
<div class="auth-shell">
|
||||||
<section class="auth-card">
|
<section class="auth-card">
|
||||||
<div class="auth-kicker">
|
<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>
|
<span class="auth-kicker-text">Wildberries Feedback</span>
|
||||||
</div>
|
</div>
|
||||||
<h1>Запрос доступа</h1>
|
<h1>Запрос доступа</h1>
|
||||||
|
|||||||
Reference in New Issue
Block a user