diff --git a/.DS_Store b/.DS_Store
index bda96e1..f234f68 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/Active-Directory/.DS_Store b/Active-Directory/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/Active-Directory/.DS_Store differ
diff --git a/Intune/.DS_Store b/Intune/.DS_Store
new file mode 100644
index 0000000..c797a86
Binary files /dev/null and b/Intune/.DS_Store differ
diff --git a/Intune/Intune-App-Manager-Web/www/app.js b/Intune/Intune-App-Manager-Web/www/app.js
index dbb1662..0bd0828 100644
--- a/Intune/Intune-App-Manager-Web/www/app.js
+++ b/Intune/Intune-App-Manager-Web/www/app.js
@@ -1094,6 +1094,14 @@ function renderAppRow(app) {
+
`;
+ const removableReq = app.RequiredGroups.filter(g => !g.IsNative);
+ const removableAvail = app.AvailableGroups.filter(g => !g.IsNative);
+ const mkRemoveBtn = (groups, type, label) => groups.length > 0
+ ? ``
+ : ``;
+ const removeReqBtn = mkRemoveBtn(removableReq, 'required', 'R');
+ const removeAvailBtn = mkRemoveBtn(removableAvail, 'available', 'A');
+
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)
const intuneIcon = intuneAppLink(app.AppId, app.AppName);
@@ -1137,6 +1145,8 @@ function renderAppRow(app) {
${availBtn || '—'}
${reqBtn || '—'}
${createBtn}
+ ${removeReqBtn}
+ ${removeAvailBtn}
${deleteBtn}
${detailHtml}
@@ -1688,6 +1698,26 @@ function attachAppRowEvents(wrap) {
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 => {
btn.addEventListener('click', e => {
e.preventDefault();
@@ -1893,6 +1923,62 @@ function showGroupPicker(anchorEl, app, groups, type) {
}, 0);
}
+function showRemoveGroupPicker(anchorEl, app, groups) {
+ closeGroupPicker();
+
+ pickerEl = document.createElement('div');
+ pickerEl.className = 'group-picker picker-remove';
+ pickerEl.innerHTML = `
+ Gruppe entfernen — ${escapeHtml(app.AppName)}
+
+ ${groups.map(g => {
+ const badge = g._type === 'required'
+ ? `
Req`
+ : `
Avail`;
+ return `
+
+
+
`;
+ }).join('')}
+
+ `;
+ 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
// =============================================================
diff --git a/Intune/Intune-App-Manager-Web/www/styles.css b/Intune/Intune-App-Manager-Web/www/styles.css
index 57bf071..afe42d3 100644
--- a/Intune/Intune-App-Manager-Web/www/styles.css
+++ b/Intune/Intune-App-Manager-Web/www/styles.css
@@ -1330,6 +1330,8 @@ html[data-theme="dark"] .entra-link.teams-link:hover {
98px /* available */
98px /* required */
26px /* + */
+ 32px /* −R */
+ 32px /* −A */
26px; /* delete */
column-gap: 8px;
padding: 6px 12px;
@@ -1610,7 +1612,9 @@ html[data-theme="dark"] .entra-link.teams-link:hover {
.col-version { justify-content: flex-start; }
.col-avail { 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.
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;
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%;
justify-content: center;
padding: 0;
@@ -1891,6 +1899,26 @@ a.app-name:hover {
border-color: var(--ink);
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) */
.type-btn-count {