Group Management: Benutzer-Tab mit Gruppen-Mitgliedschaften

Neuer Tab "Benutzer" im Group Management: Benutzer suchen und alle
Gruppen anzeigen, in denen dieser Mitglied ist (transitiveMemberOf).
Gruppen filterbar, mit Typ-Badge (Sicherheitsgruppe/M365/Verteiler).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-08-12 10:03:49 +02:00
co-authored by Claude Sonnet 4.6
parent 2f0af6e577
commit ec130031cb
4 changed files with 179 additions and 1 deletions
+103
View File
@@ -4372,10 +4372,13 @@ function _gexSwitchTab(tab) {
document.getElementById('gexPaneExport').classList.toggle('hidden', tab !== 'export');
document.getElementById('gexPaneAdd').classList.toggle('hidden', tab !== 'add');
document.getElementById('gexPaneImport').classList.toggle('hidden', tab !== 'import');
document.getElementById('gexPaneUser').classList.toggle('hidden', tab !== 'user');
document.getElementById('gexLeft').classList.toggle('hidden', tab === 'user');
document.getElementById('gexExportBtn').classList.toggle('hidden', tab !== 'export');
document.getElementById('gexAddBtn').classList.toggle('hidden', tab !== 'add');
document.getElementById('gexStats').textContent = '';
if (tab === 'add') setTimeout(() => document.getElementById('gexUserSearch').focus(), 80);
if (tab === 'user') setTimeout(() => document.getElementById('gexUmSearch').focus(), 80);
}
document.querySelectorAll('.gex-tab').forEach(btn => {
@@ -4603,6 +4606,106 @@ document.getElementById('gexRemoveBtn')?.addEventListener('click', async () => {
}
});
// ---- Benutzer → Gruppen (User-Tab) ----
(function () {
let umDebounce = null;
let umSelected = null; // { Id, DisplayName, UserPrincipalName }
const searchInput = document.getElementById('gexUmSearch');
const resultsDiv = document.getElementById('gexUmResults');
const groupsList = document.getElementById('gexUmGroupsList');
const groupsTitle = document.getElementById('gexUmGroupsTitle');
const groupCount = document.getElementById('gexUmGroupCount');
const filterWrap = document.getElementById('gexUmFilterWrap');
const filterInput = document.getElementById('gexUmFilter');
searchInput.addEventListener('input', () => {
clearTimeout(umDebounce);
const q = searchInput.value.trim();
if (q.length < 2) {
resultsDiv.innerHTML = '<div class="gex-hint">Mindestens 2 Zeichen eingeben.</div>';
return;
}
umDebounce = setTimeout(() => _umSearchUser(q), 320);
});
async function _umSearchUser(q) {
resultsDiv.innerHTML = '<div class="gex-hint">Suche…</div>';
try {
const data = await api(`/api/users?q=${encodeURIComponent(q)}`);
if (!data.items || data.items.length === 0) {
resultsDiv.innerHTML = '<div class="gex-hint">Keine Benutzer gefunden.</div>';
return;
}
let html = '';
for (const u of data.items) {
const upn = u.UserPrincipalName || u.Mail || u.Id;
html += `<div class="gex-result-item" data-uid="${escapeHtml(u.Id)}"
data-name="${escapeHtml(u.DisplayName || upn)}"
data-upn="${escapeHtml(upn)}">
<span class="gex-user-name">${escapeHtml(u.DisplayName || '—')}</span>
<span class="gex-user-upn">${escapeHtml(upn)}</span>
</div>`;
}
resultsDiv.innerHTML = html;
resultsDiv.querySelectorAll('.gex-result-item').forEach(el => {
el.addEventListener('click', () => _umSelectUser({
Id: el.dataset.uid,
DisplayName: el.dataset.name,
UserPrincipalName: el.dataset.upn
}));
});
} catch (e) {
resultsDiv.innerHTML = `<div class="gex-hint error">Fehler: ${escapeHtml(String(e))}</div>`;
}
}
async function _umSelectUser(user) {
umSelected = user;
searchInput.value = user.DisplayName || user.UserPrincipalName;
resultsDiv.innerHTML = '';
groupsList.innerHTML = '<div class="gex-hint">Lade Gruppen…</div>';
groupsTitle.style.display = '';
filterWrap.style.display = '';
filterInput.value = '';
groupCount.textContent = '…';
try {
const data = await api(`/api/users/${encodeURIComponent(user.Id)}/memberof`);
groupCount.textContent = data.count;
if (!data.groups || data.groups.length === 0) {
groupsList.innerHTML = '<div class="gex-hint">Keine Gruppen gefunden.</div>';
filterWrap.style.display = 'none';
return;
}
_umRenderGroups(data.groups);
} catch (e) {
groupsList.innerHTML = `<div class="gex-hint error">Fehler: ${escapeHtml(String(e))}</div>`;
}
}
function _umRenderGroups(groups) {
let html = '';
for (const g of groups) {
const desc = g.Description ? `<span class="gex-user-upn">${escapeHtml(g.Description)}</span>` : '';
const badge = `<span class="gex-type-badge">${escapeHtml(g.Type)}</span>`;
html += `<div class="gex-group-row" data-search="${escapeHtml((g.DisplayName || '').toLowerCase())} ${escapeHtml((g.Description || '').toLowerCase())}">
<span class="gex-user-name">${escapeHtml(g.DisplayName || '—')}</span>
${desc}
${badge}
</div>`;
}
groupsList.innerHTML = html;
}
filterInput.addEventListener('input', () => {
const q = filterInput.value.toLowerCase();
groupsList.querySelectorAll('.gex-group-row').forEach(row => {
row.classList.toggle('hidden-filter', q.length > 0 && !row.dataset.search.includes(q));
});
});
})();
// ---- Benutzer-Suche im Add-Tab ----
let _gexUserSearchTimer = null;