group remove hinzugefügt

This commit is contained in:
2026-06-19 09:03:49 +02:00
parent cbb6dcec8a
commit 0ff20b9573
4 changed files with 116 additions and 2 deletions
Vendored
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+86
View File
@@ -1094,6 +1094,14 @@ function renderAppRow(app) {
<span>+</span> <span>+</span>
</button>`; </button>`;
const removableReq = app.RequiredGroups.filter(g => !g.IsNative);
const removableAvail = app.AvailableGroups.filter(g => !g.IsNative);
const mkRemoveBtn = (groups, type, label) => groups.length > 0
? `<button class="type-btn type-remove type-remove-${type}" data-remove-group data-remove-type="${type}" data-app-id="${escapeHtml(app.AppId)}" data-app-name="${escapeHtml(app.AppName)}" title="${label}-Gruppe von App entfernen"><span>−${label}</span></button>`
: `<span class="type-btn type-remove type-remove--empty"></span>`;
const removeReqBtn = mkRemoveBtn(removableReq, 'required', 'R');
const removeAvailBtn = mkRemoveBtn(removableAvail, 'available', 'A');
const hasNoAssignments = app.AvailableGroups.length === 0 && app.RequiredGroups.length === 0; const hasNoAssignments = app.AvailableGroups.length === 0 && app.RequiredGroups.length === 0;
// Direkt-Link in das Intune Admin Center fuer diese App (dezentes Icon, NICHT der Name selbst) // Direkt-Link in das Intune Admin Center fuer diese App (dezentes Icon, NICHT der Name selbst)
const intuneIcon = intuneAppLink(app.AppId, app.AppName); const intuneIcon = intuneAppLink(app.AppId, app.AppName);
@@ -1137,6 +1145,8 @@ function renderAppRow(app) {
<div class="col col-avail">${availBtn || '<span class="cell-empty">—</span>'}</div> <div class="col col-avail">${availBtn || '<span class="cell-empty">—</span>'}</div>
<div class="col col-req">${reqBtn || '<span class="cell-empty">—</span>'}</div> <div class="col col-req">${reqBtn || '<span class="cell-empty">—</span>'}</div>
<div class="col col-create">${createBtn}</div> <div class="col col-create">${createBtn}</div>
<div class="col col-remove-req">${removeReqBtn}</div>
<div class="col col-remove-avail">${removeAvailBtn}</div>
<div class="col col-delete">${deleteBtn}</div> <div class="col col-delete">${deleteBtn}</div>
</div> </div>
${detailHtml} ${detailHtml}
@@ -1688,6 +1698,26 @@ function attachAppRowEvents(wrap) {
openCreateGroupModal(btn.dataset.appId, btn.dataset.appName); openCreateGroupModal(btn.dataset.appId, btn.dataset.appName);
}); });
}); });
wrap.querySelectorAll('[data-remove-group]').forEach(btn => {
btn.addEventListener('click', e => {
e.stopPropagation();
const app = State.apps.find(a => a.AppId === btn.dataset.appId);
if (!app) return;
const type = btn.dataset.removeType; // 'required' | 'available'
const src = type === 'required' ? app.RequiredGroups : app.AvailableGroups;
const groups = src.filter(g => !g.IsNative).map(g => ({ ...g, _type: type }));
if (groups.length === 0) return;
if (groups.length === 1) {
removeAssignmentWithConfirm({
appId: app.AppId, appName: app.AppName,
groupId: groups[0].GroupId, groupName: groups[0].GroupName,
type,
});
return;
}
showRemoveGroupPicker(btn, app, groups);
});
});
wrap.querySelectorAll('[data-delete-app]').forEach(btn => { wrap.querySelectorAll('[data-delete-app]').forEach(btn => {
btn.addEventListener('click', e => { btn.addEventListener('click', e => {
e.preventDefault(); e.preventDefault();
@@ -1893,6 +1923,62 @@ function showGroupPicker(anchorEl, app, groups, type) {
}, 0); }, 0);
} }
function showRemoveGroupPicker(anchorEl, app, groups) {
closeGroupPicker();
pickerEl = document.createElement('div');
pickerEl.className = 'group-picker picker-remove';
pickerEl.innerHTML = `
<div class="picker-head">Gruppe entfernen — ${escapeHtml(app.AppName)}</div>
<div class="picker-list">
${groups.map(g => {
const badge = g._type === 'required'
? `<span class="asg-badge req" style="font-size:10px;padding:1px 5px">Req</span>`
: `<span class="asg-badge avail" style="font-size:10px;padding:1px 5px">Avail</span>`;
return `
<div class="picker-row">
<button class="picker-item picker-remove-item"
data-group-id="${escapeHtml(g.GroupId)}"
data-group-name="${escapeHtml(g.GroupName)}"
data-type="${escapeHtml(g._type)}">
${badge}
<span class="picker-name">${escapeHtml(g.GroupName)}</span>
</button>
</div>`;
}).join('')}
</div>
`;
document.body.appendChild(pickerEl);
const r = anchorEl.getBoundingClientRect();
const pw = pickerEl.offsetWidth;
const ph = pickerEl.offsetHeight;
let left = r.left;
let top = r.bottom + 4;
if (left + pw > window.innerWidth - 8) left = window.innerWidth - pw - 8;
if (top + ph > window.innerHeight - 8) top = r.top - ph - 4;
pickerEl.style.left = left + 'px';
pickerEl.style.top = top + 'px';
pickerEl.querySelectorAll('.picker-remove-item').forEach(item => {
item.addEventListener('click', e => {
e.stopPropagation();
const ctx = {
appId: app.AppId, appName: app.AppName,
groupId: item.dataset.groupId, groupName: item.dataset.groupName,
type: item.dataset.type,
};
closeGroupPicker();
removeAssignmentWithConfirm(ctx);
});
});
setTimeout(() => {
document.addEventListener('click', onPickerOutsideClick, true);
document.addEventListener('keydown', onPickerEscape);
}, 0);
}
// ============================================================= // =============================================================
// Membership // Membership
// ============================================================= // =============================================================
+29 -1
View File
@@ -1330,6 +1330,8 @@ html[data-theme="dark"] .entra-link.teams-link:hover {
98px /* available */ 98px /* available */
98px /* required */ 98px /* required */
26px /* + */ 26px /* + */
32px /* −R */
32px /* −A */
26px; /* delete */ 26px; /* delete */
column-gap: 8px; column-gap: 8px;
padding: 6px 12px; padding: 6px 12px;
@@ -1611,6 +1613,8 @@ html[data-theme="dark"] .entra-link.teams-link:hover {
.col-avail { justify-content: stretch; } .col-avail { justify-content: stretch; }
.col-req { justify-content: stretch; } .col-req { justify-content: stretch; }
.col-create { justify-content: stretch; } .col-create { justify-content: stretch; }
.col-remove-req { justify-content: stretch; }
.col-remove-avail { justify-content: stretch; }
/* Buttons fuellen ihre Spalte komplett — saubere vertikale Linien. /* Buttons fuellen ihre Spalte komplett — saubere vertikale Linien.
Available/Required: Label klebt links, Count-Badge rechts, damit die Available/Required: Label klebt links, Count-Badge rechts, damit die
@@ -1621,7 +1625,11 @@ html[data-theme="dark"] .entra-link.teams-link:hover {
justify-content: flex-start; justify-content: flex-start;
padding: 0 10px; padding: 0 10px;
} }
.col-create .type-btn { .col-create .type-btn,
.col-remove-req .type-btn,
.col-remove-req .type-remove,
.col-remove-avail .type-btn,
.col-remove-avail .type-remove {
width: 100%; width: 100%;
justify-content: center; justify-content: center;
padding: 0; padding: 0;
@@ -1891,6 +1899,26 @@ a.app-name:hover {
border-color: var(--ink); border-color: var(--ink);
border-style: solid; border-style: solid;
} }
.type-remove {
width: 100%;
padding: 0;
font-size: 11px;
font-weight: 600;
color: var(--muted);
border-style: dashed;
justify-content: center;
letter-spacing: -0.5px;
}
.type-remove:hover:not(:disabled) {
color: var(--danger, #e55);
border-color: var(--danger, #e55);
border-style: solid;
}
.type-remove--empty {
visibility: hidden;
pointer-events: none;
border: none;
}
/* Anzahl-Hinweis (z.B. "Available 3" wenn 3 Available-Gruppen existieren) */ /* Anzahl-Hinweis (z.B. "Available 3" wenn 3 Available-Gruppen existieren) */
.type-btn-count { .type-btn-count {