first commit
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Панель администратора</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<header>
|
||||
<h1>Панель администратора</h1>
|
||||
<p class="hint">Управление пользователями и подтверждение заявок.</p>
|
||||
<a href="{{ url_for('cabinet') }}">← Вернуться в кабинет</a>
|
||||
</header>
|
||||
{% if info_message %}
|
||||
<div class="alert alert-success">{{ info_message }}</div>
|
||||
{% endif %}
|
||||
<section class="cabinet-section">
|
||||
<h2>Пользователи</h2>
|
||||
<ul class="token-list">
|
||||
{% for user in users %}
|
||||
<li class="token-item">
|
||||
<div>
|
||||
<strong>{{ user["username"] }}</strong>
|
||||
{% if user["is_admin"] %}
|
||||
<span class="badge">Администратор</span>
|
||||
{% endif %}
|
||||
{% if user["is_active"] %}
|
||||
<span class="badge">Активен</span>
|
||||
{% else %}
|
||||
<span class="badge badge-inactive">Не активен</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if current_user["id"] != user["id"] %}
|
||||
<form method="post" class="inline-form">
|
||||
<input type="hidden" name="user_id" value="{{ user['id'] }}">
|
||||
{% if user["is_active"] %}
|
||||
<input type="hidden" name="admin_action" value="deactivate">
|
||||
<button type="submit" class="secondary">Отключить</button>
|
||||
{% else %}
|
||||
<input type="hidden" name="admin_action" value="activate">
|
||||
<button type="submit">Активировать</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,84 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Личный кабинет</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<header>
|
||||
<h1>Личный кабинет</h1>
|
||||
<p class="hint">Здесь можно управлять токенами магазинов и текущим пользователем.</p>
|
||||
<div class="user-bar">
|
||||
<span>{{ current_user["username"] }}</span>
|
||||
<div>
|
||||
{% if current_user["is_admin"] %}
|
||||
<a href="{{ url_for('admin_panel') }}">Панель администратора</a>
|
||||
{% endif %}
|
||||
<a href="{{ url_for('index') }}">Вернуться к отзывам</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% if error_message %}
|
||||
<div class="alert alert-error">{{ error_message }}</div>
|
||||
{% endif %}
|
||||
{% if success_message %}
|
||||
<div class="alert alert-success">{{ success_message }}</div>
|
||||
{% endif %}
|
||||
|
||||
<section class="cabinet-section">
|
||||
<h2>Сохранённые магазины</h2>
|
||||
{% if tokens %}
|
||||
<ul class="token-list">
|
||||
{% for token in tokens %}
|
||||
<li class="token-item">
|
||||
<div>
|
||||
<strong>{{ token.name }}</strong>
|
||||
{% 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">Активен</span>
|
||||
{% endif %}
|
||||
</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 %}>Использовать</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 %}
|
||||
<p>Пока нет сохранённых токенов.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="cabinet-section">
|
||||
<h2>Добавить магазин</h2>
|
||||
<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="Вставьте токен"></textarea>
|
||||
</label>
|
||||
<button type="submit">Сохранить</button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,196 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Отзывы Wildberries</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<header>
|
||||
<h1>Отзывы Wildberries</h1>
|
||||
<p class="hint">Используйте кнопки ниже, чтобы загрузить свежие отзывы или оставить только неотвеченные.</p>
|
||||
<div class="user-bar">
|
||||
<span>Вы вошли как {{ current_user["username"] }}</span>
|
||||
<a href="{{ url_for('logout') }}">Выйти</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% 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-link">
|
||||
<a href="{{ url_for('cabinet') }}">Личный кабинет</a>
|
||||
{% if active_token_name %}
|
||||
<span>Текущий токен: {{ active_token_name }}</span>
|
||||
{% else %}
|
||||
<span>Токен не выбран</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<form class="controls" method="get" action="/">
|
||||
<div class="star-filter">
|
||||
<span>Выберите оценки:</span>
|
||||
{% for star in [5,4,3,2,1] %}
|
||||
<label>
|
||||
<input type="checkbox" name="stars" value="{{ star }}" {% if star in selected_stars %}checked{% endif %}>
|
||||
{{ star }}★
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="control-buttons">
|
||||
<button type="submit" name="action" value="all">Выгрузить отзывы</button>
|
||||
<button type="submit" name="action" value="unanswered">Только неотвеченные</button>
|
||||
<button type="submit" name="action" value="unanswered" class="secondary" formaction="/?stars=5&stars=4">Новые 5★ и 4★</button>
|
||||
<button type="submit" name="action" value="clear" class="secondary">Очистить список</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<section class="auto-reply">
|
||||
<div>
|
||||
<strong>Автоответ:</strong>
|
||||
{% if auto_reply_enabled %}
|
||||
<span class="badge">Включён</span>
|
||||
{% else %}
|
||||
<span class="badge badge-inactive">Выключен</span>
|
||||
{% endif %}
|
||||
<p>При включении сервис каждые {{ auto_reply_interval_minutes }} минут проверяет новые отзывы 5★ и 4★ и отвечает случайным текстом из пула.</p>
|
||||
</div>
|
||||
<form method="post" action="{{ url_for('auto_reply_toggle') }}">
|
||||
<input type="hidden" name="enabled" value="{{ 0 if auto_reply_enabled else 1 }}">
|
||||
<button type="submit">{{ "✓ Автоответ включён" if auto_reply_enabled else "Включить автоответ" }}</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="auto-reply-pools">
|
||||
<h2>Пулы автоответов</h2>
|
||||
<p>Один вариант ответа в каждой строке. Для каждого нового отзыва 5★/4★ текст выбирается случайно.</p>
|
||||
<form method="post" action="{{ url_for('auto_reply_pools') }}">
|
||||
<label for="pool-5">Ответы для 5★</label>
|
||||
<textarea id="pool-5" name="pool_5" rows="5" required>{{ reply_pool_5_text }}</textarea>
|
||||
<label for="pool-4">Ответы для 4★</label>
|
||||
<textarea id="pool-4" name="pool_4" rows="5" required>{{ reply_pool_4_text }}</textarea>
|
||||
<button type="submit">Сохранить пулы</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="auto-reply-logs">
|
||||
<h2>Журнал автоответов (последние 100)</h2>
|
||||
{% if auto_reply_logs %}
|
||||
<div class="logs-table-wrap">
|
||||
<table class="logs-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Дата</th>
|
||||
<th>Оценка</th>
|
||||
<th>Товар</th>
|
||||
<th>Покупатель</th>
|
||||
<th>Статус</th>
|
||||
<th>Ответ</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for log in auto_reply_logs %}
|
||||
<tr>
|
||||
<td>{{ log["created_at"] }}</td>
|
||||
<td>{{ log["rating"] }}★</td>
|
||||
<td>{{ log["product_name"] or "-" }}</td>
|
||||
<td>{{ log["user_name"] or "-" }}</td>
|
||||
<td>{{ "Отправлен" if log["status"] == "sent" else "Ошибка" }}</td>
|
||||
<td>{{ log["reply_text"] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>Пока нет записей автоответа.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
{% if reviews %}
|
||||
<section class="summary">
|
||||
{% if current_filter == 'unanswered' %}
|
||||
<h2>Неотвеченные отзывы</h2>
|
||||
{% else %}
|
||||
<h2>Все отзывы</h2>
|
||||
{% endif %}
|
||||
<p>Показано {{ reviews|length }} отзывов. Выбраны оценки: {{ selected_stars_display|join(', ') }}★.</p>
|
||||
</section>
|
||||
{% if current_filter == 'unanswered' and has_token %}
|
||||
<form class="reply-form" method="post" action="{{ url_for('reply_all') }}">
|
||||
<label for="reply-text">Ответ для всех неотвеченных отзывов</label>
|
||||
<textarea id="reply-text" name="message" rows="4" placeholder="Напишите ответ один раз — он будет отправлен каждому неотвеченному отзыву" required></textarea>
|
||||
{% for star in selected_stars_display %}
|
||||
<input type="hidden" name="stars" value="{{ star }}">
|
||||
{% endfor %}
|
||||
{% for review in reviews %}
|
||||
<input type="hidden" name="review_id" value="{{ review.id }}">
|
||||
{% endfor %}
|
||||
<div class="reply-form__actions">
|
||||
<span>Допустимая длина ответа: 2–5000 символов.</span>
|
||||
<button type="submit">Ответить всем</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<section class="reviews">
|
||||
{% for review in reviews %}
|
||||
<article class="review">
|
||||
<header class="review__header">
|
||||
<div>
|
||||
<strong>{{ review.product_name or 'Без названия' }}</strong>
|
||||
<span class="rating">★ {{ review.rating }}</span>
|
||||
</div>
|
||||
<div class="meta">
|
||||
<span>{{ review.user_name or 'Покупатель' }}</span>
|
||||
<span>{{ review.created_at|format_datetime }}</span>
|
||||
</div>
|
||||
</header>
|
||||
{% if review.text %}
|
||||
<p class="review__text">{{ review.text }}</p>
|
||||
{% endif %}
|
||||
<dl class="details">
|
||||
{% if review.pros %}
|
||||
<dt>Достоинства</dt>
|
||||
<dd>{{ review.pros }}</dd>
|
||||
{% endif %}
|
||||
{% if review.cons %}
|
||||
<dt>Недостатки</dt>
|
||||
<dd>{{ review.cons }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
<footer>
|
||||
{% if review.answer %}
|
||||
<p class="answer"><strong>Ответ:</strong> {{ review.answer }}</p>
|
||||
{% else %}
|
||||
<p class="no-answer">Без ответа</p>
|
||||
{% endif %}
|
||||
</footer>
|
||||
{% if has_token %}
|
||||
<details class="inline-reply">
|
||||
<summary>Ответить</summary>
|
||||
<form method="post" action="{{ url_for('reply_single', review_id=review.id) }}">
|
||||
<textarea name="message" rows="3" placeholder="Введите ответ" required></textarea>
|
||||
{% for star in selected_stars_display %}
|
||||
<input type="hidden" name="stars" value="{{ star }}">
|
||||
{% endfor %}
|
||||
<input type="hidden" name="next_action" value="{{ 'unanswered' if current_filter == 'unanswered' else 'all' }}">
|
||||
<button type="submit">Отправить</button>
|
||||
</form>
|
||||
</details>
|
||||
{% endif %}
|
||||
</article>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% elif current_filter %}
|
||||
<div class="alert">Отзывы с выбранными оценками ({{ selected_stars_display|join(', ') }}★) не найдены.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Вход</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="page auth-page">
|
||||
<h1>Вход в систему</h1>
|
||||
{% if error_message %}
|
||||
<div class="alert alert-error">{{ error_message }}</div>
|
||||
{% endif %}
|
||||
<form method="post" class="auth-form">
|
||||
<label>
|
||||
Логин
|
||||
<input type="text" name="username" required />
|
||||
</label>
|
||||
<label>
|
||||
Пароль
|
||||
<input type="password" name="password" required />
|
||||
</label>
|
||||
<button type="submit">Войти</button>
|
||||
</form>
|
||||
<p>Нет аккаунта? <a href="{{ url_for('register') }}">Запросить доступ</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Регистрация</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="page auth-page">
|
||||
<h1>Запрос доступа</h1>
|
||||
{% if error_message %}
|
||||
<div class="alert alert-error">{{ error_message }}</div>
|
||||
{% endif %}
|
||||
{% if success_message %}
|
||||
<div class="alert alert-success">{{ success_message }}</div>
|
||||
{% endif %}
|
||||
<form method="post" class="auth-form">
|
||||
<label>
|
||||
Логин
|
||||
<input type="text" name="username" required />
|
||||
</label>
|
||||
<label>
|
||||
Пароль
|
||||
<input type="password" name="password" required />
|
||||
</label>
|
||||
<label>
|
||||
Повторите пароль
|
||||
<input type="password" name="confirm" required />
|
||||
</label>
|
||||
<button type="submit">Отправить заявку</button>
|
||||
</form>
|
||||
<p>Уже есть аккаунт? <a href="{{ url_for('login') }}">Войти</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user