feat(ui): persist active tab in localStorage and update release info in-place without page reload
CI Test Suite / Run Component Tests & Pipeline Verification (push) Successful in 1m43s
CI Test Suite / Run Component Tests & Pipeline Verification (push) Successful in 1m43s
This commit is contained in:
+86
-16
@@ -658,7 +658,7 @@ class WebUIRenderer:
|
||||
<div class="card-title" style="font-size: 15px;">🌐 Server Version & Upstream Release Status</div>
|
||||
<div class="subtitle" style="margin-top: 4px;">Upstream release tracking via Gitea production channels</div>
|
||||
</div>
|
||||
{('<span class="badge badge-warning">UPDATE AVAILABLE</span>' if update_status.get("server_update_available") else '<span class="badge badge-active">UP TO DATE</span>')}
|
||||
<span id="upstreamReleaseStatusBadge">{('<span class="badge badge-warning">UPDATE AVAILABLE</span>' if update_status.get("server_update_available") else '<span class="badge badge-active">UP TO DATE</span>')}</span>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; margin-bottom: 20px;">
|
||||
<div style="background: #0f172a; padding: 14px 18px; border-radius: 8px; border: 1px solid var(--border);">
|
||||
@@ -667,16 +667,16 @@ class WebUIRenderer:
|
||||
</div>
|
||||
<div style="background: #0f172a; padding: 14px 18px; border-radius: 8px; border: 1px solid var(--border);">
|
||||
<div style="font-size: 12px; color: var(--text-muted); margin-bottom: 4px;">Latest Upstream Release</div>
|
||||
<div style="font-size: 18px; font-weight: 700; color: #34d399;">v{update_status.get('latest_upstream_version') or LOGAR_VERSION}</div>
|
||||
<div id="latestUpstreamVersionText" style="font-size: 18px; font-weight: 700; color: #34d399;">v{update_status.get('latest_upstream_version') or LOGAR_VERSION}</div>
|
||||
</div>
|
||||
<div style="background: #0f172a; padding: 14px 18px; border-radius: 8px; border: 1px solid var(--border);">
|
||||
<div style="font-size: 12px; color: var(--text-muted); margin-bottom: 4px;">Last Checked</div>
|
||||
<div style="font-size: 14px; font-weight: 600; color: #e2e8f0; margin-top: 3px;">{update_status.get('last_checked_utc') or 'Never'}</div>
|
||||
<div id="lastCheckedText" style="font-size: 14px; font-weight: 600; color: #e2e8f0; margin-top: 3px;">{update_status.get('last_checked_utc') or 'Never'}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
|
||||
<button type="button" onclick="checkForUpdatesNow()" class="btn btn-primary">🔍 Check for Upstream Updates</button>
|
||||
{(f'<a href="{html.escape(update_status.get("release_url", ""))}" target="_blank" class="btn btn-secondary">🌐 View Release on Gitea</a>' if update_status.get("release_url") else '')}
|
||||
<span id="viewReleaseLinkContainer">{(f'<a href="{html.escape(update_status.get("release_url", ""))}" target="_blank" class="btn btn-secondary">🌐 View Release on Gitea</a>' if update_status.get("release_url") else '')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -771,18 +771,18 @@ class WebUIRenderer:
|
||||
<tr>
|
||||
<td><span class="badge badge-os">WINDOWS</span></td>
|
||||
<td><strong>{html.escape(hosted_packages.get('windows', {}).get('filename') or 'LOGAR_Client_Setup.exe')}</strong></td>
|
||||
<td><code>v{html.escape(str(hosted_packages.get('windows', {}).get('version') or hosted_client_ver))}</code></td>
|
||||
<td>{((hosted_packages.get('windows', {}).get('size_bytes', 0) // 1024) if hosted_packages.get('windows', {}).get('exists') else 0)} KB</td>
|
||||
<td><code title="{html.escape(hosted_packages.get('windows', {}).get('sha256', ''))}">{(hosted_packages.get('windows', {}).get('sha256', 'N/A')[:16] + '...') if hosted_packages.get('windows', {}).get('sha256') else 'N/A'}</code></td>
|
||||
<td>{('<span class="badge badge-active">Ready</span>' if hosted_packages.get('windows', {}).get('exists') else '<span class="badge badge-error">Failed</span>')}</td>
|
||||
<td><code id="pkgWinVersion">v{html.escape(str(hosted_packages.get('windows', {}).get('version') or hosted_client_ver))}</code></td>
|
||||
<td><span id="pkgWinSize">{((hosted_packages.get('windows', {}).get('size_bytes', 0) // 1024) if hosted_packages.get('windows', {}).get('exists') else 0)} KB</span></td>
|
||||
<td><code id="pkgWinSha" title="{html.escape(hosted_packages.get('windows', {}).get('sha256', ''))}">{(hosted_packages.get('windows', {}).get('sha256', 'N/A')[:16] + '...') if hosted_packages.get('windows', {}).get('sha256') else 'N/A'}</code></td>
|
||||
<td><span id="pkgWinStatus">{('<span class="badge badge-active">Ready</span>' if hosted_packages.get('windows', {}).get('exists') else '<span class="badge badge-error">Failed</span>')}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="badge badge-os">LINUX</span></td>
|
||||
<td><strong>{html.escape(hosted_packages.get('linux', {}).get('filename') or 'Linux_Client.bin')}</strong></td>
|
||||
<td><code>v{html.escape(str(hosted_packages.get('linux', {}).get('version') or hosted_client_ver))}</code></td>
|
||||
<td>{((hosted_packages.get('linux', {}).get('size_bytes', 0) // 1024) if hosted_packages.get('linux', {}).get('exists') else 0)} KB</td>
|
||||
<td><code title="{html.escape(hosted_packages.get('linux', {}).get('sha256', ''))}">{(hosted_packages.get('linux', {}).get('sha256', 'N/A')[:16] + '...') if hosted_packages.get('linux', {}).get('sha256') else 'N/A'}</code></td>
|
||||
<td>{('<span class="badge badge-active">Ready</span>' if hosted_packages.get('linux', {}).get('exists') else '<span class="badge badge-error">Failed</span>')}</td>
|
||||
<td><code id="pkgLinuxVersion">v{html.escape(str(hosted_packages.get('linux', {}).get('version') or hosted_client_ver))}</code></td>
|
||||
<td><span id="pkgLinuxSize">{((hosted_packages.get('linux', {}).get('size_bytes', 0) // 1024) if hosted_packages.get('linux', {}).get('exists') else 0)} KB</span></td>
|
||||
<td><code id="pkgLinuxSha" title="{html.escape(hosted_packages.get('linux', {}).get('sha256', ''))}">{(hosted_packages.get('linux', {}).get('sha256', 'N/A')[:16] + '...') if hosted_packages.get('linux', {}).get('sha256') else 'N/A'}</code></td>
|
||||
<td><span id="pkgLinuxStatus">{('<span class="badge badge-active">Ready</span>' if hosted_packages.get('linux', {}).get('exists') else '<span class="badge badge-error">Failed</span>')}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -1440,9 +1440,19 @@ class WebUIRenderer:
|
||||
if (btn && pane) {{
|
||||
btn.classList.add('active');
|
||||
pane.classList.add('active');
|
||||
try {{ localStorage.setItem('logar_active_tab', tabId); }} catch(e) {{}}
|
||||
}}
|
||||
}}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {{
|
||||
try {{
|
||||
const savedTab = localStorage.getItem('logar_active_tab');
|
||||
if (savedTab && document.getElementById('tabBtn-' + savedTab)) {{
|
||||
switchTab(savedTab);
|
||||
}}
|
||||
}} catch(e) {{}}
|
||||
}});
|
||||
|
||||
function showAlert(msg, isSuccess = true) {{
|
||||
const el = document.getElementById('statusAlert');
|
||||
el.className = 'alert ' + (isSuccess ? 'alert-success' : 'alert-error');
|
||||
@@ -1935,17 +1945,71 @@ class WebUIRenderer:
|
||||
el.appendChild(img);
|
||||
}}
|
||||
|
||||
function updatePackageTableInPlace(pkgs) {{
|
||||
if (!pkgs) return;
|
||||
if (pkgs.windows) {{
|
||||
const w = pkgs.windows;
|
||||
const vEl = document.getElementById('pkgWinVersion');
|
||||
if (vEl && w.version) vEl.textContent = 'v' + w.version;
|
||||
const szEl = document.getElementById('pkgWinSize');
|
||||
if (szEl && w.size_bytes !== undefined) szEl.textContent = Math.floor((w.size_bytes || 0) / 1024) + ' KB';
|
||||
const shaEl = document.getElementById('pkgWinSha');
|
||||
if (shaEl && w.sha256) {{ shaEl.textContent = w.sha256.substring(0, 16) + '...'; shaEl.title = w.sha256; }}
|
||||
const stEl = document.getElementById('pkgWinStatus');
|
||||
if (stEl) stEl.innerHTML = w.exists ? '<span class="badge badge-active">Ready</span>' : '<span class="badge badge-error">Failed</span>';
|
||||
}}
|
||||
if (pkgs.linux) {{
|
||||
const l = pkgs.linux;
|
||||
const vEl = document.getElementById('pkgLinuxVersion');
|
||||
if (vEl && l.version) vEl.textContent = 'v' + l.version;
|
||||
const szEl = document.getElementById('pkgLinuxSize');
|
||||
if (szEl && l.size_bytes !== undefined) szEl.textContent = Math.floor((l.size_bytes || 0) / 1024) + ' KB';
|
||||
const shaEl = document.getElementById('pkgLinuxSha');
|
||||
if (shaEl && l.sha256) {{ shaEl.textContent = l.sha256.substring(0, 16) + '...'; shaEl.title = l.sha256; }}
|
||||
const stEl = document.getElementById('pkgLinuxStatus');
|
||||
if (stEl) stEl.innerHTML = l.exists ? '<span class="badge badge-active">Ready</span>' : '<span class="badge badge-error">Failed</span>';
|
||||
}}
|
||||
}}
|
||||
|
||||
async function checkForUpdatesNow() {{
|
||||
showAlert('Checking upstream repository for new releases...');
|
||||
try {{
|
||||
const res = await fetch('/api/server/update/check', {{ method: 'POST' }});
|
||||
const data = await res.json();
|
||||
|
||||
const badgeEl = document.getElementById('upstreamReleaseStatusBadge');
|
||||
if (badgeEl) {{
|
||||
badgeEl.innerHTML = data.server_update_available
|
||||
? '<span class="badge badge-warning">UPDATE AVAILABLE</span>'
|
||||
: '<span class="badge badge-active">UP TO DATE</span>';
|
||||
}}
|
||||
|
||||
const verEl = document.getElementById('latestUpstreamVersionText');
|
||||
if (verEl && data.latest_upstream_version) {{
|
||||
verEl.textContent = 'v' + data.latest_upstream_version;
|
||||
}}
|
||||
|
||||
const checkEl = document.getElementById('lastCheckedText');
|
||||
if (checkEl) {{
|
||||
checkEl.textContent = data.last_checked_utc || (new Date().toISOString().replace('T', ' ').substring(0, 19) + ' UTC');
|
||||
}}
|
||||
|
||||
const linkEl = document.getElementById('viewReleaseLinkContainer');
|
||||
if (linkEl) {{
|
||||
linkEl.innerHTML = data.release_url
|
||||
? '<a href="' + data.release_url + '" target="_blank" class="btn btn-secondary">🌐 View Release on Gitea</a>'
|
||||
: '';
|
||||
}}
|
||||
|
||||
if (data.hosted_packages) {{
|
||||
updatePackageTableInPlace(data.hosted_packages);
|
||||
}}
|
||||
|
||||
if (data.server_update_available) {{
|
||||
showAlert('🔔 Update Available: v' + data.latest_upstream_version + ' is ready!');
|
||||
}} else {{
|
||||
showAlert('✅ LOGAR is up to date (Current: v' + data.current_server_version + ').');
|
||||
showAlert('✅ LOGAR is up to date (Current: v' + (data.current_server_version || '0.3.5.2') + ').');
|
||||
}}
|
||||
setTimeout(() => location.reload(), 1200);
|
||||
}} catch (err) {{
|
||||
alert('Failed to check for updates: ' + err.message);
|
||||
}}
|
||||
@@ -1972,7 +2036,6 @@ class WebUIRenderer:
|
||||
const data = await res.json();
|
||||
if (res.ok && data.status === 'success') {{
|
||||
showAlert('✅ Fleet update configuration saved successfully!');
|
||||
setTimeout(() => location.reload(), 1200);
|
||||
}} else {{
|
||||
let errMsg = 'Failed to save configuration';
|
||||
if (typeof data.detail === 'string') errMsg = data.detail;
|
||||
@@ -1992,7 +2055,14 @@ class WebUIRenderer:
|
||||
if (res.ok && data.status === 'success') {{
|
||||
const dl = (data.results && data.results.downloaded) ? data.results.downloaded.length : 0;
|
||||
showAlert('✅ Upstream synchronization complete (' + dl + ' package(s) downloaded/verified)!');
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
|
||||
try {{
|
||||
const statusRes = await fetch('/api/server/update/status');
|
||||
const statusData = await statusRes.json();
|
||||
if (statusData && statusData.hosted_packages) {{
|
||||
updatePackageTableInPlace(statusData.hosted_packages);
|
||||
}}
|
||||
}} catch(e) {{}}
|
||||
}} else {{
|
||||
let errMsg = 'Synchronization failed';
|
||||
if (typeof data.detail === 'string') errMsg = data.detail;
|
||||
|
||||
Reference in New Issue
Block a user