mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 11:09:18 +02:00
admin/system: move status panel above tabs, add collapse toggle
Status (services, PHP env, disk) is now always visible above the log/config tab bar rather than being one of the tab targets: - Status section rendered unconditionally above <nav class="sys-tabs">. - Services grid, PHP info grid and disk bar grouped inside a collapsible <section> with a header row containing the cache-freshness badge and a toggle button (▲ Réduire / ▼ Développer). - Collapse state persisted in localStorage so the preference survives page reloads (e.g. when switching log tabs). - Tab bar now only contains the three log tabs + nginx config; the 'Statut' tab is removed. Legacy ?tab=status URLs fall through to nginx_access. - PHP/disk sub-sections laid out in a 2-col grid inside the status panel; responsive single-col below 700px. - system.css: new .sys-status-section / .sys-status-header / .sys-status-toggle / .sys-status-meta rules added. - aria-current="page" added to active tab links. - todo/03-system-cache.md: all items marked done; notes added explaining why log caching was deliberately omitted.
This commit is contained in:
@@ -237,11 +237,12 @@ const ALLOWED_LINES = [50, 100, 200, 500];
|
||||
const NGINX_CONFIG_LIVE = '/etc/nginx/sites-available/posterg';
|
||||
const NGINX_CONFIG_LOCAL = APP_ROOT . '/nginx/posterg.conf';
|
||||
|
||||
// Active tab: 'status', 'nginx_config', or a log key
|
||||
$activeTab = $_GET['tab'] ?? 'nginx_access';
|
||||
if ($activeTab === 'status' || $activeTab === 'nginx_config') {
|
||||
// valid as-is
|
||||
} elseif (!array_key_exists($activeTab, LOG_FILES)) {
|
||||
// Active tab: 'nginx_config', or a log key (status is now always shown above tabs)
|
||||
$activeTab = $_GET['tab'] ?? 'nginx_access';
|
||||
if ($activeTab === 'status') {
|
||||
// legacy URL — redirect to default log tab, status is always visible
|
||||
$activeTab = 'nginx_access';
|
||||
} elseif ($activeTab !== 'nginx_config' && !array_key_exists($activeTab, LOG_FILES)) {
|
||||
$activeTab = 'nginx_access';
|
||||
}
|
||||
|
||||
@@ -293,7 +294,7 @@ $logLines = null;
|
||||
$logError = null;
|
||||
$logFileMeta = null;
|
||||
|
||||
if ($activeTab !== 'status' && $activeTab !== 'nginx_config') {
|
||||
if ($activeTab !== 'nginx_config') {
|
||||
$logPath = LOG_FILES[$activeTab]['path'];
|
||||
$logLines = readLogTail($logPath, $selectedN, $logError);
|
||||
if (file_exists($logPath)) {
|
||||
@@ -361,6 +362,27 @@ $isAdmin = true; $bodyClass = 'admin-body';
|
||||
$extraCss = ['/assets/css/system.css'];
|
||||
$extraJsInline = <<<'JS'
|
||||
(function () {
|
||||
// ── Status section toggle ──────────────────────────────────────────
|
||||
var toggleBtn = document.getElementById('sys-status-toggle');
|
||||
var statusBody = document.getElementById('sys-status-body');
|
||||
if (toggleBtn && statusBody) {
|
||||
toggleBtn.addEventListener('click', function () {
|
||||
var collapsed = statusBody.hidden;
|
||||
statusBody.hidden = !collapsed;
|
||||
toggleBtn.setAttribute('aria-expanded', collapsed ? 'true' : 'false');
|
||||
toggleBtn.textContent = collapsed ? '▲ Réduire' : '▼ Développer';
|
||||
try { localStorage.setItem('sys_status_collapsed', collapsed ? '0' : '1'); } catch(e) {}
|
||||
});
|
||||
// Restore collapsed state
|
||||
try {
|
||||
if (localStorage.getItem('sys_status_collapsed') === '1') {
|
||||
statusBody.hidden = true;
|
||||
toggleBtn.setAttribute('aria-expanded', 'false');
|
||||
toggleBtn.textContent = '▼ Développer';
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
// ── Instant tab switch on lines-select change ──────────────────────
|
||||
var sel = document.getElementById('lines-select');
|
||||
if (sel) {
|
||||
@@ -425,76 +447,85 @@ require_once APP_ROOT . '/templates/head.php';
|
||||
<a href="?tab=<?= htmlspecialchars($activeTab) ?>&n=<?= $selectedN ?>&refresh=1">Forcer actualisation</a>
|
||||
</p>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════════════════
|
||||
STATUS SECTION — always visible above tabs
|
||||
════════════════════════════════════════════════════════════════════ -->
|
||||
<section class="sys-status-section" aria-label="Statut du système">
|
||||
<div class="sys-status-header">
|
||||
<h2 class="srv-section-title" style="margin:0;border:none;padding:0;">Statut
|
||||
<?php if ($statusCached && $statusCacheAge !== null): ?>
|
||||
<span class="sys-cache-badge sys-cache-badge--hit" title="Données en cache">
|
||||
⚡ Cache — il y a <?= $statusCacheAge ?>s
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<span class="sys-cache-badge sys-cache-badge--miss" title="Données fraîches">
|
||||
⟳ Actualisé
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</h2>
|
||||
<button id="sys-status-toggle" class="sys-status-toggle"
|
||||
aria-expanded="true" aria-controls="sys-status-body"
|
||||
type="button">▲ Réduire</button>
|
||||
</div>
|
||||
|
||||
<div id="sys-status-body">
|
||||
<div class="srv-grid">
|
||||
<?php foreach ($checks as $check): ?>
|
||||
<?php $st = $check['status'] ?? 'unknown'; ?>
|
||||
<div class="srv-card">
|
||||
<div class="srv-card__header">
|
||||
<span class="srv-card__name"><?= htmlspecialchars($check['label']) ?></span>
|
||||
<span class="<?= statusClass($st) ?>"><?= statusLabel($st) ?></span>
|
||||
</div>
|
||||
<?php if (!empty($check['detail'])): ?>
|
||||
<div class="srv-card__detail"><?= htmlspecialchars($check['detail']) ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="sys-status-meta">
|
||||
<div>
|
||||
<h3 class="srv-section-title" style="margin-bottom:.75rem;">Environnement PHP</h3>
|
||||
<div class="php-grid" style="margin-bottom:0;">
|
||||
<?php foreach ($phpInfo as $key => $val): ?>
|
||||
<div class="php-item">
|
||||
<div class="php-item__key"><?= htmlspecialchars($key) ?></div>
|
||||
<div class="php-item__val"><?= htmlspecialchars($val) ?></div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="srv-section-title" style="margin-bottom:.75rem;">Espace disque</h3>
|
||||
<?php $diskColor = $diskPct > 85 ? '#e05555' : ($diskPct > 70 ? '#ffc107' : '#4caf50'); ?>
|
||||
<div class="disk-bar-wrap">
|
||||
<div class="disk-bar" style="--disk-pct:<?= $diskPct ?>%;--disk-color:<?= $diskColor ?>"></div>
|
||||
</div>
|
||||
<div class="disk-stats">
|
||||
<span><?= humanBytes($diskUsed) ?> utilisé (<?= $diskPct ?>%)</span>
|
||||
<span><?= humanBytes($diskFree) ?> libre / <?= humanBytes($diskTotal) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── Tab bar ─────────────────────────────────────────────────────── -->
|
||||
<nav class="sys-tabs" aria-label="Sections système">
|
||||
<a href="?tab=status"
|
||||
class="sys-tab <?= $activeTab === 'status' ? 'active' : '' ?>">Statut</a>
|
||||
<nav class="sys-tabs" aria-label="Journaux et configuration">
|
||||
<?php foreach (LOG_FILES as $key => $def): ?>
|
||||
<a href="?tab=<?= htmlspecialchars($key) ?>&n=<?= $selectedN ?>"
|
||||
class="sys-tab <?= $activeTab === $key ? 'active' : '' ?>">
|
||||
class="sys-tab <?= $activeTab === $key ? 'active' : '' ?>"
|
||||
<?= $activeTab === $key ? 'aria-current="page"' : '' ?>>
|
||||
<?= htmlspecialchars($def['label']) ?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
<a href="?tab=nginx_config"
|
||||
class="sys-tab <?= $activeTab === 'nginx_config' ? 'active' : '' ?>">nginx — config</a>
|
||||
class="sys-tab <?= $activeTab === 'nginx_config' ? 'active' : '' ?>"
|
||||
<?= $activeTab === 'nginx_config' ? 'aria-current="page"' : '' ?>>nginx — config</a>
|
||||
</nav>
|
||||
|
||||
<?php if ($activeTab === 'status'): ?>
|
||||
<!-- ════════════════════════════════════════════════════════════════════
|
||||
STATUS PANEL
|
||||
════════════════════════════════════════════════════════════════════ -->
|
||||
|
||||
<h2 class="srv-section-title">Services
|
||||
<?php if ($statusCached && $statusCacheAge !== null): ?>
|
||||
<span class="sys-cache-badge sys-cache-badge--hit" title="Données en cache">
|
||||
⚡ Cache — il y a <?= $statusCacheAge ?>s
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<span class="sys-cache-badge sys-cache-badge--miss" title="Données fraîches">
|
||||
⟳ Actualisé
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</h2>
|
||||
<div class="srv-grid">
|
||||
<?php foreach ($checks as $check): ?>
|
||||
<?php $st = $check['status'] ?? 'unknown'; ?>
|
||||
<div class="srv-card">
|
||||
<div class="srv-card__header">
|
||||
<span class="srv-card__name"><?= htmlspecialchars($check['label']) ?></span>
|
||||
<span class="<?= statusClass($st) ?>"><?= statusLabel($st) ?></span>
|
||||
</div>
|
||||
<?php if (!empty($check['detail'])): ?>
|
||||
<div class="srv-card__detail"><?= htmlspecialchars($check['detail']) ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<h2 class="srv-section-title">Environnement PHP</h2>
|
||||
<div class="php-grid">
|
||||
<?php foreach ($phpInfo as $key => $val): ?>
|
||||
<div class="php-item">
|
||||
<div class="php-item__key"><?= htmlspecialchars($key) ?></div>
|
||||
<div class="php-item__val"><?= htmlspecialchars($val) ?></div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<h2 class="srv-section-title">Espace disque</h2>
|
||||
<div style="max-width:420px;margin-bottom:2.5rem;">
|
||||
<?php
|
||||
$diskColor = $diskPct > 85 ? '#e05555' : ($diskPct > 70 ? '#ffc107' : '#4caf50');
|
||||
?>
|
||||
<div class="disk-bar-wrap">
|
||||
<div class="disk-bar" style="--disk-pct:<?= $diskPct ?>%;--disk-color:<?= $diskColor ?>"></div>
|
||||
</div>
|
||||
<div class="disk-stats">
|
||||
<span><?= humanBytes($diskUsed) ?> utilisé (<?= $diskPct ?>%)</span>
|
||||
<span><?= humanBytes($diskFree) ?> libre / <?= humanBytes($diskTotal) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php elseif ($activeTab === 'nginx_config'): ?>
|
||||
<?php if ($activeTab === 'nginx_config'): ?>
|
||||
<!-- ════════════════════════════════════════════════════════════════════
|
||||
NGINX CONFIG PANEL
|
||||
════════════════════════════════════════════════════════════════════ -->
|
||||
|
||||
@@ -30,6 +30,48 @@
|
||||
border-bottom-color: var(--accent-primary);
|
||||
}
|
||||
|
||||
/* ── Status section (always-visible panel above tabs) ─────────────────── */
|
||||
.sys-status-section {
|
||||
background: #1a1a1a;
|
||||
border: 1px solid #555;
|
||||
border-radius: 6px;
|
||||
padding: 1rem 1.25rem 1.25rem;
|
||||
margin-bottom: 1.75rem;
|
||||
}
|
||||
.sys-status-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.sys-status-toggle {
|
||||
background: none;
|
||||
border: 1px solid #555;
|
||||
color: #969696;
|
||||
border-radius: 3px;
|
||||
font-size: .72rem;
|
||||
font-family: inherit;
|
||||
padding: .2rem .55rem;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: color .15s, border-color .15s;
|
||||
}
|
||||
.sys-status-toggle:hover {
|
||||
color: #e8e8e8;
|
||||
border-color: #888;
|
||||
}
|
||||
.sys-status-meta {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.5rem 2rem;
|
||||
margin-top: 1.5rem;
|
||||
padding-top: 1.25rem;
|
||||
border-top: 1px solid #333;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
.sys-status-meta { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
/* ── Status cards ──────────────────────────────────────────────────────── */
|
||||
.srv-grid {
|
||||
display: grid;
|
||||
|
||||
Reference in New Issue
Block a user