Move the ~130-line $extraJsInline heredoc from admin/system.php into a
static file public/assets/js/system.js, loaded via $extraJs so the
template footer emits a normal <script src=…>.
Replace 4 inline style= attributes with named CSS modifier classes in
system.css:
- style="margin:0;border:none;padding:0" on .srv-section-title
→ .srv-section-title--compact
- style="margin-bottom:.75rem" on sub-heading <h3>
→ .srv-section-title--sub
- style="margin-bottom:0" on .php-grid
→ .php-grid--flush
- style="font-size:.84rem;color:var(--text-secondary)" on <label>
→ .log-toolbar label rule in system.css
The one remaining inline style (--disk-pct / --disk-color CSS custom
properties on .disk-bar) is intentionally kept: it carries PHP runtime
values that cannot be expressed in a static stylesheet.
Replace the two undefined variables that had crept in:
- var(--admin-border) → #555 (in .log-output border)
- var(--admin-text-muted) → #969696 (inline style on log toolbar label,
in both system.php and system-fragment.php)
Revert the incorrect intermediate attempt that mapped dark-UI hex values
to light-theme tokens (--bg-primary: #fff, --border-primary: #ddd, etc.)
and also revert the .admin-body override block that was added to
variables.css — variables.css is shared and must not have per-component
overrides.
All remaining var() calls in system.css now reference tokens that exist
in variables.css:
--accent-primary, --accent-green, --error, --warning, --success,
--text-tertiary
The dark surface colours (#1a1a1a, #242424, #0d0d0d, #555, #969696, etc.)
stay as literal hex values, consistent with how admin.css handles them.
Add system-fragment.php — a thin authenticated endpoint that returns only
the tab-panel HTML (toolbar + meta + log/nginx-config output) for a given
?tab=&n= combination. No page shell, no status section, no DB queries.
system.php changes:
- Tab <a> elements gain data-tab= attributes used by JS to identify the
target without parsing hrefs.
- Tab panel content wrapped in <div id=sys-tab-panel data-tab= data-n=>
which JS uses as both the swap target and its own state store.
- JS rewritten: tab clicks and lines-select changes call loadPanel()
which fetch()es system-fragment.php, swaps innerHTML, updates active
tab ARIA attributes, and pushes state via history.pushState.
- Browser back/forward handled via popstate listener.
- bindPanelControls() re-wires the lines-select and copy-to-clipboard
button after every innerHTML swap (event delegation not feasible here
because log-output is replaced wholesale).
- fetch() failure falls back to window.location.href (full page load).
- Tabs without JS still work: <a href> links go to system.php?tab=…
as before.
system-fragment.php:
- Requires AdminAuth::isAuthenticated(); returns 403 on failure.
- Validates tab and n params against the same whitelist as system.php.
- All helper functions namespaced with frag_ prefix to avoid redeclaration
if PHP ever includes both files in the same process.
- Renders identical HTML to the corresponding section in system.php.
system.css:
- #sys-tab-panel gets min-height:8rem and position:relative to prevent
layout jump during fetch.
- .sys-panel-loading: opacity 0.4 + pointer-events:none + subtle
diagonal-stripe ::after overlay with shimmer animation.
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.
Add a TTL-based cache for the expensive checks on the admin system page,
eliminating repeated systemctl subprocess calls (~4×~100ms), curl self-pings
(~200-500ms), disk_*_space() and PHP ini reads on every page load.
Changes:
- storage/migrations/007_system_cache.sql: new migration creating the
system_cache table (key TEXT PK, value TEXT, updated_at INTEGER)
- storage/schema.sql: system_cache table added before pages table
- Applied migration to live storage/posterg.db
- src/SystemCache.php: new class with get/set/isStale/ageSeconds/invalidate;
uses SQLite INSERT … ON CONFLICT upsert; no external dependencies
- src/Database.php: added getDatabasePath(): string accessor
- public/admin/system.php:
- Bootstrap SystemCache at request start using the existing DB PDO handle
- system_status: cached with 2-min TTL (systemctl + curl checks)
- php_info: cached with 1-hour TTL (PHP ini values are runtime-constant)
- disk_info: cached with 5-min TTL (total/free/used/pct tuple)
- Logs section: unchanged — always reads live log tail per active tab
- ?refresh=1 GET param invalidates all three cache keys before rendering
- Status panel heading shows cache badge: '⚡ Cache — il y a Xs' (hit)
or '⟳ Actualisé' (miss/fresh), styled via new .sys-cache-badge rules
- public/assets/css/system.css: .sys-cache-badge / --hit / --miss styles
- Create public/assets/css/system.css with all 280 lines of CSS that were
inline in system.php: tab bar, status cards, PHP info grid, disk bar,
log viewer, nginx config viewer, and syntax-highlight classes.
- Disk bar dynamic values (width %, colour) moved from PHP-interpolated CSS
rules to CSS custom properties (--disk-pct, --disk-color) set on the
element via an inline style attribute; static .disk-bar rule in system.css
consumes them via var().
- system.php JS block (tab-select auto-nav + copy-to-clipboard) moved to
$extraJsInline heredoc; footer.php emits it before </body> — keeps it
out of the document <head> and removes the bare <script> after </main>.
- system.php now sets $extraCss = ['/assets/css/system.css'] so head.php
emits a proper <link> in <head>, consistent with all other admin pages.
- No behaviour change; system.php is now zero inline CSS/JS.