Files
wiki/tasks/flightradar24/frontend/static/schedule.html
2026-04-20 14:20:01 +03:00

321 lines
8.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FR24 — Табло аэропортов</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: #0d1117;
color: #c9d1d9;
font-family: 'Segoe UI', system-ui, sans-serif;
font-size: 14px;
min-height: 100vh;
}
header {
display: flex;
align-items: center;
gap: 16px;
padding: 20px 24px 16px;
border-bottom: 1px solid #21262d;
}
header h1 { font-size: 18px; font-weight: 600; color: #e6edf3; }
header a {
color: #58a6ff;
text-decoration: none;
font-size: 13px;
opacity: 0.8;
}
header a:hover { opacity: 1; }
#last-updated { margin-left: auto; font-size: 12px; color: #6e7681; }
/* ── filters ── */
.filters {
background: #161b22;
border-bottom: 1px solid #21262d;
padding: 14px 24px;
}
.filters-grid {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: flex-end;
}
.filter-group {
display: flex;
flex-direction: column;
gap: 4px;
}
.filter-group label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #6e7681;
}
.filter-group input,
.filter-group select {
background: #0d1117;
border: 1px solid #30363d;
border-radius: 6px;
color: #c9d1d9;
font-size: 13px;
padding: 6px 10px;
height: 32px;
outline: none;
transition: border-color 0.15s;
}
.filter-group input:focus,
.filter-group select:focus { border-color: #58a6ff; }
.filter-group input[type="date"] { width: 140px; }
.filter-group input[type="time"] { width: 100px; }
.filter-group input[type="text"] { width: 130px; }
.filter-group select { width: 130px; }
.filter-actions {
display: flex;
gap: 8px;
align-items: flex-end;
margin-left: auto;
}
button {
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
height: 32px;
padding: 0 14px;
transition: opacity 0.15s;
}
button:hover { opacity: 0.85; }
.btn-primary { background: #238636; color: #fff; }
.btn-secondary { background: #21262d; color: #c9d1d9; border: 1px solid #30363d; }
.btn-export { background: #1f6feb; color: #fff; }
/* ── table area ── */
.table-wrap {
padding: 16px 24px;
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
thead th {
background: #161b22;
border-bottom: 1px solid #30363d;
color: #8b949e;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.06em;
padding: 10px 12px;
text-align: left;
text-transform: uppercase;
white-space: nowrap;
}
tbody tr {
border-bottom: 1px solid #21262d;
transition: background 0.1s;
}
tbody tr:hover { background: #161b22; }
tbody td {
padding: 9px 12px;
vertical-align: middle;
white-space: nowrap;
}
.dir-icon { font-size: 16px; }
.dir-dep { color: #58a6ff; }
.dir-arr { color: #3fb950; }
/* status badges */
.badge {
border-radius: 4px;
display: inline-block;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
padding: 2px 8px;
text-transform: uppercase;
}
.badge-scheduled { background: #21262d; color: #8b949e; }
.badge-departed { background: #0d4429; color: #3fb950; }
.badge-arrived { background: #0d4429; color: #3fb950; }
.badge-delayed { background: #3d2b00; color: #d29922; }
.badge-cancelled { background: #3d0c0c; color: #f85149; }
.delay-pos { color: #d29922; }
.delay-neg { color: #3fb950; }
/* ── pagination ── */
.pagination {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 24px 20px;
font-size: 13px;
color: #8b949e;
}
.pagination button { height: 28px; padding: 0 12px; font-size: 12px; }
#page-info { min-width: 120px; text-align: center; }
/* ── empty / loading ── */
.state-msg {
padding: 48px;
text-align: center;
color: #6e7681;
font-size: 14px;
}
/* ── mobile cards ── */
.cards { display: none; padding: 12px 16px; }
.card {
background: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
margin-bottom: 10px;
padding: 14px 16px;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.card-flight { font-size: 16px; font-weight: 700; color: #e6edf3; }
.card-airline { font-size: 12px; color: #8b949e; margin-top: 2px; }
.card-row {
display: flex;
justify-content: space-between;
font-size: 12px;
color: #8b949e;
margin-top: 6px;
}
.card-row span:last-child { color: #c9d1d9; }
@media (max-width: 767px) {
.table-wrap { display: none; }
.cards { display: block; }
.filters-grid { flex-direction: column; }
.filter-actions { margin-left: 0; }
.filter-group input,
.filter-group select { width: 100%; }
header { padding: 14px 16px; }
.filters { padding: 12px 16px; }
}
</style>
</head>
<body>
<header>
<a href="/">← Главная</a>
<h1>✈ Табло аэропортов Москвы</h1>
<span id="last-updated"></span>
</header>
<div class="filters">
<div class="filters-grid">
<div class="filter-group">
<label>Дата от</label>
<input type="date" id="f-date-from">
</div>
<div class="filter-group">
<label>Дата до</label>
<input type="date" id="f-date-to">
</div>
<div class="filter-group">
<label>Аэропорт</label>
<select id="f-airport">
<option value="all">Все</option>
<option value="SVO">SVO — Шереметьево</option>
<option value="DME">DME — Домодедово</option>
<option value="VKO">VKO — Внуково</option>
<option value="ZIA">ZIA — Жуковский</option>
</select>
</div>
<div class="filter-group">
<label>Направление</label>
<select id="f-direction">
<option value="all">Все</option>
<option value="departure">↑ Вылет</option>
<option value="arrival">↓ Прилёт</option>
</select>
</div>
<div class="filter-group">
<label>Рейс</label>
<input type="text" id="f-flight" placeholder="SU1234">
</div>
<div class="filter-group">
<label>Время от</label>
<input type="time" id="f-time-from">
</div>
<div class="filter-group">
<label>Время до</label>
<input type="time" id="f-time-to">
</div>
<div class="filter-actions">
<button class="btn-primary" onclick="applyFilters()">Применить</button>
<button class="btn-secondary" onclick="resetFilters()">Сброс</button>
<button class="btn-export" onclick="exportCsv()">Экспорт CSV</button>
</div>
</div>
</div>
<div class="table-wrap">
<table id="schedule-table">
<thead>
<tr>
<th>Дата</th>
<th>Рейс</th>
<th>Авиакомпания</th>
<th>Аэропорт</th>
<th>Напр.</th>
<th>Маршрут</th>
<th>Запланировано</th>
<th>Фактически</th>
<th>Задержка</th>
<th>Статус</th>
</tr>
</thead>
<tbody id="table-body">
<tr><td colspan="10" class="state-msg">Загрузка…</td></tr>
</tbody>
</table>
</div>
<div class="cards" id="cards-container"></div>
<div class="pagination">
<button class="btn-secondary" id="btn-prev" onclick="prevPage()">← Назад</button>
<span id="page-info"></span>
<button class="btn-secondary" id="btn-next" onclick="nextPage()">Вперёд →</button>
<span id="total-info" style="margin-left:auto;"></span>
</div>
<script src="/static/schedule.js"></script>
</body>
</html>