|
|
<!doctype html>
|
|
|
<html lang="ru">
|
|
|
<head>
|
|
|
<meta charset="UTF-8" />
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
|
<title>Радиотрекинг</title>
|
|
|
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg" />
|
|
|
<link rel="stylesheet" href="/static/styles.css?v=20260319r2" />
|
|
|
</head>
|
|
|
<body>
|
|
|
<div class="bg-glow bg-glow-a"></div>
|
|
|
<div class="bg-glow bg-glow-b"></div>
|
|
|
|
|
|
<div id="auth-overlay" class="auth-overlay auth-overlay-hidden">
|
|
|
<div class="auth-dialog card">
|
|
|
<h2>Вход в систему</h2>
|
|
|
<p class="muted">Авторизация выполняется через Keycloak.</p>
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Логин</span>
|
|
|
<input id="login-username" type="text" autocomplete="username" />
|
|
|
</label>
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Пароль</span>
|
|
|
<input id="login-password" type="password" autocomplete="current-password" />
|
|
|
</label>
|
|
|
<div class="editor-actions">
|
|
|
<button id="login-submit" class="btn btn-primary" type="button">Войти</button>
|
|
|
<span id="login-state" class="badge">авторизация: ожидание</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<main id="app-shell" class="app-shell">
|
|
|
<aside id="side-nav" class="side-nav card">
|
|
|
<div class="nav-head">
|
|
|
<button id="menu-toggle" class="btn menu-toggle" type="button" aria-controls="menu-list" aria-expanded="true">Свернуть меню</button>
|
|
|
<div class="auth-summary">
|
|
|
<span id="auth-user-chip" class="badge badge-meta">пользователь: гость</span>
|
|
|
<span id="auth-role-chip" class="badge badge-meta">роль: -</span>
|
|
|
<button id="logout-button" class="btn btn-compact" type="button">Выйти</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="menu-wrap">
|
|
|
<div id="menu-list" class="menu-list">
|
|
|
<section class="menu-group" data-menu-group="monitoring">
|
|
|
<button
|
|
|
class="menu-group-toggle"
|
|
|
type="button"
|
|
|
data-menu-group-toggle="monitoring"
|
|
|
aria-controls="menu-group-monitoring"
|
|
|
aria-expanded="true"
|
|
|
>
|
|
|
<span class="menu-group-title">Система</span>
|
|
|
<span id="menu-badge-monitoring" class="menu-group-badge">н/д</span>
|
|
|
</button>
|
|
|
<div id="menu-group-monitoring" class="menu-group-body">
|
|
|
<button class="menu-item menu-item-active" data-section="overview" type="button">
|
|
|
<span class="menu-item-icon" aria-hidden="true">O</span>
|
|
|
<span class="menu-item-text">Обзор</span>
|
|
|
<span class="menu-item-note">Статус</span>
|
|
|
</button>
|
|
|
<button class="menu-item" data-section="frequencies" type="button">
|
|
|
<span class="menu-item-icon" aria-hidden="true">F</span>
|
|
|
<span class="menu-item-text">Частоты</span>
|
|
|
<span class="menu-item-note">Таблица</span>
|
|
|
</button>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<section class="menu-group" data-menu-group="io">
|
|
|
<button
|
|
|
class="menu-group-toggle"
|
|
|
type="button"
|
|
|
data-menu-group-toggle="io"
|
|
|
aria-controls="menu-group-io"
|
|
|
aria-expanded="true"
|
|
|
>
|
|
|
<span class="menu-group-title">Сигнал</span>
|
|
|
<span id="menu-badge-io" class="menu-group-badge">н/д</span>
|
|
|
</button>
|
|
|
<div id="menu-group-io" class="menu-group-body">
|
|
|
<button class="menu-item" data-section="io" type="button">
|
|
|
<span class="menu-item-icon" aria-hidden="true">I</span>
|
|
|
<span class="menu-item-text">Сигнал</span>
|
|
|
<span class="menu-item-note">Обмен</span>
|
|
|
</button>
|
|
|
<button class="menu-item" data-section="history" type="button">
|
|
|
<span class="menu-item-icon" aria-hidden="true">H</span>
|
|
|
<span class="menu-item-text">История</span>
|
|
|
<span class="menu-item-note">Журнал</span>
|
|
|
</button>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<section class="menu-group" data-menu-group="config">
|
|
|
<button
|
|
|
class="menu-group-toggle"
|
|
|
type="button"
|
|
|
data-menu-group-toggle="config"
|
|
|
aria-controls="menu-group-config"
|
|
|
aria-expanded="true"
|
|
|
>
|
|
|
<span class="menu-group-title">Настройки</span>
|
|
|
<span id="menu-badge-config" class="menu-group-badge">н/д</span>
|
|
|
</button>
|
|
|
<div id="menu-group-config" class="menu-group-body">
|
|
|
<button class="menu-item" data-section="servers" type="button">
|
|
|
<span class="menu-item-icon" aria-hidden="true">S</span>
|
|
|
<span class="menu-item-text">Серверы</span>
|
|
|
<span class="menu-item-note">Узлы</span>
|
|
|
</button>
|
|
|
<button class="menu-item" data-section="json" type="button">
|
|
|
<span class="menu-item-icon" aria-hidden="true">{}</span>
|
|
|
<span class="menu-item-text">Конфиг</span>
|
|
|
<span class="menu-item-note">Файл</span>
|
|
|
</button>
|
|
|
</div>
|
|
|
</section>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="side-meta">
|
|
|
<div class="datetime-panel-controls">
|
|
|
<button
|
|
|
id="datetime-toggle"
|
|
|
class="btn btn-compact"
|
|
|
type="button"
|
|
|
aria-controls="meta-panel"
|
|
|
aria-expanded="true"
|
|
|
>
|
|
|
Скрыть панель
|
|
|
</button>
|
|
|
<button id="density-toggle" class="btn btn-compact" type="button">Вид: детальный</button>
|
|
|
</div>
|
|
|
<div id="meta-panel" class="meta-panel">
|
|
|
<div id="date-time-panel" class="date-time-panel">
|
|
|
<span id="updated-date" class="meta-pill">дата: н/д</span>
|
|
|
<span id="updated-time" class="meta-pill">время: н/д</span>
|
|
|
</div>
|
|
|
<div class="status-panel">
|
|
|
<span id="health-status" class="meta-pill">сервис: н/д</span>
|
|
|
<span id="delivery-status" class="meta-pill">доставка: н/д</span>
|
|
|
</div>
|
|
|
<label class="timezone-picker">
|
|
|
<span>часовой пояс</span>
|
|
|
<select id="timezone-select"></select>
|
|
|
</label>
|
|
|
</div>
|
|
|
</div>
|
|
|
</aside>
|
|
|
|
|
|
<section class="content-area">
|
|
|
<section id="section-overview" class="panel panel-active">
|
|
|
<header class="hero card overview-hero">
|
|
|
<h2>Радиотрекинг</h2>
|
|
|
<p class="muted">Координаты по RSSI и частотам.</p>
|
|
|
<div class="hero-actions">
|
|
|
<button id="refresh-now" class="btn btn-primary">Обновить</button>
|
|
|
<button id="toggle-auto-refresh" class="btn" type="button">Пауза автообновления</button>
|
|
|
<label class="refresh-interval-control" for="auto-refresh-seconds">
|
|
|
<span>Интервал, с</span>
|
|
|
<input id="auto-refresh-seconds" type="number" min="1" max="120" step="1" value="2" />
|
|
|
</label>
|
|
|
<span id="refresh-state" class="badge badge-meta">автообновление: вкл (2с)</span>
|
|
|
</div>
|
|
|
</header>
|
|
|
|
|
|
<div class="overview-layout">
|
|
|
<article class="card overview-position-card">
|
|
|
<h2>Координаты</h2>
|
|
|
<div class="result-box">
|
|
|
<div><span class="muted">Частота:</span> <b id="selected-freq">-</b></div>
|
|
|
<div><span class="muted">X:</span> <b id="pos-x">-</b></div>
|
|
|
<div><span class="muted">Y:</span> <b id="pos-y">-</b></div>
|
|
|
<div><span class="muted">Z:</span> <b id="pos-z">-</b></div>
|
|
|
<div><span class="muted">Ошибка:</span> <b id="rmse">-</b></div>
|
|
|
</div>
|
|
|
</article>
|
|
|
|
|
|
<article class="card monitor-board overview-monitor-card">
|
|
|
<h2>Статус</h2>
|
|
|
<div class="monitor-headline">
|
|
|
<span class="io-chip io-chip-neutral">Сервис: <b id="ov-health-chip">н/д</b></span>
|
|
|
<span class="io-chip io-chip-neutral">Доставка: <b id="ov-delivery-chip">н/д</b></span>
|
|
|
<span class="io-chip io-chip-neutral">Обновлено: <b id="ov-updated-at">н/д</b></span>
|
|
|
</div>
|
|
|
|
|
|
<div class="monitor-grid">
|
|
|
<section class="monitor-panel monitor-kpi-panel">
|
|
|
<div class="overview-metrics">
|
|
|
<div class="metric-tile">
|
|
|
<span class="metric-title">Входы онлайн</span>
|
|
|
<b id="ov-input-online" class="metric-value">0/0</b>
|
|
|
</div>
|
|
|
<div class="metric-tile">
|
|
|
<span class="metric-title">Выходы онлайн</span>
|
|
|
<b id="ov-output-online" class="metric-value">0/0</b>
|
|
|
</div>
|
|
|
<div class="metric-tile">
|
|
|
<span class="metric-title">События</span>
|
|
|
<b id="ov-history-total" class="metric-value">0</b>
|
|
|
</div>
|
|
|
<div class="metric-tile">
|
|
|
<span class="metric-title">Успех отправки</span>
|
|
|
<b id="ov-success-rate" class="metric-value">0%</b>
|
|
|
</div>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<section class="monitor-panel monitor-flow-panel">
|
|
|
<h3>Потоки</h3>
|
|
|
<div class="monitor-flow-row">
|
|
|
<span>Входные потоки</span>
|
|
|
<b id="ov-input-online-bar-text">0/0</b>
|
|
|
</div>
|
|
|
<div class="monitor-progress">
|
|
|
<span id="ov-input-online-bar" style="width:0%"></span>
|
|
|
</div>
|
|
|
<div class="monitor-flow-row">
|
|
|
<span>Выходные потоки</span>
|
|
|
<b id="ov-output-online-bar-text">0/0</b>
|
|
|
</div>
|
|
|
<div class="monitor-progress">
|
|
|
<span id="ov-output-online-bar" style="width:0%"></span>
|
|
|
</div>
|
|
|
<div class="monitor-flow-row">
|
|
|
<span>Успех отправки</span>
|
|
|
<b id="ov-delivery-bar-text">0%</b>
|
|
|
</div>
|
|
|
<div class="monitor-progress monitor-progress-accent">
|
|
|
<span id="ov-delivery-bar" style="width:0%"></span>
|
|
|
</div>
|
|
|
</section>
|
|
|
</div>
|
|
|
</article>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<section id="section-frequencies" class="panel">
|
|
|
<article class="card">
|
|
|
<h2>Решения по частотам</h2>
|
|
|
<div class="table-wrap">
|
|
|
<table id="freq-table">
|
|
|
<thead>
|
|
|
<tr>
|
|
|
<th>Частота (МГц)</th>
|
|
|
<th>X</th>
|
|
|
<th>Y</th>
|
|
|
<th>Z</th>
|
|
|
<th>СКО (RMSE)</th>
|
|
|
<th>Точно</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody></tbody>
|
|
|
</table>
|
|
|
</div>
|
|
|
</article>
|
|
|
|
|
|
<article class="card monitor-board">
|
|
|
<h2>Аналитика</h2>
|
|
|
<div class="monitor-grid">
|
|
|
<section class="monitor-panel monitor-frequency-panel">
|
|
|
<h3>Диапазон</h3>
|
|
|
<div id="ov-frequency-health" class="frequency-health-list"></div>
|
|
|
</section>
|
|
|
<section class="monitor-panel monitor-topfreq-panel">
|
|
|
<h3>Лучшие частоты</h3>
|
|
|
<div id="ov-top-frequencies" class="top-frequencies"></div>
|
|
|
</section>
|
|
|
<section class="monitor-panel monitor-trends-panel">
|
|
|
<h3>Точность</h3>
|
|
|
<div class="trend-stack">
|
|
|
<article class="trend-card">
|
|
|
<div class="trend-head">
|
|
|
<span>RMSE, м</span>
|
|
|
<b id="ov-trend-rmse-meta">н/д</b>
|
|
|
</div>
|
|
|
<div id="ov-trend-rmse-chart" class="sparkline-wrap"></div>
|
|
|
</article>
|
|
|
</div>
|
|
|
</section>
|
|
|
</div>
|
|
|
</article>
|
|
|
</section>
|
|
|
|
|
|
<section id="section-io" class="panel">
|
|
|
<article class="card">
|
|
|
<h2>Сигнал и отправка</h2>
|
|
|
<p class="muted">Приём данных и отправка координат.</p>
|
|
|
<div class="io-grid">
|
|
|
<section class="io-block">
|
|
|
<h3>Приёмники</h3>
|
|
|
<div id="input-flow" class="io-list"></div>
|
|
|
</section>
|
|
|
<section class="io-block">
|
|
|
<h3>Отправка</h3>
|
|
|
<div id="output-flow" class="io-list"></div>
|
|
|
</section>
|
|
|
</div>
|
|
|
|
|
|
<div id="io-admin-controls">
|
|
|
<h3 class="io-history-title">Тестовые сбои</h3>
|
|
|
<div id="error-controls" class="io-list"></div>
|
|
|
</div>
|
|
|
</article>
|
|
|
|
|
|
<article class="card monitor-board">
|
|
|
<h2>Обработка</h2>
|
|
|
<div class="monitor-grid">
|
|
|
<section class="monitor-panel monitor-signal-panel">
|
|
|
<h3>Сигналы</h3>
|
|
|
<div id="ov-signal-grid" class="signal-grid"></div>
|
|
|
</section>
|
|
|
<section class="monitor-panel monitor-stage-panel">
|
|
|
<h3>Этапы</h3>
|
|
|
<div id="ov-pipeline-stages" class="pipeline-stages"></div>
|
|
|
</section>
|
|
|
<section class="monitor-panel monitor-trends-panel">
|
|
|
<h3>Тренды</h3>
|
|
|
<div class="trend-stack">
|
|
|
<article class="trend-card">
|
|
|
<div class="trend-head">
|
|
|
<span>Средний RSSI, дБм</span>
|
|
|
<b id="ov-trend-rssi-meta">н/д</b>
|
|
|
</div>
|
|
|
<div id="ov-trend-rssi-chart" class="sparkline-wrap"></div>
|
|
|
</article>
|
|
|
<article class="trend-card">
|
|
|
<div class="trend-head">
|
|
|
<span>Успех отправки, %</span>
|
|
|
<b id="ov-trend-delivery-meta">н/д</b>
|
|
|
</div>
|
|
|
<div id="ov-trend-delivery-chart" class="sparkline-wrap"></div>
|
|
|
</article>
|
|
|
</div>
|
|
|
</section>
|
|
|
</div>
|
|
|
</article>
|
|
|
</section>
|
|
|
|
|
|
<section id="section-history" class="panel">
|
|
|
<article class="card history-dashboard history-head-card">
|
|
|
<h2>Журнал обмена</h2>
|
|
|
<p class="muted">Приём, расчёт и отправка результатов.</p>
|
|
|
</article>
|
|
|
|
|
|
<div class="history-layout">
|
|
|
<article class="card history-data-card">
|
|
|
<h2>Журнал</h2>
|
|
|
<div class="history-toolbar">
|
|
|
<label>
|
|
|
Статус
|
|
|
<select id="history-filter">
|
|
|
<option value="all">Все</option>
|
|
|
<option value="ok">Ок</option>
|
|
|
<option value="error">Ошибка</option>
|
|
|
<option value="partial">Частично</option>
|
|
|
<option value="skipped">Пропущено</option>
|
|
|
<option value="disabled">Отключено</option>
|
|
|
<option value="warming_up">Прогрев</option>
|
|
|
</select>
|
|
|
</label>
|
|
|
<div class="history-toolbar-right">
|
|
|
<label>
|
|
|
От
|
|
|
<input id="history-date-from" type="datetime-local" />
|
|
|
</label>
|
|
|
<label>
|
|
|
До
|
|
|
<input id="history-date-to" type="datetime-local" />
|
|
|
</label>
|
|
|
<label>
|
|
|
Страница
|
|
|
<select id="history-page-size">
|
|
|
<option value="10">10</option>
|
|
|
<option value="20">20</option>
|
|
|
<option value="50">50</option>
|
|
|
</select>
|
|
|
</label>
|
|
|
<div class="history-pager">
|
|
|
<button id="history-prev" class="btn" type="button">Назад</button>
|
|
|
<span id="history-page-info" class="badge badge-meta">Стр. 1/1 • 0 записей</span>
|
|
|
<button id="history-next" class="btn" type="button">Вперёд</button>
|
|
|
</div>
|
|
|
<button id="history-date-reset" class="btn" type="button">Сброс времени</button>
|
|
|
<div id="history-admin-actions" class="history-admin-actions">
|
|
|
<button id="history-record-toggle" class="btn" type="button">Пауза записи</button>
|
|
|
<span id="history-record-state" class="badge badge-meta">запись: вкл</span>
|
|
|
<button id="clear-history" class="btn" type="button">Очистить историю</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="table-wrap history-table-wrap">
|
|
|
<table id="io-history-table">
|
|
|
<thead>
|
|
|
<tr>
|
|
|
<th>Время</th>
|
|
|
<th>Частота (МГц)</th>
|
|
|
<th>Вход</th>
|
|
|
<th>Выход</th>
|
|
|
<th>Статус</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody></tbody>
|
|
|
</table>
|
|
|
</div>
|
|
|
</article>
|
|
|
|
|
|
<article class="card history-monitor-card">
|
|
|
<h2>Сводка</h2>
|
|
|
<div class="history-kpis">
|
|
|
<div class="kpi-card">
|
|
|
<span class="kpi-title">Событий</span>
|
|
|
<b id="hist-total" class="kpi-value">0</b>
|
|
|
</div>
|
|
|
<div class="kpi-card">
|
|
|
<span class="kpi-title">Успешно</span>
|
|
|
<b id="hist-ok" class="kpi-value">0</b>
|
|
|
</div>
|
|
|
<div class="kpi-card">
|
|
|
<span class="kpi-title">Проблемы</span>
|
|
|
<b id="hist-problem" class="kpi-value">0</b>
|
|
|
</div>
|
|
|
<div class="kpi-card">
|
|
|
<span class="kpi-title">Частот</span>
|
|
|
<b id="hist-freqs" class="kpi-value">0</b>
|
|
|
</div>
|
|
|
<div class="kpi-card">
|
|
|
<span class="kpi-title">Последнее</span>
|
|
|
<b id="hist-last" class="kpi-value">н/д</b>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="history-insights">
|
|
|
<section class="insight-panel">
|
|
|
<h3>Диагностика</h3>
|
|
|
<div id="history-monitor" class="history-monitor"></div>
|
|
|
</section>
|
|
|
<section class="insight-panel">
|
|
|
<h3>Тренды</h3>
|
|
|
<div id="history-trends" class="history-trends"></div>
|
|
|
</section>
|
|
|
</div>
|
|
|
</article>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<section id="section-servers" class="panel">
|
|
|
<article class="card servers-head-card">
|
|
|
<h2>Узлы системы</h2>
|
|
|
<p class="muted">Приёмники, фильтр и адреса отправки.</p>
|
|
|
</article>
|
|
|
|
|
|
<div class="servers-layout servers-layout-modern">
|
|
|
<article class="card servers-card servers-card-modern">
|
|
|
<div class="server-card-head">
|
|
|
<h3 class="servers-title">Приёмники</h3>
|
|
|
<p class="muted">Адреса, частоты и координаты.</p>
|
|
|
</div>
|
|
|
<div class="server-card-body">
|
|
|
<div class="selector-row">
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Активный вход</span>
|
|
|
<select id="receiver-select"></select>
|
|
|
</label>
|
|
|
<span id="receiver-count" class="badge badge-meta chip-counter">входов: 0</span>
|
|
|
</div>
|
|
|
<div class="action-group">
|
|
|
<button id="add-receiver" class="btn" type="button">Добавить вход</button>
|
|
|
<button id="remove-receiver" class="btn" type="button">Удалить вход</button>
|
|
|
</div>
|
|
|
<div class="field-grid field-grid-2">
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Имя</span>
|
|
|
<input id="rx-id" type="text" placeholder="rx_north" />
|
|
|
</label>
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">URL</span>
|
|
|
<input id="rx-url" type="text" placeholder="http://receiver-r0:9000/data" />
|
|
|
</label>
|
|
|
</div>
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Частоты, МГц</span>
|
|
|
<input id="rx-frequencies" type="text" placeholder="433.92, 868.1, 915.0" />
|
|
|
<span class="field-hint">Укажите через запятую только рабочие частоты этого входа.</span>
|
|
|
</label>
|
|
|
<div class="field-grid field-grid-3">
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">X</span>
|
|
|
<input id="rx-center-x" type="number" step="0.001" />
|
|
|
</label>
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Y</span>
|
|
|
<input id="rx-center-y" type="number" step="0.001" />
|
|
|
</label>
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Z</span>
|
|
|
<input id="rx-center-z" type="number" step="0.001" />
|
|
|
</label>
|
|
|
</div>
|
|
|
</div>
|
|
|
</article>
|
|
|
|
|
|
<article class="card servers-card servers-card-modern">
|
|
|
<div class="server-card-head">
|
|
|
<h3 class="servers-title">Общий фильтр</h3>
|
|
|
<p class="muted">Применяется автоматически ко всем входным серверам.</p>
|
|
|
</div>
|
|
|
<div class="server-card-body">
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Фильтр включен</span>
|
|
|
<select id="shared-filter-enabled">
|
|
|
<option value="true">да</option>
|
|
|
<option value="false">нет</option>
|
|
|
</select>
|
|
|
</label>
|
|
|
<div class="field-grid field-grid-2">
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Мин. частота, МГц</span>
|
|
|
<input id="shared-min-freq" type="number" step="0.001" min="0" />
|
|
|
</label>
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Макс. частота, МГц</span>
|
|
|
<input id="shared-max-freq" type="number" step="0.001" min="0" />
|
|
|
</label>
|
|
|
</div>
|
|
|
<div class="field-grid field-grid-2">
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Мин. RSSI, дБм</span>
|
|
|
<input id="shared-min-rssi" type="number" step="0.1" />
|
|
|
</label>
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Макс. RSSI, дБм</span>
|
|
|
<input id="shared-max-rssi" type="number" step="0.1" />
|
|
|
</label>
|
|
|
</div>
|
|
|
</div>
|
|
|
</article>
|
|
|
|
|
|
<article class="card servers-card servers-card-modern">
|
|
|
<div class="server-card-head">
|
|
|
<h3 class="servers-title">Серверы отправки</h3>
|
|
|
<p class="muted">Адреса для передачи координат.</p>
|
|
|
</div>
|
|
|
<div class="server-card-body">
|
|
|
<div class="selector-row">
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Активный выход</span>
|
|
|
<select id="output-select"></select>
|
|
|
</label>
|
|
|
<span id="output-count" class="badge badge-meta chip-counter">выходов: 0</span>
|
|
|
</div>
|
|
|
<div class="action-group">
|
|
|
<button id="add-output-server" class="btn" type="button">Добавить выход</button>
|
|
|
<button id="remove-output-server" class="btn" type="button">Удалить выход</button>
|
|
|
</div>
|
|
|
<div class="field-grid field-grid-2">
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Имя</span>
|
|
|
<input id="out-name" type="text" placeholder="sink_main" />
|
|
|
</label>
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Адрес</span>
|
|
|
<input id="out-ip" type="text" placeholder="output-sink:8080" />
|
|
|
</label>
|
|
|
</div>
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">API-токен</span>
|
|
|
<input id="write-token" type="password" placeholder="необязательно" />
|
|
|
</label>
|
|
|
</div>
|
|
|
</article>
|
|
|
</div>
|
|
|
|
|
|
<article class="card servers-actions-card">
|
|
|
<div class="editor-actions">
|
|
|
<button id="load-servers" class="btn">Загрузить</button>
|
|
|
<button id="save-servers" class="btn btn-primary">Сохранить узлы</button>
|
|
|
<span id="servers-state" class="badge">узлы: н/д</span>
|
|
|
</div>
|
|
|
</article>
|
|
|
|
|
|
<article id="users-card" class="card servers-actions-card">
|
|
|
<div class="server-card-head">
|
|
|
<h3 class="servers-title">Пользователи</h3>
|
|
|
<p class="muted">Управление учётными записями и ролями Keycloak.</p>
|
|
|
</div>
|
|
|
<div class="users-layout">
|
|
|
<div class="table-wrap users-table-wrap">
|
|
|
<table id="users-table">
|
|
|
<thead>
|
|
|
<tr>
|
|
|
<th>Логин</th>
|
|
|
<th>Роль</th>
|
|
|
<th>Состояние</th>
|
|
|
<th>Имя</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody></tbody>
|
|
|
</table>
|
|
|
</div>
|
|
|
<div class="users-form">
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">ID пользователя</span>
|
|
|
<input id="user-id" type="text" placeholder="заполняется автоматически" />
|
|
|
</label>
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Логин</span>
|
|
|
<input id="user-username" type="text" placeholder="operator_1" />
|
|
|
</label>
|
|
|
<div class="field-grid field-grid-2">
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Имя</span>
|
|
|
<input id="user-first-name" type="text" />
|
|
|
</label>
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Фамилия</span>
|
|
|
<input id="user-last-name" type="text" />
|
|
|
</label>
|
|
|
</div>
|
|
|
<div class="field-grid field-grid-2">
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Роль</span>
|
|
|
<select id="user-role">
|
|
|
<option value="user">Пользователь</option>
|
|
|
<option value="admin">Администратор</option>
|
|
|
</select>
|
|
|
</label>
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Включён</span>
|
|
|
<select id="user-enabled">
|
|
|
<option value="true">Да</option>
|
|
|
<option value="false">Нет</option>
|
|
|
</select>
|
|
|
</label>
|
|
|
</div>
|
|
|
<label class="field-control">
|
|
|
<span class="field-label">Пароль</span>
|
|
|
<input id="user-password" type="password" placeholder="для создания или смены" />
|
|
|
</label>
|
|
|
<div class="editor-actions">
|
|
|
<button id="load-users" class="btn" type="button">Обновить список</button>
|
|
|
<button id="create-user" class="btn btn-primary" type="button">Создать</button>
|
|
|
<button id="update-user" class="btn" type="button">Сохранить</button>
|
|
|
<button id="reset-user-password" class="btn" type="button">Сменить пароль</button>
|
|
|
<button id="delete-user" class="btn" type="button">Удалить</button>
|
|
|
<button id="clear-user-form" class="btn" type="button">Очистить</button>
|
|
|
</div>
|
|
|
<span id="users-state" class="badge">пользователи: н/д</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</article>
|
|
|
</section>
|
|
|
|
|
|
<section id="section-json" class="panel">
|
|
|
<article class="card config-head-card">
|
|
|
<h2>Конфиг</h2>
|
|
|
<div class="editor-actions">
|
|
|
<button id="load-config" class="btn">Загрузить</button>
|
|
|
<button id="save-config" class="btn btn-primary">Сохранить файл</button>
|
|
|
<span id="config-state" class="badge">конфиг: н/д</span>
|
|
|
</div>
|
|
|
</article>
|
|
|
|
|
|
<div class="config-layout config-layout-modern">
|
|
|
<article class="card config-editor-card config-editor-modern">
|
|
|
<div class="config-section-head">
|
|
|
<h3>JSON</h3>
|
|
|
<p class="muted">Полный файл настроек.</p>
|
|
|
</div>
|
|
|
<div class="config-editor-shell">
|
|
|
<div class="editor-toolbar">
|
|
|
<span class="editor-chip">JSON</span>
|
|
|
<span class="editor-chip">UTF-8</span>
|
|
|
<span class="editor-chip">runtime + input + system</span>
|
|
|
</div>
|
|
|
<textarea id="config-editor" class="editor" spellcheck="false"></textarea>
|
|
|
</div>
|
|
|
</article>
|
|
|
<article class="card config-help-card config-help-modern">
|
|
|
<div class="config-section-head">
|
|
|
<h3>Структура</h3>
|
|
|
<p class="muted">Основные разделы конфигурации.</p>
|
|
|
</div>
|
|
|
<div class="config-hints config-hints-grid">
|
|
|
<p><b>input.receivers[]</b><br />приемники: координаты, URL и частоты.</p>
|
|
|
<p><b>runtime.output_servers[]</b><br />серверы отправки координат.</p>
|
|
|
<p><b>input.default_input_filter</b><br />общий фильтр частот и RSSI.</p>
|
|
|
<p><b>system</b><br />системные таймеры, лимиты и автообновление.</p>
|
|
|
</div>
|
|
|
<h3>Подсказки</h3>
|
|
|
<ul class="config-tips">
|
|
|
<li>Поддерживайте уникальные `receiver_id` для каждого входа.</li>
|
|
|
<li>Согласуйте диапазоны частот между входными серверами.</li>
|
|
|
<li>Перед сохранением проверяйте JSON на валидность.</li>
|
|
|
</ul>
|
|
|
</article>
|
|
|
</div>
|
|
|
</section>
|
|
|
</section>
|
|
|
</main>
|
|
|
|
|
|
<script src="/static/app.js?v=20260319r2"></script>
|
|
|
<div id="toast-container" class="toast-container" aria-live="polite" aria-atomic="true"></div>
|
|
|
</body>
|
|
|
</html>
|
|
|
|