diverse anpassungen

This commit is contained in:
2026-06-17 09:41:43 +01:00
parent eeabb1db31
commit c860ea9a10
9 changed files with 429 additions and 41 deletions
+157 -2
View File
@@ -1,5 +1,5 @@
// =============================================================
// Intune App-Manager — Frontend
// Intune Manager — Frontend
// Workflow: Connect → Select Targets → Pick Apps → Apply
// =============================================================
@@ -4188,6 +4188,9 @@ const GexState = {
selectedGroup: null,
members: null,
memberFilter: '',
activeTab: 'export',
userSearchResults: [],
selectedUsers: new Map(),
};
function openGroupExportModal() {
@@ -4196,6 +4199,9 @@ function openGroupExportModal() {
GexState.selectedGroup = null;
GexState.members = null;
GexState.memberFilter = '';
GexState.activeTab = 'export';
GexState.userSearchResults = [];
GexState.selectedUsers = new Map();
document.getElementById('gexSearch').value = '';
document.getElementById('gexMemberFilter').value = '';
document.getElementById('gexSearchResults').innerHTML = '<div class="gex-hint">Mindestens 2 Zeichen eingeben.</div>';
@@ -4203,10 +4209,33 @@ function openGroupExportModal() {
document.getElementById('gexMembersTitle').textContent = 'Mitglieder';
document.getElementById('gexStats').textContent = '';
document.getElementById('gexExportBtn').disabled = true;
// Add-Tab zurücksetzen
document.getElementById('gexUserSearch').value = '';
document.getElementById('gexUserResults').innerHTML = '<div class="gex-hint">Mindestens 2 Zeichen eingeben.</div>';
document.getElementById('gexSelectedUsers').innerHTML = '<div class="gex-hint">Noch keine Benutzer ausgewählt.</div>';
document.getElementById('gexSelectedCount').textContent = '0';
document.getElementById('gexAddBtn').disabled = true;
// Tab-Anzeige auf Export zurücksetzen
_gexSwitchTab('export');
document.getElementById('modalGroupExport').classList.remove('hidden');
setTimeout(() => document.getElementById('gexSearch').focus(), 80);
}
function _gexSwitchTab(tab) {
GexState.activeTab = tab;
document.querySelectorAll('.gex-tab').forEach(b => b.classList.toggle('active', b.dataset.tab === tab));
document.getElementById('gexPaneExport').classList.toggle('hidden', tab !== 'export');
document.getElementById('gexPaneAdd').classList.toggle('hidden', tab !== 'add');
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);
}
document.querySelectorAll('.gex-tab').forEach(btn => {
btn.addEventListener('click', () => _gexSwitchTab(btn.dataset.tab));
});
document.getElementById('btnGroupExport')?.addEventListener('click', openGroupExportModal);
// Suche mit Debounce
@@ -4257,8 +4286,12 @@ async function _gexSelectGroup(id, name) {
GexState.selectedGroup = { Id: id, DisplayName: name };
GexState.members = null;
GexState.memberFilter = '';
document.getElementById('gexMemberFilter').value = '';
_gexRenderResults(); // aktualisiert selected-Highlight
_gexUpdateAddBtn();
if (GexState.activeTab !== 'export') return;
document.getElementById('gexMemberFilter').value = '';
document.getElementById('gexMembersTitle').textContent = `Mitglieder — ${name}`;
document.getElementById('gexStats').textContent = '';
document.getElementById('gexExportBtn').disabled = true;
@@ -4340,6 +4373,128 @@ function _gexRenderMembers() {
if (GexState.memberFilter) _gexApplyFilter();
}
// ---- Benutzer-Suche im Add-Tab ----
let _gexUserSearchTimer = null;
document.getElementById('gexUserSearch')?.addEventListener('input', e => {
clearTimeout(_gexUserSearchTimer);
const q = e.target.value.trim();
if (q.length < 2) {
GexState.userSearchResults = [];
document.getElementById('gexUserResults').innerHTML = '<div class="gex-hint">Mindestens 2 Zeichen eingeben.</div>';
return;
}
document.getElementById('gexUserResults').innerHTML = '<div class="gex-loading"><div class="spinner" style="width:14px;height:14px;border-width:2px;"></div><span>Suche…</span></div>';
_gexUserSearchTimer = setTimeout(() => _gexDoUserSearch(q), 350);
});
async function _gexDoUserSearch(q) {
try {
const data = await api(`/api/users?q=${encodeURIComponent(q)}`);
GexState.userSearchResults = data.items || [];
_gexRenderUserResults();
} catch (e) {
document.getElementById('gexUserResults').innerHTML = `<div class="gex-error">Suche fehlgeschlagen: ${escapeHtml(e.message)}</div>`;
}
}
function _gexRenderUserResults() {
const el = document.getElementById('gexUserResults');
if (!el) return;
const items = GexState.userSearchResults;
if (items.length === 0) {
el.innerHTML = '<div class="gex-empty">Keine Benutzer gefunden.</div>';
return;
}
el.innerHTML = items.map(u => {
const already = GexState.selectedUsers.has(u.Id);
return `<div class="gex-user-result-item ${already ? 'selected' : ''}"
data-id="${escapeHtml(u.Id)}" data-name="${escapeHtml(u.DisplayName || '')}" data-upn="${escapeHtml(u.UserPrincipalName || '')}">
<span class="gex-user-name">${escapeHtml(u.DisplayName || '—')}</span>
<span class="gex-user-upn">${escapeHtml(u.UserPrincipalName || u.Mail || '')}</span>
${already ? '<span class="gex-user-added">✓</span>' : ''}
</div>`;
}).join('');
el.querySelectorAll('.gex-user-result-item:not(.selected)').forEach(item => {
item.addEventListener('click', () => _gexSelectUser({ Id: item.dataset.id, DisplayName: item.dataset.name, UserPrincipalName: item.dataset.upn }));
});
}
function _gexSelectUser(u) {
if (GexState.selectedUsers.has(u.Id)) return;
GexState.selectedUsers.set(u.Id, u);
_gexRenderUserResults();
_gexRenderSelectedUsers();
_gexUpdateAddBtn();
}
function _gexDeselectUser(id) {
GexState.selectedUsers.delete(id);
_gexRenderUserResults();
_gexRenderSelectedUsers();
_gexUpdateAddBtn();
}
function _gexRenderSelectedUsers() {
const el = document.getElementById('gexSelectedUsers');
const countEl = document.getElementById('gexSelectedCount');
if (!el) return;
const users = [...GexState.selectedUsers.values()];
countEl.textContent = users.length;
if (users.length === 0) {
el.innerHTML = '<div class="gex-hint">Noch keine Benutzer ausgewählt.</div>';
return;
}
el.innerHTML = users.map(u => `
<div class="gex-selected-chip">
<span class="gex-selected-chip-name">${escapeHtml(u.DisplayName || '—')}</span>
<span class="gex-selected-chip-upn">${escapeHtml(u.UserPrincipalName || '')}</span>
<button class="gex-selected-chip-remove" data-id="${escapeHtml(u.Id)}" title="Entfernen">×</button>
</div>
`).join('');
el.querySelectorAll('.gex-selected-chip-remove').forEach(btn => {
btn.addEventListener('click', () => _gexDeselectUser(btn.dataset.id));
});
}
function _gexUpdateAddBtn() {
const btn = document.getElementById('gexAddBtn');
if (!btn) return;
btn.disabled = !(GexState.selectedGroup && GexState.selectedUsers.size > 0);
}
document.getElementById('gexAddBtn')?.addEventListener('click', async () => {
if (!GexState.selectedGroup) { toast('Bitte zuerst eine Gruppe auswählen.', 'warn'); return; }
if (GexState.selectedUsers.size === 0) { toast('Bitte mindestens einen Benutzer auswählen.', 'warn'); return; }
const btn = document.getElementById('gexAddBtn');
btn.disabled = true;
const origHtml = btn.innerHTML;
btn.innerHTML = '<div class="spinner" style="width:14px;height:14px;border-width:2px;display:inline-block;vertical-align:middle;margin-right:6px;"></div>Wird hinzugefügt…';
const userIds = [...GexState.selectedUsers.keys()];
try {
const data = await api(`/api/groups/${encodeURIComponent(GexState.selectedGroup.Id)}/members`, {
method: 'POST',
body: { userIds },
});
if (data.errors > 0) {
toast(`${data.success} von ${data.total} hinzugefügt, ${data.errors} Fehler.`, 'warn');
} else {
toast(`${data.success} Benutzer zu „${GexState.selectedGroup.DisplayName}" hinzugefügt.`, 'ok');
GexState.selectedUsers.clear();
_gexRenderSelectedUsers();
document.getElementById('gexUserSearch').value = '';
document.getElementById('gexUserResults').innerHTML = '<div class="gex-hint">Mindestens 2 Zeichen eingeben.</div>';
}
} catch (e) {
toast(`Fehler: ${e.message}`, 'err');
} finally {
btn.innerHTML = origHtml;
_gexUpdateAddBtn();
}
});
document.getElementById('gexExportBtn')?.addEventListener('click', () => {
if (!GexState.members?.users) return;
const m = GexState.members;