v0.1.33 — Offboarding-Report und Update-Hinweis
Build & Release MSI / build-msi (push) Canceled after 0s
Build & Release MSI / build-msi (push) Canceled after 0s
- Offboarding: HTML-Report pro Geraet (New-OffboardHtmlReport) + reportUrl; Dialog-Ergebnis pro Geraet gruppiert + Report-Link - Update-Check: GET /api/update/check (Gitea releases/latest, Compare-SemVer); Kopfzeilen-Badge bei neuer Version, Link zur Release-Seite; optionaler read-only Token Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+42
-2
@@ -4784,6 +4784,24 @@ document.getElementById('btnRestoreColors')?.addEventListener('click', () => {
|
||||
// Init
|
||||
// =============================================================
|
||||
|
||||
// Prueft (nicht-blockierend) auf eine neuere Release-Version und zeigt bei Bedarf
|
||||
// das Update-Badge in der Kopfzeile mit Link zur Release-Seite.
|
||||
async function checkForUpdate() {
|
||||
try {
|
||||
const res = await api('/api/update/check');
|
||||
if (res && res.updateAvailable && res.releaseUrl) {
|
||||
const badge = document.getElementById('updateBadge');
|
||||
const txt = document.getElementById('updateBadgeText');
|
||||
if (badge && txt) {
|
||||
txt.textContent = `Update ${res.latest}`;
|
||||
badge.href = res.releaseUrl;
|
||||
badge.title = `Neue Version ${res.latest} verfügbar (aktuell ${res.current}) — Release-Seite öffnen`;
|
||||
badge.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
} catch (e) { /* still: Update-Check ist optional */ }
|
||||
}
|
||||
|
||||
(async function init() {
|
||||
renderConnection();
|
||||
renderTargets();
|
||||
@@ -4807,6 +4825,7 @@ document.getElementById('btnRestoreColors')?.addEventListener('click', () => {
|
||||
}
|
||||
await refreshStatus();
|
||||
refreshTenantSwitcher();
|
||||
checkForUpdate(); // nicht-blockierend
|
||||
if (State.connected) autoLoadAfterConnect();
|
||||
// First-Run: wenn kritische Felder leer sind, Settings-Modal automatisch oeffnen.
|
||||
if (setupNeeded) {
|
||||
@@ -7012,9 +7031,30 @@ async function offExecute() {
|
||||
try {
|
||||
const res = await api('/api/offboard/execute', { method: 'POST', body: { devices: payload, services }, timeoutMs: 180000 });
|
||||
const results = res.results || [];
|
||||
// Ergebnis pro Gerät gruppieren
|
||||
const byDev = new Map();
|
||||
for (const r of results) {
|
||||
if (!byDev.has(r.deviceName)) byDev.set(r.deviceName, []);
|
||||
byDev.get(r.deviceName).push(r);
|
||||
}
|
||||
const devHtml = [...byDev.entries()].map(([name, rows]) => {
|
||||
const errs = rows.filter(r => !r.success).length;
|
||||
const lines = rows.map(r =>
|
||||
`<div class="off-result ${r.success ? 'ok' : 'fail'}">${r.success ? '✓' : '✗'} ${escapeHtml(r.service)} ${escapeHtml(r.action)}${r.error ? ': ' + escapeHtml(r.error) : ''}</div>`
|
||||
).join('');
|
||||
return `<div class="off-result-dev">
|
||||
<div class="off-result-devname">${escapeHtml(name || '—')}${errs ? ` <span class="off-result-badge">${errs} Fehler</span>` : ''}</div>
|
||||
${lines}
|
||||
</div>`;
|
||||
}).join('');
|
||||
const reportLink = res.reportUrl
|
||||
? `<a href="${escapeHtml(res.reportUrl)}" target="_blank" rel="noopener" class="btn btn-secondary btn-sm" style="margin-top:12px;display:inline-flex;align-items:center;gap:6px;">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><line x1="10" y1="9" x2="8" y2="9"/></svg>
|
||||
HTML-Report öffnen
|
||||
</a>`
|
||||
: '';
|
||||
document.getElementById('offResults').innerHTML =
|
||||
`<div class="lbl">Ergebnis (${res.successCount}/${res.total})</div>` +
|
||||
results.map(r => `<div class="off-result ${r.success ? 'ok' : 'fail'}">${r.success ? '✓' : '✗'} ${escapeHtml(r.deviceName)} — ${escapeHtml(r.service)} ${escapeHtml(r.action)}${r.error ? ': ' + escapeHtml(r.error) : ''}</div>`).join('');
|
||||
`<div class="lbl">Ergebnis (${res.successCount}/${res.total})</div>${devHtml}${reportLink}`;
|
||||
toast(`${res.successCount}/${res.total} Aktionen erfolgreich.`, res.successCount === res.total ? 'ok' : 'warn', 'Offboarding');
|
||||
// Erfolgreich vollständig offgeboardete Geräte aus der Liste nehmen.
|
||||
const doneNames = new Set(results.filter(r => r.success).map(r => r.deviceName));
|
||||
|
||||
@@ -41,6 +41,10 @@
|
||||
</nav>
|
||||
|
||||
<div class="header-actions">
|
||||
<a id="updateBadge" class="update-badge hidden" href="#" target="_blank" rel="noopener" title="Update verfügbar — Release-Seite öffnen">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"/><polyline points="21 3 21 9 15 9"/></svg>
|
||||
<span id="updateBadgeText">Update</span>
|
||||
</a>
|
||||
<button id="btnSettings" class="icon-btn icon-btn-header" title="Einstellungen" aria-label="Einstellungen öffnen">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .34 1.86l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.7 1.7 0 0 0-1.86-.34 1.7 1.7 0 0 0-1.04 1.57V21a2 2 0 0 1-4 0v-.09a1.7 1.7 0 0 0-1.1-1.57 1.7 1.7 0 0 0-1.86.34l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.7 1.7 0 0 0 .34-1.86 1.7 1.7 0 0 0-1.57-1.04H3a2 2 0 0 1 0-4h.09A1.7 1.7 0 0 0 4.66 9a1.7 1.7 0 0 0-.34-1.86l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.7 1.7 0 0 0 1.86.34H9a1.7 1.7 0 0 0 1.04-1.57V3a2 2 0 0 1 4 0v.09c0 .68.42 1.29 1.04 1.57.62.27 1.36.13 1.86-.34l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.7 1.7 0 0 0-.34 1.86V9a1.7 1.7 0 0 0 1.57 1.04H21a2 2 0 0 1 0 4h-.09a1.7 1.7 0 0 0-1.51 1z"/></svg>
|
||||
</button>
|
||||
@@ -1070,6 +1074,9 @@
|
||||
.off-result { font-size:13px; padding:3px 0; overflow-wrap:anywhere; }
|
||||
.off-result.ok { color:var(--success); }
|
||||
.off-result.fail { color:var(--error); }
|
||||
.off-result-dev { border:1px solid var(--hairline-soft, var(--hairline)); border-radius:8px; padding:8px 12px; margin-bottom:8px; }
|
||||
.off-result-devname { font-weight:600; font-size:13px; margin-bottom:4px; display:flex; align-items:center; gap:8px; }
|
||||
.off-result-badge { font-size:10.5px; font-weight:700; padding:1px 8px; border-radius:999px; background:rgba(245,80,80,0.18); color:var(--error); }
|
||||
#offResults .lbl { display:block; font-size:12px; color:var(--body); margin:10px 0 4px; }
|
||||
</style>
|
||||
|
||||
|
||||
@@ -335,6 +335,24 @@ h1, h2, h3 {
|
||||
|
||||
.header-actions { display: flex; align-items: center; gap: 12px; }
|
||||
|
||||
/* Update-Hinweis-Badge (neue Version verfuegbar) */
|
||||
.update-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 5px 11px;
|
||||
border-radius: 999px;
|
||||
background: var(--accent-soft);
|
||||
color: var(--brand-accent);
|
||||
border: 1px solid var(--brand-accent);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
transition: filter var(--tx, 0.15s ease-out);
|
||||
}
|
||||
.update-badge:hover { filter: brightness(0.96); }
|
||||
|
||||
/* Theme-Toggle — Slide-Switch */
|
||||
.theme-toggle {
|
||||
appearance: none;
|
||||
|
||||
Reference in New Issue
Block a user