beta 2.0
This commit is contained in:
+40
-4
@@ -34,29 +34,62 @@
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path == '/' %}active{% endif %}" href="/">
|
||||
<i class="bi bi-speedometer2 me-1"></i> Главная
|
||||
<i class="bi bi-speedometer2 me-2"></i>Главная
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path.startswith('/medods') %}active{% endif %}" href="/medods">
|
||||
<i class="bi bi-diagram-3 me-1"></i> Medods
|
||||
<i class="bi bi-plug me-2"></i>Medods
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path.startswith('/vk') %}active{% endif %}" href="/vk">
|
||||
<i class="bi bi-chat-dots me-1"></i> VK
|
||||
<i class="bi bi-chat-dots me-2"></i>VK
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path.startswith('/posts') %}active{% endif %}" href="/posts">
|
||||
<i class="bi bi-file-earmark-text me-1"></i> Посты
|
||||
<i class="bi bi-file-earmark-text me-2"></i>Посты
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path.startswith('/birthdate') %}active{% endif %}"
|
||||
href="/birthdate">
|
||||
<i class="bi bi-balloon-heart-fill text-pink me-2"></i>Дни рождения
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<div class="dropstart">
|
||||
<button type="button" class="btn btn-danger dropdown-toggle" id="changePasswordBtn"
|
||||
data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">
|
||||
<i class="bi bi-shield-lock-fill me-1"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu p-4" style="width: 200px;">
|
||||
<button type="button" class="btn btn-danger" onclick="deleteAuthToken()">
|
||||
<i class="bi bi-box-arrow-right me-1"></i>Выйти
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<form id="changePasswordForm">
|
||||
<div class="mb-3">
|
||||
<label for="newPassword" class="form-label">Новый пароль</label>
|
||||
<input type="password" class="form-control" id="newPassword" placeholder="Новый пароль"
|
||||
required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-outline-danger"><i
|
||||
class="bi bi-key-fill me-1"></i>Изменить</button>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -64,8 +97,11 @@
|
||||
<main class="container-fluid pt-2">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
<!-- Контейнер для уведомлений -->
|
||||
<div id="alertContainer" class="alert-fixed"></div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/static/js/base.js"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Дни рождения{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
<link href="/static/css/birthdate.css" rel="stylesheet">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Заголовок -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="mb-1"><i class="bi bi-balloon-heart-fill text-pink me-2"></i>Дни рождения</h2>
|
||||
<p class="text-muted mb-0">Управление поздравлениями сотрудников</p>
|
||||
</div>
|
||||
<div class="badge bg-pink fs-6 px-3 py-2">
|
||||
<i class="bi bi-calendar-heart me-1"></i>Поздравления
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Основной контент -->
|
||||
<div class="row g-4 mb-3">
|
||||
<!-- Левая колонка: Список сотрудников -->
|
||||
<div class="col-lg-7">
|
||||
<div class="card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="bi bi-people-fill me-2"></i>Сотрудники</h5>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="refreshUsersList()">
|
||||
<i class="bi bi-arrow-clockwise me-1"></i>Обновить список
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<!-- Таблица сотрудников -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0" id="usersTable">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th width="120">Дата рождения</th>
|
||||
<th>Имя</th>
|
||||
<th>Короткое имя</th>
|
||||
<th>Полная дата<br><small>(возраст)</small></th>
|
||||
<th>Пол</th>
|
||||
<th>Специальности</th>
|
||||
<th width="100" class="text-center">Статус</th>
|
||||
<th width="80" class="text-center">Данные</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="usersTableBody">
|
||||
<!-- Данные будут загружены через JS -->
|
||||
<tr>
|
||||
<td colspan="8" class="text-center py-5">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Загрузка...</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-6">
|
||||
<button type="button" class="btn btn-sm btn-outline-success w-100" onclick="enableAllUsers()">
|
||||
<i class="bi bi-check-circle me-1"></i>Включить всех
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary w-100"
|
||||
onclick="disableAllUsers()">
|
||||
<i class="bi bi-x-circle me-1"></i>Отключить всех
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Правая колонка: Данные сотрудника и планировщик -->
|
||||
<div class="col-lg-5">
|
||||
<!-- Данные выбранного сотрудника -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-person-badge me-2"></i>
|
||||
<span id="selectedUserName">Выберите сотрудника</span>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="userForm">
|
||||
<input type="hidden" id="userId">
|
||||
|
||||
<!-- Переключатель enabled -->
|
||||
<div class="mb-4">
|
||||
<div class="form-check form-switch d-flex align-items-center gap-2 p-0">
|
||||
<input class="form-check-input m-0" type="checkbox" role="switch" id="user_enabled"
|
||||
onchange="markUserChanged()">
|
||||
<label class="form-check-label fw-semibold mb-0" for="user_enabled">
|
||||
Включить поздравление для этого сотрудника
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ссылка на фото -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-semibold">
|
||||
<i class="bi bi-image me-2"></i>Ссылка на фото для поздравления
|
||||
</label>
|
||||
<input type="url" class="form-control" id="photo_link"
|
||||
placeholder="https://vk.com/groupname?z=photo-27937673_457248154"
|
||||
oninput="markUserChanged()">
|
||||
<div class="form-text">
|
||||
Ссылка на изображение для поздравления. Должна быть доступна для всех. Лишние символы в
|
||||
ссылке будут удалены автоматически.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Текст поздравления -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-semibold">
|
||||
<i class="bi bi-chat-heart me-2"></i>Текст поздравления
|
||||
</label>
|
||||
<textarea class="form-control" id="congratulations" rows="5"
|
||||
placeholder="Дорогой(ая) [Имя], поздравляем с днем рождения! 🎉"
|
||||
oninput="markUserChanged()"></textarea>
|
||||
<div class="form-text">
|
||||
Не поддерживается никакая разметка. Только текст и эмоджи. Максимум 2000 символов.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Информация о посте -->
|
||||
<div id="postInfo" style="display: none;">
|
||||
<div class="alert alert-info">
|
||||
<h6 class="alert-heading"><i class="bi bi-info-circle me-2"></i>Информация о посте</h6>
|
||||
<div class="mb-2" id="postLinkContainer">
|
||||
<!-- Ссылка на пост будет здесь -->
|
||||
</div>
|
||||
<div class="small" id="publishTime">
|
||||
<!-- Время публикации будет здесь -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Кнопка сохранения -->
|
||||
<div class="d-flex gap-2 pt-3 border-top">
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="resetUserForm()">
|
||||
<i class="bi bi-x-lg me-1"></i>Отмена
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" id="saveUserButton" onclick="saveUserData()"
|
||||
disabled>
|
||||
<i class="bi bi-save me-1"></i>Сохранить данные сотрудника
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Настройки планировщика -->
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="bi bi-clock me-2"></i>Настройки планировщика</h5>
|
||||
<span id="schedulerStatus" class="badge">
|
||||
{% if data.schedulerStatus.scheduler %}
|
||||
<span class="badge bg-success"><i class="bi bi-play-circle me-1"></i>Активен</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary"><i class="bi bi-stop-circle me-1"></i>Неактивен</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="schedulerForm">
|
||||
<!-- Время запуска -->
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold">Час запуска</label>
|
||||
<input type="number" class="form-control" id="scheduler_hour" min="0" max="23"
|
||||
placeholder="9"
|
||||
value="{{ data.schedulerSettings.hour if data.schedulerSettings else 9 }}"
|
||||
oninput="markSchedulerChanged()">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold">Минута запуска</label>
|
||||
<input type="number" class="form-control" id="scheduler_minute" min="0" max="59"
|
||||
placeholder="0"
|
||||
value="{{ data.schedulerSettings.minute if data.schedulerSettings else 0 }}"
|
||||
oninput="markSchedulerChanged()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Включение планировщика -->
|
||||
<div class="mb-4">
|
||||
<div class="form-check form-switch d-flex align-items-center gap-2 p-0">
|
||||
<input class="form-check-input m-0" type="checkbox" role="switch" id="scheduler_enabled" {%
|
||||
if data.schedulerSettings and data.schedulerSettings.enabled %}checked{% endif %}
|
||||
onchange="markSchedulerChanged()">
|
||||
<label class="form-check-label fw-semibold mb-0" for="scheduler_enabled">
|
||||
Включить автоматическую публикацию
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-text">
|
||||
Планировщик будет проверять дни рождения и публиковать поздравления ежедневно в
|
||||
указанное время.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Информация о следующем запуске -->
|
||||
{% if data.schedulerStatus.next_run_time %}
|
||||
<div class="alert alert-success">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bi bi-calendar-event fs-5 me-3"></i>
|
||||
<div>
|
||||
<h6 class="alert-heading mb-1">Следующий запуск</h6>
|
||||
<p class="mb-0">{{ data.schedulerStatus.next_run_time }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Кнопка сохранения -->
|
||||
<div class="d-flex gap-2 pt-3 border-top">
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="resetSchedulerForm()">
|
||||
<i class="bi bi-x-lg me-1"></i>Сбросить
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning" id="saveSchedulerButton"
|
||||
onclick="saveSchedulerSettings()" disabled>
|
||||
<i class="bi bi-save me-1"></i>Сохранить настройки планировщика
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Модальное окно для подтверждения сохранения -->
|
||||
<div class="modal fade" id="confirmModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="bi bi-exclamation-triangle text-warning me-2"></i>Несохраненные
|
||||
изменения</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>У вас есть несохраненные изменения для сотрудника <span id="modalUserName"
|
||||
class="fw-semibold"></span>.</p>
|
||||
<p>Вы хотите сохранить изменения перед переходом к другому сотруднику?</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" onclick="cancelSwitchUser()">
|
||||
<i class="bi bi-x-circle me-1"></i>Отмена
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal"
|
||||
onclick="discardUserChanges()">
|
||||
<i class="bi bi-trash me-1"></i>Не сохранять
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="saveAndSwitchUser()">
|
||||
<i class="bi bi-save me-1"></i>Сохранить и перейти
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="/static/js/birthdate.js"></script>
|
||||
{% endblock %}
|
||||
@@ -421,8 +421,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Контейнер для уведомлений -->
|
||||
<div id="alertContainer" class="alert-fixed"></div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Авторизация</title>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
body {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="card login-card shadow">
|
||||
<div class="card-body">
|
||||
<h4 class="text-center mb-4">Вход</h4>
|
||||
|
||||
<!-- ВАЖНО: form -->
|
||||
<form id="loginForm">
|
||||
<div class="mb-3">
|
||||
<input type="password" class="form-control" id="password" placeholder="Введите пароль" autofocus>
|
||||
</div>
|
||||
|
||||
<!-- Кнопка может остаться, но теперь не обязательна -->
|
||||
<button type="submit" class="btn btn-success w-100">
|
||||
Войти
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div id="error" class="alert alert-danger mt-3 d-none"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById("loginForm").addEventListener("submit", function (e) {
|
||||
e.preventDefault(); // НЕ перезагружать страницу
|
||||
login();
|
||||
});
|
||||
|
||||
async function login() {
|
||||
const password = document.getElementById("password").value;
|
||||
const errorBox = document.getElementById("error");
|
||||
|
||||
errorBox.classList.add("d-none");
|
||||
|
||||
if (!password) {
|
||||
errorBox.textContent = "Введите пароль";
|
||||
errorBox.classList.remove("d-none");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch("/login", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({ password })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.status === "ok" && data.token) {
|
||||
setCookie("auth_token", data.token, 365);
|
||||
window.location.href = "/";
|
||||
} else {
|
||||
errorBox.textContent = data.errorMessage || "Неверный пароль";
|
||||
errorBox.classList.remove("d-none");
|
||||
}
|
||||
} catch (e) {
|
||||
errorBox.textContent = "Ошибка соединения";
|
||||
errorBox.classList.remove("d-none");
|
||||
}
|
||||
}
|
||||
|
||||
function setCookie(name, value, days) {
|
||||
const date = new Date();
|
||||
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
|
||||
document.cookie = `${name}=${value}; expires=${date.toUTCString()}; path=/`;
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -255,9 +255,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Всплывающие уведомления -->
|
||||
<div id="alertContainer" style="position: fixed; top: 80px; right: 20px; z-index: 1050;"></div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<!-- Заголовок -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="mb-1"><i class="bi bi-megaphone-fill text-primary me-2"></i>Управление постами</h2>
|
||||
<h2 class="mb-1"><i class="bi bi-file-earmark-text text-primary me-2"></i>Управление постами</h2>
|
||||
<p class="text-muted mb-0">Создание и планирование публикаций в VK</p>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-lg px-5" onclick="saveSettings()">
|
||||
@@ -215,8 +215,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Контейнер для уведомлений -->
|
||||
<div id="alertContainer" class="alert-fixed"></div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
|
||||
+1
-3
@@ -9,7 +9,7 @@
|
||||
<!-- Заголовок -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="mb-1"><i class="bi bi-megaphone vk-icon me-2"></i>VK Настройки</h2>
|
||||
<h2 class="mb-1"><i class="bi bi-chat-dots vk-icon me-2"></i>VK Настройки</h2>
|
||||
<p class="text-muted mb-0">Настройки для работы с VK API и сообществом</p>
|
||||
</div>
|
||||
<div class="badge bg-primary fs-6 px-3 py-2">
|
||||
@@ -155,8 +155,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Контейнер для уведомлений -->
|
||||
<div id="alertContainer" class="alert-fixed"></div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
|
||||
Reference in New Issue
Block a user