mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
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.
147 lines
5.7 KiB
JavaScript
147 lines
5.7 KiB
JavaScript
(function () {
|
|
|
|
// ── Status section collapse 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) {}
|
|
});
|
|
try {
|
|
if (localStorage.getItem('sys_status_collapsed') === '1') {
|
|
statusBody.hidden = true;
|
|
toggleBtn.setAttribute('aria-expanded', 'false');
|
|
toggleBtn.textContent = '▼ Développer';
|
|
}
|
|
} catch(e) {}
|
|
}
|
|
|
|
// ── fetch()-based tab panel loading ───────────────────────────────
|
|
var panel = document.getElementById('sys-tab-panel');
|
|
var tabNav = document.querySelector('.sys-tabs');
|
|
if (!panel || !tabNav || !window.fetch) return;
|
|
|
|
var currentTab = panel.dataset.tab;
|
|
var currentN = panel.dataset.n;
|
|
|
|
function loadPanel(tab, n, pushState) {
|
|
var url = new URL(window.location.href);
|
|
var fragUrl = new URL('/admin/system-fragment.php', window.location.origin);
|
|
fragUrl.searchParams.set('tab', tab);
|
|
fragUrl.searchParams.set('n', n);
|
|
|
|
// Mark the panel as loading
|
|
panel.classList.add('sys-panel-loading');
|
|
|
|
fetch(fragUrl.toString(), { credentials: 'same-origin' })
|
|
.then(function (r) {
|
|
if (!r.ok) throw new Error('HTTP ' + r.status);
|
|
return r.text();
|
|
})
|
|
.then(function (html) {
|
|
panel.innerHTML = html;
|
|
panel.dataset.tab = tab;
|
|
panel.dataset.n = n;
|
|
currentTab = tab;
|
|
currentN = n;
|
|
panel.classList.remove('sys-panel-loading');
|
|
|
|
// Update active tab indicators
|
|
tabNav.querySelectorAll('.sys-tab').forEach(function (a) {
|
|
var isActive = a.dataset.tab === tab;
|
|
a.classList.toggle('active', isActive);
|
|
if (isActive) {
|
|
a.setAttribute('aria-current', 'page');
|
|
} else {
|
|
a.removeAttribute('aria-current');
|
|
}
|
|
});
|
|
|
|
// Re-bind inner controls (lines-select, copy btn)
|
|
bindPanelControls();
|
|
|
|
// Update browser URL without reloading
|
|
if (pushState) {
|
|
url.searchParams.set('tab', tab);
|
|
url.searchParams.set('n', n);
|
|
history.pushState({ tab: tab, n: n }, '', url.toString());
|
|
}
|
|
})
|
|
.catch(function () {
|
|
// Graceful degradation: fall back to full page load
|
|
panel.classList.remove('sys-panel-loading');
|
|
url.searchParams.set('tab', tab);
|
|
url.searchParams.set('n', n);
|
|
window.location.href = url.toString();
|
|
});
|
|
}
|
|
|
|
// Wire tab links
|
|
tabNav.querySelectorAll('.sys-tab').forEach(function (a) {
|
|
a.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
var tab = a.dataset.tab;
|
|
if (tab && tab !== currentTab) {
|
|
loadPanel(tab, currentN, true);
|
|
}
|
|
});
|
|
});
|
|
|
|
// Handle browser back/forward
|
|
window.addEventListener('popstate', function (e) {
|
|
if (e.state && e.state.tab) {
|
|
loadPanel(e.state.tab, e.state.n || 100, false);
|
|
}
|
|
});
|
|
|
|
function bindPanelControls() {
|
|
// Lines-select
|
|
var sel = panel.querySelector('#lines-select');
|
|
if (sel) {
|
|
sel.addEventListener('change', function () {
|
|
loadPanel(currentTab, parseInt(this.value, 10), true);
|
|
});
|
|
}
|
|
// Copy button
|
|
var copyBtn = panel.querySelector('#log-copy-btn');
|
|
var logOut = panel.querySelector('#log-output');
|
|
if (copyBtn && logOut) {
|
|
copyBtn.addEventListener('click', function () {
|
|
var text = Array.from(logOut.querySelectorAll('.log-line'))
|
|
.map(function (el) { return el.textContent; })
|
|
.join('\n');
|
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
navigator.clipboard.writeText(text).then(function () {
|
|
copyBtn.textContent = '✓ Copié';
|
|
copyBtn.classList.add('copied');
|
|
setTimeout(function () {
|
|
copyBtn.textContent = 'Copier';
|
|
copyBtn.classList.remove('copied');
|
|
}, 2000);
|
|
}).catch(function () { fallbackCopy(text); });
|
|
} else {
|
|
fallbackCopy(text);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Bind controls already present on first load
|
|
bindPanelControls();
|
|
|
|
function fallbackCopy(text) {
|
|
var ta = document.createElement('textarea');
|
|
ta.value = text;
|
|
ta.style.cssText = 'position:fixed;opacity:0;top:0;left:0';
|
|
document.body.appendChild(ta);
|
|
ta.select();
|
|
try { document.execCommand('copy'); } catch (e) {}
|
|
document.body.removeChild(ta);
|
|
}
|
|
|
|
})();
|