106 lines
7.0 KiB
PHP
106 lines
7.0 KiB
PHP
<?php
|
|
/*
|
|
* Reflector Dashboard — web front-end for SvxReflector
|
|
* Copyright (C) 2026 SA7AUX (Björn)
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it under
|
|
* the terms of the GNU Affero General Public License as published by the Free
|
|
* Software Foundation, either version 3 of the License, or (at your option) any
|
|
* later version. See the LICENSE file for details.
|
|
*/
|
|
// Shared header — include on all pages
|
|
if (!function_exists('cfg')) require __DIR__ . '/lib.php';
|
|
if (session_status() !== PHP_SESSION_ACTIVE) start_session();
|
|
// Never let browsers cache these pages: without this they guess freshness
|
|
// themselves and keep showing days-old UI after every deploy.
|
|
header('Cache-Control: no-store');
|
|
?>
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<title><?= $page_title ?? 'Reflector Dashboard' ?></title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<style>
|
|
:root{ color-scheme:dark; --bg:#000; --fg:#00ffff; --fg2:rgba(0,255,255,.75); --line:rgba(0,255,255,.35); }
|
|
html, body{ height:100%; background:var(--bg); color:var(--fg); margin:0; }
|
|
body{ display:flex; flex-direction:column; min-height:100vh; }
|
|
*{ color:var(--fg); }
|
|
*::selection{ background: rgba(0,255,255,.2); }
|
|
.navbar{ background: var(--bg) !important; position:sticky; top:0; z-index:1030; }
|
|
a, .nav-link{ color:var(--fg) !important; text-decoration:none; }
|
|
.text-muted, .text-secondary{ color: var(--fg2) !important; }
|
|
.btn.btn-outline-secondary{ background: var(--bg) !important; color: var(--fg) !important; border-color: rgba(0,255,255,.7) !important; }
|
|
.btn.btn-outline-secondary:hover{ background: rgba(0,255,255,.1) !important; }
|
|
.btn.btn-primary{ background: rgba(0,255,255,.15) !important; color: var(--fg) !important; border-color: rgba(0,255,255,.7) !important; }
|
|
.btn.btn-primary:hover{ background: rgba(0,255,255,.25) !important; }
|
|
.btn.btn-danger{ background: rgba(255,80,80,.2) !important; color: #ff8080 !important; border-color: rgba(255,80,80,.6) !important; }
|
|
.btn.btn-warning{ background: rgba(255,200,0,.15) !important; color: #ffc800 !important; border-color: rgba(255,200,0,.5) !important; }
|
|
.btn.btn-success{ background: rgba(0,200,100,.15) !important; color: #00c864 !important; border-color: rgba(0,200,100,.5) !important; }
|
|
.btn.btn-outline-danger{ background: transparent !important; color: #ff8080 !important; border-color: rgba(255,80,80,.6) !important; }
|
|
.form-control, .form-select{ background: var(--bg) !important; color: var(--fg) !important; border-color: rgba(0,255,255,.5) !important; }
|
|
.form-control:focus, .form-select:focus{ box-shadow: 0 0 0 .2rem rgba(0,255,255,.25) !important; }
|
|
.form-label{ color: var(--fg2) !important; }
|
|
.panel{ border:1px solid var(--line); border-radius:10px; padding:16px; margin-bottom:16px; }
|
|
.mono{ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
|
|
.border, .border-bottom{ border-color: var(--line) !important; }
|
|
main.content{ flex:1; }
|
|
footer{ flex-shrink:0; position:sticky; bottom:0; background:var(--bg); }
|
|
table.data-table{ width:100%; min-width:500px; border-collapse:collapse; font-size:.85rem; }
|
|
table.data-table th, table.data-table td{ padding:6px 10px; border-bottom:1px solid var(--line); }
|
|
table.data-table th{ color: var(--fg2); font-weight:600; text-align:left; }
|
|
.profile-tabs{ display:flex; gap:4px; flex-wrap:wrap; border-bottom:1px solid var(--line); padding-bottom:8px; margin-bottom:16px; }
|
|
.profile-tab{ display:flex; align-items:center; gap:6px; padding:8px 14px; background:transparent; border:1px solid transparent; border-radius:6px 6px 0 0; color:var(--fg2); cursor:pointer; font-size:.85rem; transition:background .15s, color .15s; white-space:nowrap; }
|
|
.profile-tab:hover{ background:rgba(0,255,255,.08); color:var(--fg); }
|
|
.profile-tab.active{ background:rgba(0,255,255,.12); border-color:var(--line); border-bottom-color:var(--bg); color:var(--fg); }
|
|
.node-card{ border:1px solid var(--line); border-radius:8px; padding:12px; margin-bottom:12px; }
|
|
.node-badge{ display:inline-block; background:rgba(0,255,255,.1); border:1px solid var(--line); border-radius:4px; padding:2px 8px; font-size:.75rem; margin:2px; }
|
|
.node-grid{ display:grid; grid-template-columns:auto 1fr; gap:2px 12px; font-size:.85rem; }
|
|
.node-grid dt{ color:var(--fg2); font-weight:normal; }
|
|
.node-grid dd{ margin:0; }
|
|
#adminMenuItem{ position: relative; }
|
|
#adminSubMenu .dropdown-item{ padding: 6px 16px; white-space: nowrap; }
|
|
/* Dark dropdown menus (fixes white Bootstrap default on callsign/admin menu) */
|
|
.dropdown-menu{ background:#111 !important; border-color:var(--line) !important; }
|
|
.dropdown-item{ color:var(--fg) !important; }
|
|
.dropdown-item:hover, .dropdown-item:focus{ background:rgba(0,255,255,.1) !important; color:var(--fg) !important; }
|
|
.dropdown-divider{ border-color:var(--line) !important; }
|
|
#adminSubMenu{ position:absolute; right:100%; left:auto; top:0; min-width:170px; background:#111; border:1px solid var(--line); border-radius:6px; padding:4px 0; list-style:none; margin:0; z-index:1050; }
|
|
@media (max-width:576px){ .profile-tab span{ display:none; } .profile-tab{ padding:8px 10px; } }
|
|
@media (max-width: 768px) {
|
|
.container, .container-fluid { padding-left: 10px; padding-right: 10px; }
|
|
table.data-table { font-size: .75rem; }
|
|
table.data-table th, table.data-table td { padding: 4px 6px; }
|
|
.node-grid { grid-template-columns: 1fr; gap: 1px 0; }
|
|
.node-grid dt { margin-top: 6px; }
|
|
.panel { padding: 12px; }
|
|
.btn-sm { font-size: .75rem; padding: 3px 8px; }
|
|
.mono { font-size: .8rem; word-break: break-all; }
|
|
}
|
|
@media (max-width: 480px) {
|
|
table.data-table th:nth-child(3), table.data-table td:nth-child(3),
|
|
table.data-table th:nth-child(5), table.data-table td:nth-child(5),
|
|
table.data-table th:nth-child(6), table.data-table td:nth-child(6) { display: none; }
|
|
}
|
|
<?= $extra_css ?? '' ?>
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<nav class="navbar navbar-expand border-bottom">
|
|
<div class="container-fluid">
|
|
<ul class="navbar-nav me-auto">
|
|
<li class="nav-item"><a class="nav-link" href="/#clients">Reflector clients</a></li>
|
|
<li class="nav-item"><a class="nav-link" href="/#monitor">Monitor</a></li>
|
|
<li class="nav-item"><a class="nav-link" href="/#lastheard">Lastheard</a></li>
|
|
<li class="nav-item"><a class="nav-link" href="/#ctcss">CTCSS Table</a></li>
|
|
<li class="nav-item"><a class="nav-link" href="/#map">Map</a></li>
|
|
<li class="nav-item"><a class="nav-link" href="/help.html">Help</a></li>
|
|
</ul>
|
|
<button class="btn btn-outline-secondary btn-sm me-2" id="btnRegister" type="button">Register</button>
|
|
<button class="btn btn-outline-secondary btn-sm" id="btnSignIn" type="button">Sign in</button>
|
|
</div>
|
|
</nav>
|
|
<main class="content">
|