200 lines
11 KiB
HTML
200 lines
11 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Управление расписанием — Morozovka Portal</title>
|
||
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/themes/dark.css">
|
||
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/l10n/ru.js"></script>
|
||
|
||
<style>
|
||
body { background: #0d1117; color: #e6edf3; font-family: sans-serif; padding: 40px; }
|
||
.container { max-width: 1000px; margin: 0 auto; }
|
||
.header-actions { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #30363d; padding-bottom: 10px; margin-bottom: 20px; }
|
||
h2 { color: #58a6ff; margin: 0; }
|
||
h3 { color: #8b949e; margin-top: 30px; }
|
||
.card { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 20px; margin-bottom: 20px; }
|
||
|
||
.form-row { display: flex; gap: 15px; margin-bottom: 15px; align-items: flex-end; flex-wrap: wrap; }
|
||
.form-group { display: flex; flex-direction: column; flex: 1; min-width: 200px; }
|
||
.form-group label { margin-bottom: 5px; font-size: 0.85rem; color: #8b949e; }
|
||
input, select, textarea { padding: 8px; background: #0d1117; color: white; border: 1px solid #30363d; border-radius: 5px; font-family: inherit; }
|
||
textarea { resize: vertical; min-height: 60px; }
|
||
|
||
.quick-btns { display: flex; gap: 10px; margin-bottom: 15px; flex-wrap: wrap; }
|
||
.quick-btn { padding: 8px 15px; border-radius: 5px; border: none; cursor: pointer; font-weight: bold; font-size: 0.85rem; color: white; transition: opacity 0.2s; }
|
||
.quick-btn:hover { opacity: 0.8; }
|
||
.btn-green { background: #238636; }
|
||
.btn-yellow { background: #d29922; }
|
||
.btn-red { background: #da3633; }
|
||
.btn-grey { background: #30363d; }
|
||
|
||
button[type="submit"] { padding: 10px 15px; background: #238636; color: white; border: none; border-radius: 5px; cursor: pointer; font-weight: bold; height: 38px; }
|
||
button[type="submit"]:hover { background: #2ea043; }
|
||
button.danger { background: #da3633; padding: 6px 10px; font-size: 0.8rem; height: auto; }
|
||
button.danger:hover { background: #f85149; }
|
||
button.edit-btn { background: #1f6feb; padding: 6px 10px; font-size: 0.8rem; height: auto; text-decoration: none; color: white; border-radius: 5px; margin-right: 5px; }
|
||
button.edit-btn:hover { background: #388bfd; }
|
||
|
||
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
|
||
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #30363d; }
|
||
th { background: #21262d; color: #8b949e; font-size: 0.85rem; }
|
||
|
||
.back-link { display: inline-block; margin-bottom: 20px; color: #8b949e; text-decoration: none; }
|
||
.back-link:hover { color: #58a6ff; }
|
||
|
||
.action-buttons a { display: inline-block; padding: 8px 15px; border-radius: 5px; text-decoration: none; font-weight: bold; font-size: 0.85rem; margin-left: 10px; color: white; }
|
||
.btn-import { background: #a371f7; }
|
||
.btn-export { background: #21262d; border: 1px solid #30363d; }
|
||
.btn-export:hover { background: #30363d; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<a href="/" class="back-link">← На главную</a>
|
||
|
||
<div class="header-actions">
|
||
<h2>Управление расписанием</h2>
|
||
<div class="action-buttons">
|
||
<a href="/admin/schedule/import" class="btn-import">📥 Импорт (.ics)</a>
|
||
<a href="/schedule/export.ics" class="btn-export">📤 Экспорт (.ics)</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h3>Текущий статус</h3>
|
||
<div class="quick-btns">
|
||
<button type="button" class="quick-btn btn-green" onclick="setFastStatus('on_place', 0, 'На месте')">✅ Я на месте</button>
|
||
<button type="button" class="quick-btn btn-yellow" onclick="setFastStatus('delayed', 5, 'Задерживаюсь на 5 мин')">⏳ Опоздаю на 5 мин</button>
|
||
<button type="button" class="quick-btn btn-yellow" onclick="setFastStatus('delayed', 15, 'Задерживаюсь на 15 мин')">⏳ Опоздаю на 15 мин</button>
|
||
<button type="button" class="quick-btn btn-yellow" onclick="setFastStatus('delayed', 30, 'Задерживаюсь на 30 мин')">⏳ Опоздаю на 30 мин</button>
|
||
<button type="button" class="quick-btn btn-grey" onclick="setFastStatus('away', 0, 'Вне вуза')">🚶 Вне вуза</button>
|
||
</div>
|
||
|
||
<form action="/admin/schedule/status" method="POST" id="status-form">
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Статус (вручную)</label>
|
||
<select name="status" id="status-select">
|
||
<option value="free" {% if status.status == 'free' %}selected{% endif %}>Свободен / Вне расписания</option>
|
||
<option value="on_place" {% if status.status == 'on_place' %}selected{% endif %}>На месте</option>
|
||
<option value="delayed" {% if status.status == 'delayed' %}selected{% endif %}>Опаздываю</option>
|
||
<option value="away" {% if status.status == 'away' %}selected{% endif %}>Вне вуза</option>
|
||
<option value="cancelled" {% if status.status == 'cancelled' %}selected{% endif %}>Отменено</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group" style="flex: 0.5;">
|
||
<label>Опоздание (мин)</label>
|
||
<input type="number" name="delay_minutes" id="delay-input" value="{{ status.delay_minutes or 0 }}" min="0">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Сообщение для студентов</label>
|
||
<input type="text" name="message" id="message-input" value="{{ status.message or '' }}" placeholder="Например: задерживаюсь на 15 минут">
|
||
</div>
|
||
<button type="submit">Обновить</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h3>Добавить новое событие</h3>
|
||
<form action="/admin/schedule/event/add" method="POST">
|
||
<div class="form-row">
|
||
<div class="form-group" style="flex: 2;">
|
||
<label>Название</label>
|
||
<input type="text" name="title" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Тип</label>
|
||
<select name="event_type">
|
||
<option value="lecture">Лекция</option>
|
||
<option value="workshop">Мастер-класс</option>
|
||
<option value="meeting">Встреча</option>
|
||
<option value="personal">Личное</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Место</label>
|
||
<input type="text" name="location">
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Начало</label>
|
||
<input type="text" class="flatpickr" name="start_time" required placeholder="Выберите дату и время">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Конец</label>
|
||
<input type="text" class="flatpickr" name="end_time" required placeholder="Выберите дату и время">
|
||
</div>
|
||
<div class="form-group" style="flex: 0.5; flex-direction: row; align-items: center; gap: 10px;">
|
||
<input type="checkbox" name="is_public" checked id="is_public">
|
||
<label for="is_public" style="margin: 0; cursor: pointer;">Публичное</label>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Описание</label>
|
||
<textarea name="description"></textarea>
|
||
</div>
|
||
<button type="submit" style="height: 60px;">Добавить</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h3>Последние 50 событий</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Дата и время</th>
|
||
<th>Название</th>
|
||
<th>Тип</th>
|
||
<th>Публичное</th>
|
||
<th>Действие</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for event in events %}
|
||
<tr>
|
||
<td>{{ event.start_time|msk_strftime('%d.%m.%Y %H:%M') }}<br><small style="color:#8b949e">до {{ event.end_time|msk_strftime('%H:%M') }}</small></td>
|
||
<td><strong>{{ event.title }}</strong><br><small style="color:#8b949e">{{ event.location or '' }}</small></td>
|
||
<td>{{ event.event_type }}</td>
|
||
<td>{{ 'Да' if event.is_public else 'Нет' }}</td>
|
||
<td style="display:flex; gap: 5px;">
|
||
<a href="/admin/schedule/event/{{ event.id }}/edit" class="edit-btn">✎ Ред.</a>
|
||
<form action="/admin/schedule/event/{{ event.id }}/delete" method="POST" onsubmit="return confirm('Удалить событие?');">
|
||
<button type="submit" class="danger">✕ Удалить</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
{% else %}
|
||
<tr><td colspan="5" style="text-align: center; color: #8b949e;">Нет событий</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
flatpickr(".flatpickr", {
|
||
enableTime: true,
|
||
dateFormat: "Y-m-d H:i",
|
||
time_24hr: true,
|
||
locale: "ru",
|
||
minuteIncrement: 15,
|
||
defaultDate: "today"
|
||
});
|
||
|
||
function setFastStatus(status, minutes, message) {
|
||
document.getElementById('status-select').value = status;
|
||
document.getElementById('delay-input').value = minutes;
|
||
document.getElementById('message-input').value = message;
|
||
document.getElementById('status-form').submit();
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|