Install anzeige hinzugefügt
This commit is contained in:
@@ -1261,6 +1261,12 @@ function renderAppDetailPanel(appId) {
|
||||
sections.push(renderAssignmentsSection(app));
|
||||
}
|
||||
|
||||
// Installationen — direkt nach Zuweisungen, damit die operative Kennzahl
|
||||
// sichtbar ist ohne scrollen zu muessen.
|
||||
if (d.InstallSummary) {
|
||||
sections.push(renderInstallStatsSection(d.InstallSummary));
|
||||
}
|
||||
|
||||
// Allgemein
|
||||
const general = [
|
||||
row('Hersteller', d.Publisher),
|
||||
@@ -1330,11 +1336,6 @@ function renderAppDetailPanel(appId) {
|
||||
].filter(Boolean).join('');
|
||||
if (metaRows) sections.push(`<div class="app-detail-section"><div class="app-detail-section-title">Meta</div>${metaRows}</div>`);
|
||||
|
||||
// Installationen — Stats aus installSummary (Erfolg/Fehler je User/Device)
|
||||
if (d.InstallSummary) {
|
||||
sections.push(renderInstallStatsSection(d.InstallSummary));
|
||||
}
|
||||
|
||||
// Dependencies — Voraussetzungen dieser App
|
||||
if (d.Dependencies && d.Dependencies.length) {
|
||||
sections.push(renderRelationshipFlow({
|
||||
@@ -1404,8 +1405,6 @@ function renderAppDetailPanel(appId) {
|
||||
// =============================================================
|
||||
|
||||
function renderInstallStatsSection(s) {
|
||||
// Berechnet einen kleinen Erfolgs-Quotient (Installed / (Installed + Failed))
|
||||
// pro Achse. Wenn keine Vorgaenge: zeige nur Counter ohne %-Wert.
|
||||
const pct = (ok, fail) => {
|
||||
const total = ok + fail;
|
||||
if (total === 0) return null;
|
||||
@@ -1417,52 +1416,65 @@ function renderInstallStatsSection(s) {
|
||||
<div class="stat-lbl">${escapeHtml(label)}</div>
|
||||
</div>`;
|
||||
|
||||
// Stacked-Balken: Segmente in Reihenfolge ok / err / pend / not / na
|
||||
const stackedBar = (ok, fail, pend, not, na) => {
|
||||
const total = ok + fail + pend + not + na;
|
||||
if (total === 0) return `<div class="install-bar"><div class="install-bar-seg install-bar-na" style="width:100%" title="Keine Daten"></div></div>`;
|
||||
const w = (v) => `${Math.max(0, Math.round((v / total) * 1000) / 10)}%`;
|
||||
return `<div class="install-bar" title="${ok} inst. / ${fail} Fehler / ${pend} ausst. / ${not} nicht inst. / ${na} N/A (gesamt ${total})">
|
||||
${ok ? `<div class="install-bar-seg install-bar-ok" style="width:${w(ok)}" title="${ok} installiert"></div>` : ''}
|
||||
${fail ? `<div class="install-bar-seg install-bar-err" style="width:${w(fail)}" title="${fail} Fehler"></div>` : ''}
|
||||
${pend ? `<div class="install-bar-seg install-bar-pend" style="width:${w(pend)}" title="${pend} ausstehend"></div>` : ''}
|
||||
${not ? `<div class="install-bar-seg install-bar-not" style="width:${w(not)}" title="${not} nicht inst."></div>` : ''}
|
||||
${na ? `<div class="install-bar-seg install-bar-na" style="width:${w(na)}" title="${na} N/A"></div>` : ''}
|
||||
</div>`;
|
||||
};
|
||||
|
||||
const dev = {
|
||||
ok: s.InstalledDeviceCount,
|
||||
fail: s.FailedDeviceCount,
|
||||
pend: s.PendingInstallDeviceCount,
|
||||
not: s.NotInstalledDeviceCount,
|
||||
na: s.NotApplicableDeviceCount,
|
||||
ok: s.InstalledDeviceCount || 0,
|
||||
fail: s.FailedDeviceCount || 0,
|
||||
pend: s.PendingInstallDeviceCount || 0,
|
||||
not: s.NotInstalledDeviceCount || 0,
|
||||
na: s.NotApplicableDeviceCount || 0,
|
||||
};
|
||||
const usr = {
|
||||
ok: s.InstalledUserCount,
|
||||
fail: s.FailedUserCount,
|
||||
pend: s.PendingInstallUserCount,
|
||||
not: s.NotInstalledUserCount,
|
||||
na: s.NotApplicableUserCount,
|
||||
ok: s.InstalledUserCount || 0,
|
||||
fail: s.FailedUserCount || 0,
|
||||
pend: s.PendingInstallUserCount || 0,
|
||||
not: s.NotInstalledUserCount || 0,
|
||||
na: s.NotApplicableUserCount || 0,
|
||||
};
|
||||
const devPct = pct(dev.ok, dev.fail);
|
||||
const usrPct = pct(usr.ok, usr.fail);
|
||||
|
||||
const axis = (title, d, ratePct) => `
|
||||
<div class="install-axis">
|
||||
<div class="install-axis-head">
|
||||
<span class="install-axis-title">${escapeHtml(title)}</span>
|
||||
${ratePct !== null ? `<span class="install-axis-rate ${ratePct >= 95 ? 'good' : ratePct >= 75 ? 'mid' : 'bad'}">${ratePct}% erfolgreich</span>` : ''}
|
||||
</div>
|
||||
${stackedBar(d.ok, d.fail, d.pend, d.not, d.na)}
|
||||
<div class="install-bar-legend">
|
||||
<span class="ibl ibl-ok">Installiert</span>
|
||||
<span class="ibl ibl-err">Fehler</span>
|
||||
<span class="ibl ibl-pend">Ausstehend</span>
|
||||
<span class="ibl ibl-not">Nicht inst.</span>
|
||||
<span class="ibl ibl-na">N/A</span>
|
||||
</div>
|
||||
<div class="install-stats-grid">
|
||||
${stat('Installiert', d.ok, 'ok')}
|
||||
${stat('Fehler', d.fail, 'err')}
|
||||
${stat('Ausstehend', d.pend, 'pend')}
|
||||
${stat('Nicht inst.', d.not, 'not')}
|
||||
${stat('N/A', d.na, 'na')}
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
return `<div class="app-detail-section app-detail-section--wide">
|
||||
<div class="app-detail-section-title">Installationen</div>
|
||||
<div class="app-detail-section-title">Installationsübersicht</div>
|
||||
<div class="install-stats">
|
||||
<div class="install-axis">
|
||||
<div class="install-axis-head">
|
||||
<span class="install-axis-title">Geräte</span>
|
||||
${devPct !== null ? `<span class="install-axis-rate ${devPct >= 95 ? 'good' : devPct >= 75 ? 'mid' : 'bad'}">${devPct}% erfolgreich</span>` : ''}
|
||||
</div>
|
||||
<div class="install-stats-grid">
|
||||
${stat('Installiert', dev.ok, 'ok')}
|
||||
${stat('Fehler', dev.fail, 'err')}
|
||||
${stat('Ausstehend', dev.pend, 'pend')}
|
||||
${stat('Nicht inst.', dev.not, 'not')}
|
||||
${stat('N/A', dev.na, 'na')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="install-axis">
|
||||
<div class="install-axis-head">
|
||||
<span class="install-axis-title">Benutzer</span>
|
||||
${usrPct !== null ? `<span class="install-axis-rate ${usrPct >= 95 ? 'good' : usrPct >= 75 ? 'mid' : 'bad'}">${usrPct}% erfolgreich</span>` : ''}
|
||||
</div>
|
||||
<div class="install-stats-grid">
|
||||
${stat('Installiert', usr.ok, 'ok')}
|
||||
${stat('Fehler', usr.fail, 'err')}
|
||||
${stat('Ausstehend', usr.pend, 'pend')}
|
||||
${stat('Nicht inst.', usr.not, 'not')}
|
||||
${stat('N/A', usr.na, 'na')}
|
||||
</div>
|
||||
</div>
|
||||
${axis('Geräte', dev, devPct)}
|
||||
${axis('Benutzer', usr, usrPct)}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -4058,6 +4058,50 @@ body.members-page {
|
||||
.stat-not .stat-num { color: var(--muted); }
|
||||
.stat-na .stat-num { color: var(--muted-soft); }
|
||||
|
||||
/* Stacked-Balken */
|
||||
.install-bar {
|
||||
display: flex;
|
||||
height: 10px;
|
||||
border-radius: var(--r-pill);
|
||||
overflow: hidden;
|
||||
background: var(--hairline);
|
||||
margin-bottom: 6px;
|
||||
gap: 1px;
|
||||
}
|
||||
.install-bar-seg { height: 100%; min-width: 2px; transition: opacity .15s; }
|
||||
.install-bar-seg:hover { opacity: .75; }
|
||||
.install-bar-ok { background: var(--success); }
|
||||
.install-bar-err { background: var(--error); }
|
||||
.install-bar-pend { background: var(--warning); }
|
||||
.install-bar-not { background: var(--muted); }
|
||||
.install-bar-na { background: var(--hairline-soft); }
|
||||
|
||||
/* Balken-Legende */
|
||||
.install-bar-legend {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.ibl {
|
||||
font-size: 10.5px;
|
||||
color: var(--muted);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.ibl::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 9px; height: 9px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.ibl-ok::before { background: var(--success); }
|
||||
.ibl-err::before { background: var(--error); }
|
||||
.ibl-pend::before { background: var(--warning); }
|
||||
.ibl-not::before { background: var(--muted); }
|
||||
.ibl-na::before { background: var(--hairline-soft); border: 1px solid var(--hairline); }
|
||||
|
||||
/* =============================================================
|
||||
App-Detail — Flow-Visualisierung (Dependencies + Supersedence)
|
||||
============================================================= */
|
||||
|
||||
Reference in New Issue
Block a user