diverse anpassungen
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user