Sync project structure and apply feature updates

- Move files from remote_copy/ to root (proper project structure)
- Add Docker setup: Dockerfile, docker-compose.yml with volume mounts
- Auto-reply: fix skip logic (only text field, not pros/cons)
- Auto-reply: fix _paginate to handle cooldown gracefully mid-pagination
- Auto-reply: fix fetch timestamp not saved on API error (infinite retry loop)
- Auto-reply: reduce EMPTY_REMAINING_FALLBACK from 600s to 150s default
- UI: add auto-reply queue display with article number (nm_id)
- UI: replace button with CSS tumbler toggle for auto-reply
- UI: add review_created_at and review_id columns to journal
- UI: add skipped/sent/error status colors to journal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-15 17:46:35 +03:00
parent 79768a4fac
commit 53f1bb2e71
21 changed files with 7037 additions and 783 deletions
+158
View File
@@ -0,0 +1,158 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Кабинет — WB Feedback</title>
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" />
</head>
<body>
<nav class="topbar">
<div class="topbar__inner">
<a href="{{ url_for('index') }}" class="topbar__brand">
<span class="topbar__logo">WB</span>
<span class="topbar__name">Feedback</span>
</a>
<div class="topbar__nav">
<a href="{{ url_for('index') }}" class="topbar__link">Отзывы</a>
<a href="{{ url_for('cabinet') }}" class="topbar__link active">Кабинет</a>
{% if current_user["is_admin"] %}
<a href="{{ url_for('admin_panel') }}" class="topbar__link">Администратор</a>
{% endif %}
</div>
<div class="topbar__user">
<span class="topbar__username">{{ current_user["username"] }}</span>
<a href="{{ url_for('logout') }}" class="btn-ghost">Выйти</a>
</div>
</div>
</nav>
<div class="page">
<div class="page-header">
<h1>Личный кабинет</h1>
<p class="hint">Управляйте токенами магазинов Wildberries.</p>
</div>
{% if error_message %}
<div class="alert alert-error">{{ error_message }}</div>
{% endif %}
{% if success_message %}
<div class="alert alert-success">{{ success_message }}</div>
{% endif %}
<div class="cabinet-section">
<div class="section-header">
<h2>Сохранённые магазины</h2>
{% if tokens %}
<span class="badge">{{ tokens|length }}</span>
{% endif %}
</div>
{% if tokens %}
<ul class="token-list">
{% for token in tokens %}
<li class="token-item">
<div class="token-main">
<div class="token-name">
{{ token.name }}
{% if current_user["is_admin"] and token.owner %}
<span class="token-owner">({{ token.owner }})</span>
{% endif %}
{% if token.id == active_token_id %}
<span class="badge badge-green">Активен</span>
{% endif %}
</div>
<form method="post" class="cabinet-form token-edit-form">
<input type="hidden" name="cabinet_action" value="edit">
<input type="hidden" name="token_id" value="{{ token.id }}">
<label>
Название магазина
<input type="text" name="name" required value="{{ token.name }}">
</label>
<label>
Токен API
<textarea name="token" rows="3" required>{{ token.token }}</textarea>
</label>
<div>
<button type="submit">Сохранить изменения</button>
</div>
</form>
</div>
<div class="token-actions">
<form method="post" class="inline-form">
<input type="hidden" name="cabinet_action" value="select">
<input type="hidden" name="token_id" value="{{ token.id }}">
<button type="submit" {% if token.id == active_token_id %}class="secondary"{% endif %}>
{{ "Используется" if token.id == active_token_id else "Использовать" }}
</button>
</form>
<form method="post" class="inline-form">
<input type="hidden" name="cabinet_action" value="check">
<input type="hidden" name="token_id" value="{{ token.id }}">
<button type="submit" class="secondary">Проверить</button>
</form>
</div>
</li>
{% endfor %}
</ul>
{% else %}
<div class="empty-state">Пока нет сохранённых магазинов.</div>
{% endif %}
</div>
<div class="cabinet-section">
<div class="section-header">
<h2>Добавить магазин</h2>
</div>
<form method="post" class="cabinet-form">
<input type="hidden" name="cabinet_action" value="add">
<label>
Название магазина
<input type="text" name="name" required placeholder="Например, Основной аккаунт">
</label>
<label>
Токен API
<textarea name="token" rows="3" required placeholder="Вставьте токен Wildberries"></textarea>
</label>
<div>
<button type="submit">Добавить магазин</button>
</div>
</form>
</div>
</div>
<!-- Нижняя навигация (мобайл) -->
<nav class="bottom-nav">
<a href="{{ url_for('index') }}" class="bottom-nav__item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2"/>
<rect x="9" y="3" width="6" height="4" rx="1"/>
<line x1="9" y1="12" x2="15" y2="12"/>
<line x1="9" y1="16" x2="13" y2="16"/>
</svg>
Отзывы
</a>
<a href="{{ url_for('cabinet') }}" class="bottom-nav__item active">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
<circle cx="12" cy="7" r="4"/>
</svg>
Кабинет
</a>
{% if current_user["is_admin"] %}
<a href="{{ url_for('admin_panel') }}" class="bottom-nav__item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
</svg>
Админ
</a>
{% endif %}
</nav>
</body>
</html>