Files
2026-04-25 01:40:01 +03:00

358 lines
9.6 KiB
HTML
Raw Permalink 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: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 14px;
min-height: 100vh;
}
nav { background: #1a1d27; padding: 12px 24px; display: flex; gap: 24px; align-items: center; border-bottom: 1px solid #2a2d3a; }
nav a { color: #8b8fa8; text-decoration: none; font-size: 14px; }
nav a:hover, nav a.active { color: #fff; }
nav .brand { color: #fff; font-weight: 600; margin-right: 16px; }
header {
display: flex;
align-items: center;
gap: 16px;
padding: 16px 24px 12px;
border-bottom: 1px solid #21262d;
}
header h1 { font-size: 18px; font-weight: 600; color: #e6edf3; }
#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; }
.delay-ok { color: #3fb950; }
.delay-critical { color: #f85149; font-weight: 700; }
/* category badges */
.cat-badge {
border-radius: 3px;
display: inline-block;
font-size: 10px;
font-weight: 700;
padding: 1px 5px;
margin-left: 4px;
}
.cat-pax { background: #0d4429; color: #3fb950; }
.cat-cargo { background: #3d2b00; color: #d29922; }
.cat-mil { background: #3d0c0c; color: #f85149; }
.cat-other { background: #21262d; color: #8b949e; }
/* source badge */
.src-badge { border-radius: 3px; font-size: 9px; font-weight: 700; padding: 1px 4px; margin-left: 3px; vertical-align: middle; }
.src-fr24 { background: #0d2d5e; color: #58a6ff; }
/* track link */
.track-link {
color: #58a6ff;
text-decoration: none;
font-size: 13px;
}
.track-link:hover { text-decoration: underline; }
/* actual time display */
.act-time { color: #c9d1d9; }
.act-landed { color: #8b949e; font-size: 11px; }
/* ── 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>
<nav>
<span class="brand">✈ FR24</span>
<a href="/">Карта</a>
<a href="/schedule" class="active">Расписание</a>
<a href="/monitoring">Мониторинг</a>
<a href="/data-sources">Источники</a>
</nav>
<header>
<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>
<th>ВС</th>
<th>Трек</th>
</tr>
</thead>
<tbody id="table-body">
<tr><td colspan="12" 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>