🎨 refactor: Единая навигация — ВСЕ 14 страниц используют base.html
БЫЛО: Каждая из 14 страниц имела своё собственное вшитое меню
с разным набором кнопок (от 4 до 10 пунктов)
СТАЛО: Все 14 страниц наследуют base.html с единым меню из 14 пунктов
в 4 разделах: Пользователи / Коммуникации / Контент / Система
Убрано 1949 строк дублирующегося кода, добавлено 1323 строки чистого контента.
Экономия: -626 строк (32% сокращение).
Переписанные страницы:
- dashboard.html, users.html, verification.html
- phones.html, polls.html, events.html
- schedules.html, ads.html, scheduled_posts.html, export.html
Результат: На ЛЮБОЙ странице пользователь видит ВСЕ 14 пунктов меню.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
bd3a56ab3d
commit
97c3e5864d
10 changed files with 1334 additions and 1960 deletions
|
|
@ -1,40 +1,13 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Объявления - Домовой Бот</title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
.sidebar { min-height: 100vh; background: linear-gradient(180deg, #2c3e50 0%, #1a252f 100%); }
|
|
||||||
.sidebar .nav-link { color: rgba(255,255,255,0.8); padding: 1rem; }
|
|
||||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { color: white; background-color: rgba(255,255,255,0.1); }
|
|
||||||
.content { padding: 2rem; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2 sidebar p-0">
|
|
||||||
<div class="p-3 text-white text-center"><h4><i class="bi bi-house-door"></i> Домовой Бот</h4></div>
|
|
||||||
<nav class="nav flex-column">
|
|
||||||
<a class="nav-link" href="/"><i class="bi bi-speedometer2"></i> Дашборд</a>
|
|
||||||
<a class="nav-link" href="/users"><i class="bi bi-people"></i> Пользователи</a>
|
|
||||||
<a class="nav-link" href="/verification"><i class="bi bi-shield-check"></i> Верификация</a>
|
|
||||||
<a class="nav-link" href="/phones"><i class="bi bi-telephone"></i> Телефоны</a>
|
|
||||||
<a class="nav-link active" href="/ads"><i class="bi bi-megaphone"></i> Объявления</a>
|
|
||||||
<a class="nav-link" href="/broadcast"><i class="bi bi-broadcast"></i> Рассылки</a>
|
|
||||||
<a class="nav-link" href="/scheduled_posts"><i class="bi bi-calendar2-plus"></i> Запланированные посты</a>
|
|
||||||
<a class="nav-link" href="/polls"><i class="bi bi-bar-chart"></i> Опросы</a>
|
|
||||||
<a class="nav-link" href="/events"><i class="bi bi-calendar-event"></i> События</a>
|
|
||||||
<a class="nav-link" href="/schedules"><i class="bi bi-clock"></i> Расписания</a>
|
|
||||||
<a class="nav-link" href="/export"><i class="bi bi-download"></i> Экспорт</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 content">
|
|
||||||
<h2 class="mb-4"><i class="bi bi-megaphone"></i> Объявления</h2>
|
|
||||||
|
|
||||||
<div class="card mb-4">
|
{% block title %}Объявления - Домовой Бот{% endblock %}
|
||||||
|
|
||||||
|
{% block nav_ads %}active{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2 class="mb-4"><i class="bi bi-megaphone"></i> Объявления</h2>
|
||||||
|
|
||||||
|
<div class="card mb-4">
|
||||||
<div class="card-header bg-primary text-white">
|
<div class="card-header bg-primary text-white">
|
||||||
<h5 class="mb-0"><i class="bi bi-plus-lg"></i> Добавить объявление</h5>
|
<h5 class="mb-0"><i class="bi bi-plus-lg"></i> Добавить объявление</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -61,9 +34,9 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
|
|
@ -93,12 +66,11 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% endblock %}
|
||||||
</div>
|
|
||||||
</div>
|
{% block extra_script %}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script>
|
||||||
<script>
|
|
||||||
document.getElementById('addAdForm').addEventListener('submit', async (e) => {
|
document.getElementById('addAdForm').addEventListener('submit', async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const data = {
|
const data = {
|
||||||
|
|
@ -117,6 +89,5 @@
|
||||||
const result = await r.json();
|
const result = await r.json();
|
||||||
if (result.success) { alert('✅ Удалено'); location.reload(); }
|
if (result.success) { alert('✅ Удалено'); location.reload(); }
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,11 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Домовой Бот - Админ Панель</title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--primary-color: #2c3e50;
|
|
||||||
--secondary-color: #3498db;
|
|
||||||
--success-color: #27ae60;
|
|
||||||
--warning-color: #f39c12;
|
|
||||||
--danger-color: #e74c3c;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
{% block title %}Дашборд - Домовой Бот{% endblock %}
|
||||||
background-color: #f8f9fa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
{% block nav_dashboard %}active{% endblock %}
|
||||||
min-height: 100vh;
|
|
||||||
background: linear-gradient(180deg, var(--primary-color) 0%, #1a252f 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar .nav-link {
|
|
||||||
color: rgba(255,255,255,0.8);
|
|
||||||
padding: 1rem;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar .nav-link:hover,
|
|
||||||
.sidebar .nav-link.active {
|
|
||||||
color: white;
|
|
||||||
background-color: rgba(255,255,255,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar .nav-link i {
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
{% block extra_style %}
|
||||||
|
<style>
|
||||||
.stat-card {
|
.stat-card {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
|
|
@ -47,74 +13,18 @@
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
transition: transform 0.3s;
|
transition: transform 0.3s;
|
||||||
}
|
}
|
||||||
|
.stat-card:hover { transform: translateY(-5px); }
|
||||||
|
.stat-card.primary { background: linear-gradient(135deg, #3498db, #2980b9); }
|
||||||
|
.stat-card.success { background: linear-gradient(135deg, #27ae60, #229954); }
|
||||||
|
.stat-card.info { background: linear-gradient(135deg, #17a2b8, #117a8b); }
|
||||||
|
.stat-card.danger { background: linear-gradient(135deg, #e74c3c, #c0392b); }
|
||||||
|
.stat-card .stat-value { font-size: 2.5rem; font-weight: bold; }
|
||||||
|
.stat-card .stat-label { font-size: 0.9rem; opacity: 0.9; }
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
.stat-card:hover {
|
{% block content %}
|
||||||
transform: translateY(-5px);
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card.primary { background: linear-gradient(135deg, var(--secondary-color), #2980b9); }
|
|
||||||
.stat-card.success { background: linear-gradient(135deg, var(--success-color), #229954); }
|
|
||||||
.stat-card.warning { background: linear-gradient(135deg, var(--warning-color), #d68910); }
|
|
||||||
.stat-card.danger { background: linear-gradient(135deg, var(--danger-color), #c0392b); }
|
|
||||||
|
|
||||||
.stat-card .stat-value {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card .stat-label {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
border: none;
|
|
||||||
box-shadow: 0 0 20px rgba(0,0,0,0.1);
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
background-color: white;
|
|
||||||
border-bottom: 2px solid #f0f0f0;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<!-- Sidebar -->
|
|
||||||
<div class="col-md-2 sidebar p-0">
|
|
||||||
<div class="p-3 text-white text-center">
|
|
||||||
<h4><i class="bi bi-house-door"></i> Домовой Бот</h4>
|
|
||||||
<small>Админ Панель v1.11</small>
|
|
||||||
</div>
|
|
||||||
<nav class="nav flex-column">
|
|
||||||
<a class="nav-link active" href="/"><i class="bi bi-speedometer2"></i> Дашборд</a>
|
|
||||||
<a class="nav-link" href="/users"><i class="bi bi-people"></i> Пользователи</a>
|
|
||||||
<a class="nav-link" href="/verification">
|
|
||||||
<i class="bi bi-shield-check"></i> Верификация
|
|
||||||
{% if pending_count and pending_count > 0 %}
|
|
||||||
<span class="badge bg-danger ms-2">{{ pending_count }}</span>
|
|
||||||
{% endif %}
|
|
||||||
</a>
|
|
||||||
<a class="nav-link" href="/phones"><i class="bi bi-telephone"></i> Телефоны</a>
|
|
||||||
<a class="nav-link" href="/broadcast"><i class="bi bi-broadcast"></i> Рассылки</a>
|
|
||||||
<a class="nav-link" href="/scheduled_posts"><i class="bi bi-calendar2-plus"></i> Запланированные посты</a>
|
|
||||||
<a class="nav-link" href="/polls"><i class="bi bi-bar-chart"></i> Опросы</a>
|
|
||||||
<a class="nav-link" href="/events"><i class="bi bi-calendar-event"></i> События</a>
|
|
||||||
<a class="nav-link" href="/schedules"><i class="bi bi-clock"></i> Расписания</a>
|
|
||||||
<a class="nav-link" href="/export"><i class="bi bi-download"></i> Экспорт</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Main Content -->
|
|
||||||
<div class="col-md-10 content">
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
||||||
<h2>Дашборд</h2>
|
<h2>Дашборд</h2>
|
||||||
<div>
|
<div>
|
||||||
<span class="text-muted">Админ: {{ username }}</span>
|
<span class="text-muted">Админ: {{ username }}</span>
|
||||||
|
|
@ -122,10 +32,10 @@
|
||||||
<i class="bi bi-arrow-clockwise"></i> Обновить
|
<i class="bi bi-arrow-clockwise"></i> Обновить
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Stats Cards -->
|
<!-- Stats Cards -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<a href="/users?filter=all" style="text-decoration: none;">
|
<a href="/users?filter=all" style="text-decoration: none;">
|
||||||
<div class="stat-card primary">
|
<div class="stat-card primary">
|
||||||
|
|
@ -144,7 +54,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<a href="/users?filter=ig" style="text-decoration: none;">
|
<a href="/users?filter=ig" style="text-decoration: none;">
|
||||||
<div class="stat-card" style="background: linear-gradient(135deg, #17a2b8, #117a8b);">
|
<div class="stat-card info">
|
||||||
<div class="stat-value">{{ stats.ig }}</div>
|
<div class="stat-value">{{ stats.ig }}</div>
|
||||||
<div class="stat-label">Инициативная группа</div>
|
<div class="stat-label">Инициативная группа</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -158,10 +68,10 @@
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Additional Stats -->
|
<!-- Additional Stats -->
|
||||||
<div class="row mt-3">
|
<div class="row mt-3">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
|
|
@ -184,10 +94,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Quick Actions -->
|
<!-- Quick Actions -->
|
||||||
<div class="card mt-4">
|
<div class="card mt-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<i class="bi bi-lightning"></i> Быстрые действия
|
<i class="bi bi-lightning"></i> Быстрые действия
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -195,17 +105,15 @@
|
||||||
<a href="/users" class="btn btn-primary me-2">
|
<a href="/users" class="btn btn-primary me-2">
|
||||||
<i class="bi bi-people"></i> Пользователи
|
<i class="bi bi-people"></i> Пользователи
|
||||||
</a>
|
</a>
|
||||||
<a href="/export" class="btn btn-success me-2">
|
<a href="/broadcast" class="btn btn-success me-2">
|
||||||
<i class="bi bi-download"></i> Экспорт JSON
|
<i class="bi bi-broadcast"></i> Рассылки
|
||||||
</a>
|
</a>
|
||||||
<i class="bi bi-gear"></i> Настройки
|
<a href="/digests" class="btn btn-info me-2">
|
||||||
|
<i class="bi bi-newspaper"></i> Дайджесты
|
||||||
|
</a>
|
||||||
|
<a href="/export" class="btn btn-secondary me-2">
|
||||||
|
<i class="bi bi-download"></i> Экспорт
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% endblock %}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,13 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>События - Домовой Бот</title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
.sidebar { min-height: 100vh; background: linear-gradient(180deg, #2c3e50 0%, #1a252f 100%); }
|
|
||||||
.sidebar .nav-link { color: rgba(255,255,255,0.8); padding: 1rem; }
|
|
||||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { color: white; background-color: rgba(255,255,255,0.1); }
|
|
||||||
.content { padding: 2rem; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2 sidebar p-0">
|
|
||||||
<div class="p-3 text-white text-center"><h4><i class="bi bi-house-door"></i> Домовой Бот</h4></div>
|
|
||||||
<nav class="nav flex-column">
|
|
||||||
<a class="nav-link" href="/"><i class="bi bi-speedometer2"></i> Дашборд</a>
|
|
||||||
<a class="nav-link" href="/users"><i class="bi bi-people"></i> Пользователи</a>
|
|
||||||
<a class="nav-link" href="/verification"><i class="bi bi-shield-check"></i> Верификация</a>
|
|
||||||
<a class="nav-link" href="/phones"><i class="bi bi-telephone"></i> Телефоны</a>
|
|
||||||
<a class="nav-link" href="/ads"><i class="bi bi-megaphone"></i> Объявления</a>
|
|
||||||
<a class="nav-link" href="/broadcast"><i class="bi bi-broadcast"></i> Рассылки</a>
|
|
||||||
<a class="nav-link" href="/scheduled_posts"><i class="bi bi-calendar2-plus"></i> Запланированные посты</a>
|
|
||||||
<a class="nav-link" href="/polls"><i class="bi bi-bar-chart"></i> Опросы</a>
|
|
||||||
<a class="nav-link active" href="/events"><i class="bi bi-calendar-event"></i> События</a>
|
|
||||||
<a class="nav-link" href="/schedules"><i class="bi bi-clock"></i> Расписания</a>
|
|
||||||
<a class="nav-link" href="/export"><i class="bi bi-download"></i> Экспорт</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 content">
|
|
||||||
<h2 class="mb-4"><i class="bi bi-calendar-event"></i> События дома</h2>
|
|
||||||
|
|
||||||
<div class="card mb-4">
|
{% block title %}События - Домовой Бот{% endblock %}
|
||||||
|
|
||||||
|
{% block nav_events %}active{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2 class="mb-4"><i class="bi bi-calendar-event"></i> События дома</h2>
|
||||||
|
|
||||||
|
<div class="card mb-4">
|
||||||
<div class="card-header bg-primary text-white">
|
<div class="card-header bg-primary text-white">
|
||||||
<h5 class="mb-0"><i class="bi bi-plus-lg"></i> Добавить событие</h5>
|
<h5 class="mb-0"><i class="bi bi-plus-lg"></i> Добавить событие</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -62,9 +35,9 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5>Предстоящие события:</h5>
|
<h5>Предстоящие события:</h5>
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
|
|
@ -83,12 +56,11 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% endblock %}
|
||||||
</div>
|
|
||||||
</div>
|
{% block extra_script %}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script>
|
||||||
<script>
|
|
||||||
document.getElementById('addEventForm').addEventListener('submit', async (e) => {
|
document.getElementById('addEventForm').addEventListener('submit', async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const data = {
|
const data = {
|
||||||
|
|
@ -104,6 +76,5 @@
|
||||||
const result = await r.json();
|
const result = await r.json();
|
||||||
if (result.success) { alert('✅ Событие добавлено'); location.reload(); }
|
if (result.success) { alert('✅ Событие добавлено'); location.reload(); }
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,13 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Экспорт - Домовой Бот</title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
.sidebar {
|
|
||||||
min-height: 100vh;
|
|
||||||
background: linear-gradient(180deg, #2c3e50 0%, #1a252f 100%);
|
|
||||||
}
|
|
||||||
.sidebar .nav-link { color: rgba(255,255,255,0.8); padding: 1rem; }
|
|
||||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { color: white; background-color: rgba(255,255,255,0.1); }
|
|
||||||
.content { padding: 2rem; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2 sidebar p-0">
|
|
||||||
<div class="p-3 text-white text-center">
|
|
||||||
<h4><i class="bi bi-house-door"></i> Домовой Бот</h4>
|
|
||||||
</div>
|
|
||||||
<nav class="nav flex-column">
|
|
||||||
<a class="nav-link" href="/"><i class="bi bi-speedometer2"></i> Дашборд</a>
|
|
||||||
<a class="nav-link" href="/users"><i class="bi bi-people"></i> Пользователи</a>
|
|
||||||
<a class="nav-link active" href="/export"><i class="bi bi-download"></i> Экспорт</a>
|
|
||||||
<a class="nav-link" href="/scheduled_posts"><i class="bi bi-calendar2-plus"></i> Запланированные посты</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-10 content">
|
{% block title %}Экспорт - Домовой Бот{% endblock %}
|
||||||
<h2 class="mb-4">Экспорт данных</h2>
|
|
||||||
|
|
||||||
<div class="row">
|
{% block nav_export %}active{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2 class="mb-4">Экспорт данных</h2>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
|
|
@ -69,13 +42,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% endblock %}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
{% block extra_script %}
|
||||||
<script>
|
<script>
|
||||||
async function exportJSON() {
|
async function exportJSON() {
|
||||||
const resultDiv = document.getElementById('exportResult');
|
const resultDiv = document.getElementById('exportResult');
|
||||||
resultDiv.innerHTML = '<div class="alert alert-info">Экспорт...</div>';
|
resultDiv.innerHTML = '<div class="alert alert-info">Экспорт...</div>';
|
||||||
|
|
@ -98,6 +69,5 @@
|
||||||
resultDiv.innerHTML = `<div class="alert alert-danger">Ошибка: ${error.message}</div>`;
|
resultDiv.innerHTML = `<div class="alert alert-danger">Ошибка: ${error.message}</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,18 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Телефоны и службы - Домовой Бот</title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
.sidebar { min-height: 100vh; background: linear-gradient(180deg, #2c3e50 0%, #1a252f 100%); }
|
|
||||||
.sidebar .nav-link { color: rgba(255,255,255,0.8); padding: 1rem; }
|
|
||||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { color: white; background-color: rgba(255,255,255,0.1); }
|
|
||||||
.content { padding: 2rem; }
|
|
||||||
.service-image-preview { max-width: 200px; max-height: 200px; object-fit: contain; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2 sidebar p-0">
|
|
||||||
<div class="p-3 text-white text-center"><h4><i class="bi bi-house-door"></i> Домовой Бот</h4></div>
|
|
||||||
<nav class="nav flex-column">
|
|
||||||
<a class="nav-link" href="/"><i class="bi bi-speedometer2"></i> Дашборд</a>
|
|
||||||
<a class="nav-link" href="/users"><i class="bi bi-people"></i> Пользователи</a>
|
|
||||||
<a class="nav-link" href="/verification"><i class="bi bi-shield-check"></i> Верификация</a>
|
|
||||||
<a class="nav-link active" href="/phones"><i class="bi bi-telephone"></i> Телефоны</a>
|
|
||||||
<a class="nav-link" href="/ads"><i class="bi bi-megaphone"></i> Объявления</a>
|
|
||||||
<a class="nav-link" href="/broadcast"><i class="bi bi-broadcast"></i> Рассылки</a>
|
|
||||||
<a class="nav-link" href="/scheduled_posts"><i class="bi bi-calendar2-plus"></i> Запланированные посты</a>
|
|
||||||
<a class="nav-link" href="/polls"><i class="bi bi-bar-chart"></i> Опросы</a>
|
|
||||||
<a class="nav-link" href="/events"><i class="bi bi-calendar-event"></i> События</a>
|
|
||||||
<a class="nav-link" href="/schedules"><i class="bi bi-clock"></i> Расписания</a>
|
|
||||||
<a class="nav-link" href="/export"><i class="bi bi-download"></i> Экспорт</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 content">
|
|
||||||
<h2 class="mb-4"><i class="bi bi-telephone"></i> Телефоны и службы</h2>
|
|
||||||
|
|
||||||
<!-- Добавить службу -->
|
{% block title %}Телефоны и службы - Домовой Бот{% endblock %}
|
||||||
<div class="card mb-4">
|
|
||||||
|
{% block nav_phones %}active{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_style %}
|
||||||
|
.service-image-preview { max-width: 200px; max-height: 200px; object-fit: contain; }
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2 class="mb-4"><i class="bi bi-telephone"></i> Телефоны и службы</h2>
|
||||||
|
|
||||||
|
<!-- Добавить службу -->
|
||||||
|
<div class="card mb-4">
|
||||||
<div class="card-header bg-primary text-white">
|
<div class="card-header bg-primary text-white">
|
||||||
<h5 class="mb-0"><i class="bi bi-plus-lg"></i> Добавить службу</h5>
|
<h5 class="mb-0"><i class="bi bi-plus-lg"></i> Добавить службу</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -65,10 +41,10 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Загрузить картинку -->
|
<!-- Загрузить картинку -->
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header bg-success text-white">
|
<div class="card-header bg-success text-white">
|
||||||
<h5 class="mb-0"><i class="bi bi-image"></i> Загрузить картинку для службы</h5>
|
<h5 class="mb-0"><i class="bi bi-image"></i> Загрузить картинку для службы</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -93,10 +69,10 @@
|
||||||
</form>
|
</form>
|
||||||
<div id="uploadResult" class="mt-3"></div>
|
<div id="uploadResult" class="mt-3"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Список служб -->
|
<!-- Список служб -->
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5>Существующие службы:</h5>
|
<h5>Существующие службы:</h5>
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
|
|
@ -151,12 +127,11 @@
|
||||||
<p class="text-center text-muted">Служб нет</p>
|
<p class="text-center text-muted">Служб нет</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% endblock %}
|
||||||
</div>
|
|
||||||
</div>
|
{% block extra_script %}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script>
|
||||||
<script>
|
|
||||||
// Добавление службы
|
// Добавление службы
|
||||||
document.getElementById('addServiceForm').addEventListener('submit', async (e) => {
|
document.getElementById('addServiceForm').addEventListener('submit', async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -208,6 +183,5 @@
|
||||||
const result = await r.json();
|
const result = await r.json();
|
||||||
if (result.success) { alert('✅ Удалено'); location.reload(); }
|
if (result.success) { alert('✅ Удалено'); location.reload(); }
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,13 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Опросы - Домовой Бот</title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
.sidebar { min-height: 100vh; background: linear-gradient(180deg, #2c3e50 0%, #1a252f 100%); }
|
|
||||||
.sidebar .nav-link { color: rgba(255,255,255,0.8); padding: 1rem; }
|
|
||||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { color: white; background-color: rgba(255,255,255,0.1); }
|
|
||||||
.content { padding: 2rem; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2 sidebar p-0">
|
|
||||||
<div class="p-3 text-white text-center"><h4><i class="bi bi-house-door"></i> Домовой Бот</h4></div>
|
|
||||||
<nav class="nav flex-column">
|
|
||||||
<a class="nav-link" href="/"><i class="bi bi-speedometer2"></i> Дашборд</a>
|
|
||||||
<a class="nav-link" href="/users"><i class="bi bi-people"></i> Пользователи</a>
|
|
||||||
<a class="nav-link" href="/verification"><i class="bi bi-shield-check"></i> Верификация</a>
|
|
||||||
<a class="nav-link" href="/phones"><i class="bi bi-telephone"></i> Телефоны</a>
|
|
||||||
<a class="nav-link" href="/ads"><i class="bi bi-megaphone"></i> Объявления</a>
|
|
||||||
<a class="nav-link" href="/broadcast"><i class="bi bi-broadcast"></i> Рассылки</a>
|
|
||||||
<a class="nav-link" href="/scheduled_posts"><i class="bi bi-calendar2-plus"></i> Запланированные посты</a>
|
|
||||||
<a class="nav-link active" href="/polls"><i class="bi bi-bar-chart"></i> Опросы</a>
|
|
||||||
<a class="nav-link" href="/events"><i class="bi bi-calendar-event"></i> События</a>
|
|
||||||
<a class="nav-link" href="/schedules"><i class="bi bi-clock"></i> Расписания</a>
|
|
||||||
<a class="nav-link" href="/export"><i class="bi bi-download"></i> Экспорт</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 content">
|
|
||||||
<h2 class="mb-4"><i class="bi bi-bar-chart"></i> Опросы и голосования</h2>
|
|
||||||
|
|
||||||
<div class="card mb-4">
|
{% block title %}Опросы - Домовой Бот{% endblock %}
|
||||||
|
|
||||||
|
{% block nav_polls %}active{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2 class="mb-4"><i class="bi bi-bar-chart"></i> Опросы и голосования</h2>
|
||||||
|
|
||||||
|
<div class="card mb-4">
|
||||||
<div class="card-header bg-primary text-white">
|
<div class="card-header bg-primary text-white">
|
||||||
<h5 class="mb-0"><i class="bi bi-plus-lg"></i> Создать опрос</h5>
|
<h5 class="mb-0"><i class="bi bi-plus-lg"></i> Создать опрос</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -52,9 +25,9 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5>Активные опросы:</h5>
|
<h5>Активные опросы:</h5>
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
|
|
@ -82,12 +55,11 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% endblock %}
|
||||||
</div>
|
|
||||||
</div>
|
{% block extra_script %}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script>
|
||||||
<script>
|
|
||||||
document.getElementById('createPollForm').addEventListener('submit', async (e) => {
|
document.getElementById('createPollForm').addEventListener('submit', async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const question = document.getElementById('pollQuestion').value;
|
const question = document.getElementById('pollQuestion').value;
|
||||||
|
|
@ -108,6 +80,5 @@
|
||||||
const result = await r.json();
|
const result = await r.json();
|
||||||
if (result.success) { alert('✅ Опрос закрыт'); location.reload(); }
|
if (result.success) { alert('✅ Опрос закрыт'); location.reload(); }
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,21 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Запланированные посты - Домовой Бот</title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
.sidebar { min-height: 100vh; background: linear-gradient(180deg, #2c3e50 0%, #1a252f 100%); }
|
|
||||||
.sidebar .nav-link { color: rgba(255,255,255,0.8); padding: 1rem; }
|
|
||||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { color: white; background-color: rgba(255,255,255,0.1); }
|
|
||||||
.content { padding: 2rem; }
|
|
||||||
#preview { max-width: 400px; margin-top: 1rem; display: none; }
|
|
||||||
#preview img { max-width: 100%; border-radius: 8px; }
|
|
||||||
.post-card { transition: transform 0.2s; }
|
|
||||||
.post-card:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2 sidebar p-0">
|
|
||||||
<div class="p-3 text-white text-center">
|
|
||||||
<h4><i class="bi bi-house-door"></i> Домовой Бот</h4>
|
|
||||||
</div>
|
|
||||||
<nav class="nav flex-column">
|
|
||||||
<a class="nav-link" href="/"><i class="bi bi-speedometer2"></i> Дашборд</a>
|
|
||||||
<a class="nav-link" href="/users"><i class="bi bi-people"></i> Пользователи</a>
|
|
||||||
<a class="nav-link" href="/verification"><i class="bi bi-shield-check"></i> Верификация</a>
|
|
||||||
<a class="nav-link" href="/phones"><i class="bi bi-telephone"></i> Телефоны</a>
|
|
||||||
<a class="nav-link" href="/broadcast"><i class="bi bi-broadcast"></i> Рассылки</a>
|
|
||||||
<a class="nav-link active" href="/scheduled_posts"><i class="bi bi-calendar-event"></i> Запланированные посты</a>
|
|
||||||
<a class="nav-link" href="/polls"><i class="bi bi-bar-chart"></i> Опросы</a>
|
|
||||||
<a class="nav-link" href="/events"><i class="bi bi-calendar-event"></i> События</a>
|
|
||||||
<a class="nav-link" href="/schedules"><i class="bi bi-clock"></i> Расписания</a>
|
|
||||||
<a class="nav-link" href="/export"><i class="bi bi-download"></i> Экспорт</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 content">
|
|
||||||
<h2 class="mb-4"><i class="bi bi-calendar2-plus"></i> Запланированные посты</h2>
|
|
||||||
|
|
||||||
<!-- Форма создания поста -->
|
{% block title %}Запланированные посты - Домовой Бот{% endblock %}
|
||||||
<div class="card mb-4">
|
|
||||||
|
{% block nav_scheduled_posts %}active{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_style %}
|
||||||
|
#preview { max-width: 400px; margin-top: 1rem; display: none; }
|
||||||
|
#preview img { max-width: 100%; border-radius: 8px; }
|
||||||
|
.post-card { transition: transform 0.2s; }
|
||||||
|
.post-card:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2 class="mb-4"><i class="bi bi-calendar2-plus"></i> Запланированные посты</h2>
|
||||||
|
|
||||||
|
<!-- Форма создания поста -->
|
||||||
|
<div class="card mb-4">
|
||||||
<div class="card-header bg-primary text-white">
|
<div class="card-header bg-primary text-white">
|
||||||
<h5 class="mb-0"><i class="bi bi-plus-circle"></i> Создать новый пост</h5>
|
<h5 class="mb-0"><i class="bi bi-plus-circle"></i> Создать новый пост</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -119,10 +94,10 @@
|
||||||
|
|
||||||
<div id="result" class="mt-4"></div>
|
<div id="result" class="mt-4"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Список запланированных постов -->
|
<!-- Список запланированных постов -->
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header bg-info text-white">
|
<div class="card-header bg-info text-white">
|
||||||
<h5 class="mb-0"><i class="bi bi-list-check"></i> Запланированные посты</h5>
|
<h5 class="mb-0"><i class="bi bi-list-check"></i> Запланированные посты</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -133,12 +108,11 @@
|
||||||
<p class="mt-3">Пока нет запланированных постов</p>
|
<p class="mt-3">Пока нет запланированных постов</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% endblock %}
|
||||||
</div>
|
|
||||||
</div>
|
{% block extra_script %}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script>
|
||||||
<script>
|
|
||||||
// Установка минимальной даты (сегодня)
|
// Установка минимальной даты (сегодня)
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
|
|
@ -291,6 +265,5 @@
|
||||||
alert('❌ Ошибка: ' + err.message);
|
alert('❌ Ошибка: ' + err.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,13 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Расписания - Домовой Бот</title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
.sidebar { min-height: 100vh; background: linear-gradient(180deg, #2c3e50 0%, #1a252f 100%); }
|
|
||||||
.sidebar .nav-link { color: rgba(255,255,255,0.8); padding: 1rem; }
|
|
||||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { color: white; background-color: rgba(255,255,255,0.1); }
|
|
||||||
.content { padding: 2rem; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2 sidebar p-0">
|
|
||||||
<div class="p-3 text-white text-center"><h4><i class="bi bi-house-door"></i> Домовой Бот</h4></div>
|
|
||||||
<nav class="nav flex-column">
|
|
||||||
<a class="nav-link" href="/"><i class="bi bi-speedometer2"></i> Дашборд</a>
|
|
||||||
<a class="nav-link" href="/users"><i class="bi bi-people"></i> Пользователи</a>
|
|
||||||
<a class="nav-link" href="/verification"><i class="bi bi-shield-check"></i> Верификация</a>
|
|
||||||
<a class="nav-link" href="/phones"><i class="bi bi-telephone"></i> Телефоны</a>
|
|
||||||
<a class="nav-link" href="/ads"><i class="bi bi-megaphone"></i> Объявления</a>
|
|
||||||
<a class="nav-link" href="/broadcast"><i class="bi bi-broadcast"></i> Рассылки</a>
|
|
||||||
<a class="nav-link" href="/scheduled_posts"><i class="bi bi-calendar2-plus"></i> Запланированные посты</a>
|
|
||||||
<a class="nav-link" href="/polls"><i class="bi bi-bar-chart"></i> Опросы</a>
|
|
||||||
<a class="nav-link" href="/events"><i class="bi bi-calendar-event"></i> События</a>
|
|
||||||
<a class="nav-link active" href="/schedules"><i class="bi bi-clock"></i> Расписания</a>
|
|
||||||
<a class="nav-link" href="/export"><i class="bi bi-download"></i> Экспорт</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10 content">
|
|
||||||
<h2 class="mb-4"><i class="bi bi-clock"></i> Расписания отключений</h2>
|
|
||||||
|
|
||||||
<div class="card mb-4">
|
{% block title %}Расписания - Домовой Бот{% endblock %}
|
||||||
|
|
||||||
|
{% block nav_schedules %}active{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2 class="mb-4"><i class="bi bi-clock"></i> Расписания отключений</h2>
|
||||||
|
|
||||||
|
<div class="card mb-4">
|
||||||
<div class="card-header bg-primary text-white">
|
<div class="card-header bg-primary text-white">
|
||||||
<h5 class="mb-0"><i class="bi bi-plus-lg"></i> Добавить расписание</h5>
|
<h5 class="mb-0"><i class="bi bi-plus-lg"></i> Добавить расписание</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -67,9 +40,9 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
|
|
@ -100,12 +73,11 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% endblock %}
|
||||||
</div>
|
|
||||||
</div>
|
{% block extra_script %}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script>
|
||||||
<script>
|
|
||||||
document.getElementById('addScheduleForm').addEventListener('submit', async (e) => {
|
document.getElementById('addScheduleForm').addEventListener('submit', async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const data = {
|
const data = {
|
||||||
|
|
@ -128,6 +100,5 @@
|
||||||
const result = await r.json();
|
const result = await r.json();
|
||||||
if (result.success) { alert('✅ Удалено'); location.reload(); }
|
if (result.success) { alert('✅ Удалено'); location.reload(); }
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -1,96 +1,43 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Пользователи - Домовой Бот</title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
.sidebar {
|
|
||||||
min-height: 100vh;
|
|
||||||
background: linear-gradient(180deg, #2c3e50 0%, #1a252f 100%);
|
|
||||||
}
|
|
||||||
.sidebar .nav-link { color: rgba(255,255,255,0.8); padding: 1rem; }
|
|
||||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { color: white; background-color: rgba(255,255,255,0.1); }
|
|
||||||
.content { padding: 2rem; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2 sidebar p-0">
|
|
||||||
<div class="p-3 text-white text-center">
|
|
||||||
<h4><i class="bi bi-house-door"></i> Домовой Бот</h4>
|
|
||||||
</div>
|
|
||||||
<nav class="nav flex-column">
|
|
||||||
<a class="nav-link" href="/"><i class="bi bi-speedometer2"></i> Дашборд</a>
|
|
||||||
<a class="nav-link active" href="/users"><i class="bi bi-people"></i> Пользователи</a>
|
|
||||||
<a class="nav-link" href="/verification"><i class="bi bi-shield-check"></i> Верификация</a>
|
|
||||||
<a class="nav-link" href="/phones"><i class="bi bi-telephone"></i> Телефоны</a>
|
|
||||||
<a class="nav-link" href="/export"><i class="bi bi-download"></i> Экспорт</a>
|
|
||||||
<a class="nav-link" href="/scheduled_posts"><i class="bi bi-calendar2-plus"></i> Запланированные посты</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-10 content">
|
{% block title %}Пользователи - Домовой Бот{% endblock %}
|
||||||
<h2 class="mb-4">
|
|
||||||
|
{% block nav_users %}active{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2 class="mb-4">
|
||||||
Пользователи
|
Пользователи
|
||||||
{% if current_filter %}
|
{% if current_filter %}
|
||||||
<span class="badge bg-info ms-2">
|
<span class="badge bg-info ms-2">
|
||||||
{% if current_filter == 'verified' %}
|
{% if current_filter == 'verified' %}✅ Верифицированные
|
||||||
✅ Верифицированные
|
{% elif current_filter == 'unverified' %}⏳ Не верифицированы
|
||||||
{% elif current_filter == 'unverified' %}
|
{% elif current_filter == 'ig' %}🔵 Инициативная группа
|
||||||
⏳ Не верифицированы
|
{% elif current_filter == 'active_unverified' %}🔥 Активные (50+ сообщ.)
|
||||||
{% elif current_filter == 'ig' %}
|
{% else %}Все{% endif %}
|
||||||
🔵 Инициативная группа
|
|
||||||
{% elif current_filter == 'active_unverified' %}
|
|
||||||
🔥 Активные (50+ сообщ.)
|
|
||||||
{% else %}
|
|
||||||
Все
|
|
||||||
{% endif %}
|
|
||||||
</span>
|
</span>
|
||||||
<a href="/users" class="btn btn-sm btn-outline-secondary ms-2">
|
<a href="/users" class="btn btn-sm btn-outline-secondary ms-2"><i class="bi bi-x"></i> Сбросить фильтр</a>
|
||||||
<i class="bi bi-x"></i> Сбросить фильтр
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<!-- Pending Verifications Alert -->
|
<!-- Pending Verifications Alert -->
|
||||||
{% if pending_count and pending_count > 0 %}
|
{% if pending_count and pending_count > 0 %}
|
||||||
<div class="card mb-4 border-warning">
|
<div class="card mb-4 border-warning">
|
||||||
<div class="card-header bg-warning text-dark">
|
<div class="card-header bg-warning text-dark">
|
||||||
<h5 class="mb-0">
|
<h5 class="mb-0"><i class="bi bi-bell"></i> Ожидают верификации: {{ pending_count }}</h5>
|
||||||
<i class="bi bi-bell"></i> Ожидают верификации: {{ pending_count }}
|
|
||||||
</h5>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-sm">
|
<table class="table table-sm">
|
||||||
<thead>
|
<thead><tr><th>Пользователь</th><th>Квартира</th><th>Дата заявки</th><th>Действия</th></tr></thead>
|
||||||
<tr>
|
|
||||||
<th>Пользователь</th>
|
|
||||||
<th>Квартира</th>
|
|
||||||
<th>Дата заявки</th>
|
|
||||||
<th>Действия</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for req in pending_requests %}
|
{% for req in pending_requests %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td><strong>{{ req.user_name or 'user' ~ req.user_id }}</strong><br><small class="text-muted">@{{ req.username or 'нет' }}</small></td>
|
||||||
<strong>{{ req.user_name or 'user' ~ req.user_id }}</strong><br>
|
|
||||||
<small class="text-muted">@{{ req.username or 'нет' }}</small>
|
|
||||||
</td>
|
|
||||||
<td><code>{{ req.apartment }}</code></td>
|
<td><code>{{ req.apartment }}</code></td>
|
||||||
<td>{{ req.created_at.strftime('%d.%m.%Y %H:%M') }}</td>
|
<td>{{ req.created_at.strftime('%d.%m.%Y %H:%M') }}</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-sm btn-success" onclick="quickVerify({{ req.user_id }}, '{{ req.apartment }}')" title="Верифицировать">
|
<button class="btn btn-sm btn-success" onclick="quickVerify({{ req.user_id }}, '{{ req.apartment }}')" title="Верифицировать"><i class="bi bi-check-lg"></i></button>
|
||||||
<i class="bi bi-check-lg"></i>
|
<button class="btn btn-sm btn-danger" onclick="quickReject({{ req.id }})" title="Отклонить"><i class="bi bi-x-lg"></i></button>
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-danger" onclick="quickReject({{ req.id }})" title="Отклонить">
|
|
||||||
<i class="bi bi-x-lg"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
@ -98,104 +45,47 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- Stats -->
|
<!-- Stats -->
|
||||||
{% if stats %}
|
{% if stats %}
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3"><a href="/users" style="text-decoration: none;"><div class="card bg-primary text-white"><div class="card-body text-center"><h3>{{ stats.total }}</h3><small>Всего</small></div></div></a></div>
|
||||||
<a href="/users" style="text-decoration: none;">
|
<div class="col-md-3"><a href="/users?filter=verified" style="text-decoration: none;"><div class="card bg-success text-white"><div class="card-body text-center"><h3>{{ stats.verified }}</h3><small>Верифицировано</small></div></div></a></div>
|
||||||
<div class="card bg-primary text-white">
|
<div class="col-md-3"><a href="/users?filter=ig" style="text-decoration: none;"><div class="card text-white" style="background: linear-gradient(135deg, #17a2b8, #117a8b);"><div class="card-body text-center"><h3>{{ stats.ig }}</h3><small>Инициативная группа</small></div></div></a></div>
|
||||||
<div class="card-body text-center">
|
<div class="col-md-3"><a href="/users?filter=active_unverified" style="text-decoration: none;"><div class="card bg-info text-white"><div class="card-body text-center"><h3>{{ stats.active_unverified }}</h3><small>Активных (50+ сообщ.)</small></div></div></a></div>
|
||||||
<h3>{{ stats.total }}</h3>
|
</div>
|
||||||
<small>Всего пользователей</small>
|
{% endif %}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<a href="/users?filter=verified" style="text-decoration: none;">
|
|
||||||
<div class="card bg-success text-white">
|
|
||||||
<div class="card-body text-center">
|
|
||||||
<h3>{{ stats.verified }}</h3>
|
|
||||||
<small>Верифицировано</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<a href="/users?filter=ig" style="text-decoration: none;">
|
|
||||||
<div class="card text-white" style="background: linear-gradient(135deg, #17a2b8, #117a8b);">
|
|
||||||
<div class="card-body text-center">
|
|
||||||
<h3>{{ stats.ig }}</h3>
|
|
||||||
<small>Инициативная группа</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<a href="/users?filter=active_unverified" style="text-decoration: none;">
|
|
||||||
<div class="card bg-info text-white">
|
|
||||||
<div class="card-body text-center">
|
|
||||||
<h3>{{ stats.active_unverified }}</h3>
|
|
||||||
<small>Активных (50+ сообщений)</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<!-- Auto Verify Button -->
|
<!-- Auto Verify -->
|
||||||
{% if stats and stats.active_unverified > 0 %}
|
{% if stats and stats.active_unverified > 0 %}
|
||||||
<div class="card mb-4 bg-light">
|
<div class="card mb-4 bg-light">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row align-items-center">
|
<div class="row align-items-center">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8"><h5 class="mb-0">⚡ Автоверификация активных</h5><small class="text-muted">Верифицировать {{ stats.active_unverified }} пользователей с 50+ сообщениями</small></div>
|
||||||
<h5 class="mb-0">⚡ Автоверификация активных</h5>
|
<div class="col-md-4 text-end"><button class="btn btn-success" onclick="autoVerify()"><i class="bi bi-lightning-charge"></i> Верифицировать всех</button></div>
|
||||||
<small class="text-muted">
|
|
||||||
Верифицировать {{ stats.active_unverified }} пользователей с 50+ сообщениями
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4 text-end">
|
|
||||||
<button class="btn btn-success" onclick="autoVerify()">
|
|
||||||
<i class="bi bi-lightning-charge"></i> Верифицировать всех
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% endif %}
|
||||||
{% endif %}
|
|
||||||
<div class="card mb-4">
|
<!-- Search -->
|
||||||
|
<div class="card mb-4">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form method="GET" action="/users" class="row g-3">
|
<form method="GET" action="/users" class="row g-3">
|
||||||
<div class="col-md-10">
|
<div class="col-md-10"><input type="text" class="form-control" name="search" placeholder="Поиск по имени, квартире, нику..." value="{{ search }}"></div>
|
||||||
<input type="text" class="form-control" name="search"
|
<div class="col-md-2"><button type="submit" class="btn btn-primary w-100"><i class="bi bi-search"></i> Поиск</button></div>
|
||||||
placeholder="Поиск по имени, квартире, нику..."
|
|
||||||
value="{{ search }}">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-2">
|
|
||||||
<button type="submit" class="btn btn-primary w-100">
|
|
||||||
<i class="bi bi-search"></i> Поиск
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Users Table -->
|
<!-- Users Table -->
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header bg-light">
|
<div class="card-header bg-light">
|
||||||
<div class="row align-items-center">
|
<div class="row align-items-center">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8"><h5 class="mb-0">📋 Пользователи</h5></div>
|
||||||
<h5 class="mb-0">📋 Пользователи</h5>
|
<div class="col-md-4 text-end"><button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#legendCollapse">ℹ️ Легенда</button></div>
|
||||||
</div>
|
|
||||||
<div class="col-md-4 text-end">
|
|
||||||
<button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#legendCollapse">
|
|
||||||
ℹ️ Легенда
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse" id="legendCollapse">
|
<div class="collapse" id="legendCollapse">
|
||||||
|
|
@ -208,7 +98,6 @@
|
||||||
<li><span class="btn btn-sm btn-success"><i class="bi bi-shield-check"></i></span> — Верифицировать</li>
|
<li><span class="btn btn-sm btn-success"><i class="bi bi-shield-check"></i></span> — Верифицировать</li>
|
||||||
<li><span class="btn btn-sm btn-warning"><i class="bi bi-shield-x"></i></span> — Отозвать верификацию</li>
|
<li><span class="btn btn-sm btn-warning"><i class="bi bi-shield-x"></i></span> — Отозвать верификацию</li>
|
||||||
<li><span class="btn btn-sm btn-danger"><i class="bi bi-lock"></i></span> — Забанить</li>
|
<li><span class="btn btn-sm btn-danger"><i class="bi bi-lock"></i></span> — Забанить</li>
|
||||||
<li><span class="btn btn-sm btn-success"><i class="bi bi-unlock"></i></span> — Разбанить</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
|
|
@ -225,19 +114,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
<thead>
|
<thead><tr><th>ID</th><th>Имя</th><th>Ник</th><th>Квартира</th><th>Телефон</th><th>Вериф.</th><th>Бан</th><th>Рейтинг</th><th>Действия</th></tr></thead>
|
||||||
<tr>
|
|
||||||
<th>ID</th>
|
|
||||||
<th>Имя</th>
|
|
||||||
<th>Ник</th>
|
|
||||||
<th>Квартира</th>
|
|
||||||
<th>Телефон</th>
|
|
||||||
<th>Верифицирован</th>
|
|
||||||
<th>Бан</th>
|
|
||||||
<th>Рейтинг</th>
|
|
||||||
<th>Действия</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for user in users %}
|
{% for user in users %}
|
||||||
{% set is_in_ig = user.user_id in ig_user_ids %}
|
{% set is_in_ig = user.user_id in ig_user_ids %}
|
||||||
|
|
@ -245,153 +122,80 @@
|
||||||
<td>{{ user.user_id }}</td>
|
<td>{{ user.user_id }}</td>
|
||||||
<td>{{ user.full_name or '—' }}</td>
|
<td>{{ user.full_name or '—' }}</td>
|
||||||
<td>@{{ user.username or 'нет' }}</td>
|
<td>@{{ user.username or 'нет' }}</td>
|
||||||
<td>
|
<td><span id="apt-{{ user.user_id }}">{{ user.apartment or '—' }}</span></td>
|
||||||
<span id="apt-{{ user.user_id }}">{{ user.apartment or '—' }}</span>
|
<td><span id="phone-{{ user.user_id }}">{{ user.phone or '—' }}</span></td>
|
||||||
</td>
|
<td>{% if user.verified %}<span class="badge bg-success">✅</span>{% else %}<span class="badge bg-danger">❌</span>{% endif %}</td>
|
||||||
<td>
|
<td>{% if user.banned %}<span class="badge bg-danger">🚫</span>{% else %}<span class="badge bg-success">✅</span>{% endif %}</td>
|
||||||
<span id="phone-{{ user.user_id }}">{{ user.phone or '—' }}</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if user.verified %}
|
|
||||||
<span class="badge bg-success">✅ Да</span>
|
|
||||||
{% else %}
|
|
||||||
<span class="badge bg-danger">❌ Нет</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if user.banned %}
|
|
||||||
<span class="badge bg-danger">🚫 Да</span>
|
|
||||||
{% else %}
|
|
||||||
<span class="badge bg-success">✅ Нет</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>{{ user.rating }}</td>
|
<td>{{ user.rating }}</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-sm btn-primary" onclick="editUser({{ user.user_id }})">
|
<button class="btn btn-sm btn-primary" onclick="editUser({{ user.user_id }})"><i class="bi bi-pencil"></i></button>
|
||||||
<i class="bi bi-pencil"></i>
|
|
||||||
</button>
|
|
||||||
{% if user.banned %}
|
{% if user.banned %}
|
||||||
<button class="btn btn-sm btn-success" onclick="unbanUser({{ user.user_id }})">
|
<button class="btn btn-sm btn-success" onclick="unbanUser({{ user.user_id }})"><i class="bi bi-unlock"></i></button>
|
||||||
<i class="bi bi-unlock"></i>
|
|
||||||
</button>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<button class="btn btn-sm btn-danger" onclick="banUser({{ user.user_id }})">
|
<button class="btn btn-sm btn-danger" onclick="banUser({{ user.user_id }})"><i class="bi bi-lock"></i></button>
|
||||||
<i class="bi bi-lock"></i>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user.verified %}
|
{% if user.verified %}
|
||||||
<button class="btn btn-sm btn-warning" onclick="unverifyUser({{ user.user_id }})" title="Отозвать верификацию">
|
<button class="btn btn-sm btn-warning" onclick="unverifyUser({{ user.user_id }})"><i class="bi bi-shield-x"></i></button>
|
||||||
<i class="bi bi-shield-x"></i>
|
|
||||||
</button>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<button class="btn btn-sm btn-success" onclick="verifyUser({{ user.user_id }})" title="Верифицировать">
|
<button class="btn btn-sm btn-success" onclick="verifyUser({{ user.user_id }})"><i class="bi bi-shield-check"></i></button>
|
||||||
<i class="bi bi-shield-check"></i>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user.is_admin %}
|
{% if user.is_admin %}
|
||||||
<button class="btn btn-sm btn-outline-dark" onclick="removeAdmin({{ user.user_id }})" title="Снять админа">
|
<button class="btn btn-sm btn-outline-dark" onclick="removeAdmin({{ user.user_id }})"><i class="bi bi-person-x"></i></button>
|
||||||
<i class="bi bi-person-x"></i>
|
|
||||||
</button>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<button class="btn btn-sm btn-dark" onclick="makeAdmin({{ user.user_id }})" title="Назначить админом">
|
<button class="btn btn-sm btn-dark" onclick="makeAdmin({{ user.user_id }})"><i class="bi bi-person-check"></i></button>
|
||||||
<i class="bi bi-person-check"></i>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if is_in_ig %}
|
{% if is_in_ig %}
|
||||||
<button class="btn btn-sm btn-outline-info" onclick="removeFromIg({{ user.user_id }})" title="Удалить из ИГ">
|
<button class="btn btn-sm btn-outline-info" onclick="removeFromIg({{ user.user_id }})"><i class="bi bi-person-dash"></i></button>
|
||||||
<i class="bi bi-person-dash"></i>
|
|
||||||
</button>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<button class="btn btn-sm btn-info" onclick="addToIg({{ user.user_id }})" title="Добавить в ИГ">
|
<button class="btn btn-sm btn-info" onclick="addToIg({{ user.user_id }})"><i class="bi bi-person-plus"></i></button>
|
||||||
<i class="bi bi-person-plus"></i>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
{% if not users %}<p class="text-center text-muted">Пользователи не найдены</p>{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if not users %}
|
<!-- Edit Modal -->
|
||||||
<p class="text-center text-muted">Пользователи не найдены</p>
|
<div class="modal fade" id="editUserModal" tabindex="-1">
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Modal для редактирования -->
|
|
||||||
<div class="modal fade" id="editUserModal" tabindex="-1">
|
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header"><h5 class="modal-title">Редактировать пользователя</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
|
||||||
<h5 class="modal-title">Редактировать пользователя</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<input type="hidden" id="editUserId">
|
<input type="hidden" id="editUserId">
|
||||||
<div class="mb-3">
|
<div class="mb-3"><label class="form-label">Квартира</label><input type="text" class="form-control" id="editApartment"></div>
|
||||||
<label class="form-label">Квартира</label>
|
<div class="mb-3"><label class="form-label">Телефон</label><input type="text" class="form-control" id="editPhone"></div>
|
||||||
<input type="text" class="form-control" id="editApartment" placeholder="45 или 12А">
|
<div class="mb-3"><label class="form-label">Рейтинг</label><input type="number" class="form-control" id="editRating" value="0"></div>
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Телефон</label>
|
|
||||||
<input type="text" class="form-control" id="editPhone" placeholder="+7 XXX XXX-XX-XX">
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Рейтинг</label>
|
|
||||||
<input type="number" class="form-control" id="editRating" value="0">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
|
||||||
<button type="button" class="btn btn-primary" onclick="saveUser()">Сохранить</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button><button type="button" class="btn btn-primary" onclick="saveUser()">Сохранить</button></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
{% block extra_script %}
|
||||||
<script>
|
<script>
|
||||||
// Быстрая верификация
|
async function quickVerify(userId, apartment) {
|
||||||
async function quickVerify(userId, apartment) {
|
|
||||||
if (!confirm(`Верифицировать пользователя (кв. ${apartment})?`)) return;
|
if (!confirm(`Верифицировать пользователя (кв. ${apartment})?`)) return;
|
||||||
|
|
||||||
try {
|
|
||||||
const r = await fetch(`/api/user/${userId}/verify`, { method: 'POST' });
|
const r = await fetch(`/api/user/${userId}/verify`, { method: 'POST' });
|
||||||
const result = await r.json();
|
const result = await r.json();
|
||||||
if (result.success) {
|
if (result.success) { alert('✅ Пользователь верифицирован!'); location.reload(); }
|
||||||
alert('✅ Пользователь верифицирован!');
|
}
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
alert('❌ Ошибка: ' + err.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Быстрое отклонение
|
async function quickReject(reqId) {
|
||||||
async function quickReject(reqId) {
|
|
||||||
if (!confirm('Отклонить заявку на верификацию?')) return;
|
if (!confirm('Отклонить заявку на верификацию?')) return;
|
||||||
|
|
||||||
try {
|
|
||||||
const r = await fetch(`/api/verification/${reqId}/reject`, { method: 'POST' });
|
const r = await fetch(`/api/verification/${reqId}/reject`, { method: 'POST' });
|
||||||
const result = await r.json();
|
const result = await r.json();
|
||||||
if (result.success) {
|
if (result.success) { alert('✅ Заявка отклонена'); location.reload(); }
|
||||||
alert('✅ Заявка отклонена');
|
}
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
alert('❌ Ошибка: ' + err.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const editModal = new bootstrap.Modal(document.getElementById('editUserModal'));
|
const editModal = new bootstrap.Modal(document.getElementById('editUserModal'));
|
||||||
let currentUserId = null;
|
let currentUserId = null;
|
||||||
|
|
||||||
function editUser(userId) {
|
function editUser(userId) {
|
||||||
currentUserId = userId;
|
currentUserId = userId;
|
||||||
// Загружаем данные пользователя
|
|
||||||
fetch(`/api/user/${userId}`)
|
fetch(`/api/user/${userId}`)
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
|
@ -402,139 +206,29 @@
|
||||||
document.getElementById('editRating').value = user.rating || 0;
|
document.getElementById('editRating').value = user.rating || 0;
|
||||||
editModal.show();
|
editModal.show();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveUser() {
|
async function saveUser() {
|
||||||
const userId = document.getElementById('editUserId').value;
|
const userId = document.getElementById('editUserId').value;
|
||||||
const data = {
|
const data = {
|
||||||
apartment: document.getElementById('editApartment').value,
|
apartment: document.getElementById('editApartment').value,
|
||||||
phone: document.getElementById('editPhone').value,
|
phone: document.getElementById('editPhone').value,
|
||||||
rating: parseInt(document.getElementById('editRating').value)
|
rating: parseInt(document.getElementById('editRating').value)
|
||||||
};
|
};
|
||||||
|
const response = await fetch(`/api/user/${userId}/update`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) });
|
||||||
const response = await fetch(`/api/user/${userId}/update`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify(data)
|
|
||||||
});
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
if (result.success) { alert('✅ Данные обновлены'); location.reload(); }
|
||||||
|
else { alert('❌ Ошибка: ' + result.error); }
|
||||||
|
}
|
||||||
|
|
||||||
if (result.success) {
|
async function banUser(userId) { if (!confirm('Забанить пользователя?')) return; const r = await fetch(`/api/user/${userId}/ban`, { method: 'POST' }); const result = await r.json(); if (result.success) { alert('Забанен'); location.reload(); } }
|
||||||
alert('✅ Данные обновлены');
|
async function unbanUser(userId) { if (!confirm('Разбанить?')) return; const r = await fetch(`/api/user/${userId}/unban`, { method: 'POST' }); const result = await r.json(); if (result.success) { alert('Разбанен'); location.reload(); } }
|
||||||
location.reload();
|
async function verifyUser(userId) { if (!confirm('Верифицировать?')) return; const r = await fetch(`/api/user/${userId}/verify`, { method: 'POST' }); const result = await r.json(); if (result.success) { alert('Верифицирован'); location.reload(); } }
|
||||||
} else {
|
async function unverifyUser(userId) { if (!confirm('Отозвать верификацию?')) return; const r = await fetch(`/api/user/${userId}/unverify`, { method: 'POST' }); const result = await r.json(); if (result.success) { alert('Верификация отозвана'); location.reload(); } }
|
||||||
alert('❌ Ошибка: ' + result.error);
|
async function makeAdmin(userId) { if (!confirm('Назначить админом?')) return; const r = await fetch(`/api/user/${userId}/make_admin`, { method: 'POST' }); const result = await r.json(); if (result.success) { alert('✅ Теперь админ!'); location.reload(); } }
|
||||||
}
|
async function removeAdmin(userId) { if (!confirm('Снять админа?')) return; const r = await fetch(`/api/user/${userId}/remove_admin`, { method: 'POST' }); const result = await r.json(); if (result.success) { alert('Админ снят'); location.reload(); } }
|
||||||
}
|
async function addToIg(userId) { if (!confirm('Добавить в ИГ?')) return; const r = await fetch(`/api/user/${userId}/add_to_ig`, { method: 'POST' }); const result = await r.json(); if (result.success) { alert('✅ Добавлен в ИГ'); location.reload(); } }
|
||||||
|
async function removeFromIg(userId) { if (!confirm('Удалить из ИГ?')) return; const r = await fetch(`/api/user/${userId}/remove_from_ig`, { method: 'POST' }); const result = await r.json(); if (result.success) { alert('Удален из ИГ'); location.reload(); } }
|
||||||
async function banUser(userId) {
|
async function autoVerify() { if (!confirm('Верифицировать всех активных с 50+ сообщениями?')) return; const r = await fetch('/api/users/auto_verify', { method: 'POST' }); const result = await r.json(); if (result.success) { alert('✅ ' + result.message); location.reload(); } }
|
||||||
if (!confirm('Забанить пользователя?')) return;
|
</script>
|
||||||
const r = await fetch(`/api/user/${userId}/ban`, { method: 'POST' });
|
{% endblock %}
|
||||||
const result = await r.json();
|
|
||||||
if (result.success) {
|
|
||||||
alert('Пользователь забанен');
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function unbanUser(userId) {
|
|
||||||
if (!confirm('Разбанить пользователя?')) return;
|
|
||||||
const r = await fetch(`/api/user/${userId}/unban`, { method: 'POST' });
|
|
||||||
const result = await r.json();
|
|
||||||
if (result.success) {
|
|
||||||
alert('Пользователь разбанен');
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function verifyUser(userId) {
|
|
||||||
if (!confirm('Верифицировать пользователя?')) return;
|
|
||||||
const r = await fetch(`/api/user/${userId}/verify`, { method: 'POST' });
|
|
||||||
const result = await r.json();
|
|
||||||
if (result.success) {
|
|
||||||
alert('Пользователь верифицирован');
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function unverifyUser(userId) {
|
|
||||||
if (!confirm('Отозвать верификацию?')) return;
|
|
||||||
const r = await fetch(`/api/user/${userId}/unverify`, { method: 'POST' });
|
|
||||||
const result = await r.json();
|
|
||||||
if (result.success) {
|
|
||||||
alert('Верификация отозвана');
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function makeAdmin(userId) {
|
|
||||||
if (!confirm('Назначить пользователем админом?')) return;
|
|
||||||
const r = await fetch(`/api/user/${userId}/make_admin`, { method: 'POST' });
|
|
||||||
const result = await r.json();
|
|
||||||
if (result.success) {
|
|
||||||
alert('✅ Пользователь теперь админ!');
|
|
||||||
location.reload();
|
|
||||||
} else {
|
|
||||||
alert('❌ Ошибка: ' + result.error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function removeAdmin(userId) {
|
|
||||||
if (!confirm('Снять админа?')) return;
|
|
||||||
const r = await fetch(`/api/user/${userId}/remove_admin`, { method: 'POST' });
|
|
||||||
const result = await r.json();
|
|
||||||
if (result.success) {
|
|
||||||
alert('✅ Админ снят');
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addToIg(userId) {
|
|
||||||
if (!confirm('Добавить в Инициативную Группу?')) return;
|
|
||||||
const r = await fetch(`/api/user/${userId}/add_to_ig`, { method: 'POST' });
|
|
||||||
const result = await r.json();
|
|
||||||
if (result.success) {
|
|
||||||
alert('✅ Добавлен в ИГ!');
|
|
||||||
location.reload();
|
|
||||||
} else {
|
|
||||||
alert('❌ Ошибка: ' + result.error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function removeFromIg(userId) {
|
|
||||||
if (!confirm('Удалить из Инициативной Группы?')) return;
|
|
||||||
const r = await fetch(`/api/user/${userId}/remove_from_ig`, { method: 'POST' });
|
|
||||||
const result = await r.json();
|
|
||||||
if (result.success) {
|
|
||||||
alert('✅ Удалён из ИГ');
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function autoVerify() {
|
|
||||||
if (!confirm('Верифицировать всех активных пользователей (50+ сообщений)?')) return;
|
|
||||||
|
|
||||||
const resultDiv = document.getElementById('autoVerifyResult');
|
|
||||||
if (resultDiv) {
|
|
||||||
resultDiv.innerHTML = '<div class="alert alert-info">Автоверификация...</div>';
|
|
||||||
} else {
|
|
||||||
alert('Автоверификация...');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const r = await fetch('/api/users/auto_verify', { method: 'POST' });
|
|
||||||
const result = await r.json();
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
alert('✅ ' + result.message);
|
|
||||||
location.reload();
|
|
||||||
} else {
|
|
||||||
alert('❌ Ошибка: ' + (result.error || 'Неизвестная'));
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
alert('❌ Ошибка: ' + err.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,20 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Верификация - Домовой Бот</title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
.sidebar {
|
|
||||||
min-height: 100vh;
|
|
||||||
background: linear-gradient(180deg, #2c3e50 0%, #1a252f 100%);
|
|
||||||
}
|
|
||||||
.sidebar .nav-link { color: rgba(255,255,255,0.8); padding: 1rem; }
|
|
||||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { color: white; background-color: rgba(255,255,255,0.1); }
|
|
||||||
.content { padding: 2rem; }
|
|
||||||
.status-pending { color: #f39c12; }
|
|
||||||
.status-approved { color: #27ae60; }
|
|
||||||
.status-rejected { color: #c0392b; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2 sidebar p-0">
|
|
||||||
<div class="p-3 text-white text-center">
|
|
||||||
<h4><i class="bi bi-house-door"></i> Домовой Бот</h4>
|
|
||||||
</div>
|
|
||||||
<nav class="nav flex-column">
|
|
||||||
<a class="nav-link" href="/"><i class="bi bi-speedometer2"></i> Дашборд</a>
|
|
||||||
<a class="nav-link" href="/users"><i class="bi bi-people"></i> Пользователи</a>
|
|
||||||
<a class="nav-link active" href="/verification"><i class="bi bi-shield-check"></i> Верификация</a>
|
|
||||||
<a class="nav-link" href="/phones"><i class="bi bi-telephone"></i> Телефоны</a>
|
|
||||||
<a class="nav-link" href="/export"><i class="bi bi-download"></i> Экспорт</a>
|
|
||||||
<a class="nav-link" href="/scheduled_posts"><i class="bi bi-calendar2-plus"></i> Запланированные посты</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-10 content">
|
{% block title %}Верификация - Домовой Бот{% endblock %}
|
||||||
<h2 class="mb-4"><i class="bi bi-shield-check"></i> Управление верификацией</h2>
|
|
||||||
|
|
||||||
<!-- Заявки на верификацию -->
|
{% block nav_verification %}active{% endblock %}
|
||||||
<div class="card mb-4">
|
|
||||||
|
{% block extra_style %}
|
||||||
|
.status-pending { color: #f39c12; }
|
||||||
|
.status-approved { color: #27ae60; }
|
||||||
|
.status-rejected { color: #c0392b; }
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2 class="mb-4"><i class="bi bi-shield-check"></i> Управление верификацией</h2>
|
||||||
|
|
||||||
|
<!-- Заявки на верификацию -->
|
||||||
|
<div class="card mb-4">
|
||||||
<div class="card-header bg-warning text-dark">
|
<div class="card-header bg-warning text-dark">
|
||||||
<h5 class="mb-0"><i class="bi bi-clock-history"></i> Заявки на проверку</h5>
|
<h5 class="mb-0"><i class="bi bi-clock-history"></i> Заявки на проверку</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -81,10 +55,10 @@
|
||||||
<p class="text-muted text-center">Нет заявок на верификацию</p>
|
<p class="text-muted text-center">Нет заявок на верификацию</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Непроверенные пользователи -->
|
<!-- Непроверенные пользователи -->
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header bg-secondary text-white">
|
<div class="card-header bg-secondary text-white">
|
||||||
<h5 class="mb-0"><i class="bi bi-person-x"></i> Непроверенные пользователи</h5>
|
<h5 class="mb-0"><i class="bi bi-person-x"></i> Непроверенные пользователи</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -123,13 +97,11 @@
|
||||||
<p class="text-muted text-center">Все пользователи верифицированы</p>
|
<p class="text-muted text-center">Все пользователи верифицированы</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% endblock %}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
{% block extra_script %}
|
||||||
<script>
|
<script>
|
||||||
async function approveVerification(requestId) {
|
async function approveVerification(requestId) {
|
||||||
if (!confirm('Одобрить верификацию?')) return;
|
if (!confirm('Одобрить верификацию?')) return;
|
||||||
|
|
||||||
|
|
@ -185,6 +157,5 @@
|
||||||
alert('❌ Ошибка: ' + result.error);
|
alert('❌ Ошибка: ' + result.error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue