888 lines
44 KiB
HTML
888 lines
44 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Intune Manager</title>
|
||
<script>
|
||
// Theme vor erstem Render anwenden — verhindert Flash
|
||
(function () {
|
||
try {
|
||
var saved = localStorage.getItem('iam-theme');
|
||
var theme = saved || (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||
document.documentElement.setAttribute('data-theme', theme);
|
||
} catch (e) { /* localStorage-Block */ }
|
||
})();
|
||
</script>
|
||
<link rel="stylesheet" href="/styles.css">
|
||
<link rel="stylesheet" href="/vendor/flatpickr.min.css">
|
||
<link rel="preconnect" href="https://rsms.me/">
|
||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='22' fill='%23111111'/%3E%3Ctext x='50' y='70' font-size='62' text-anchor='middle' fill='white' font-family='Inter,Arial' font-weight='600'%3EI%3C/text%3E%3C/svg%3E">
|
||
</head>
|
||
<body>
|
||
|
||
<!-- HEADER -->
|
||
<header class="topbar">
|
||
<div class="brand">
|
||
<div id="brandLogo" class="logo"><span class="logo-letter">I</span></div>
|
||
<div class="brand-text">
|
||
<div class="brand-name">Intune Manager</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="header-actions">
|
||
<button id="btnGroupExport" class="icon-btn icon-btn-header" title="Gruppen-Export" aria-label="Gruppen-Export öffnen">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/><polyline points="17 8 19 10 23 6"/></svg>
|
||
</button>
|
||
<button id="btnSettings" class="icon-btn icon-btn-header" title="Einstellungen" aria-label="Einstellungen öffnen">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .34 1.86l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.7 1.7 0 0 0-1.86-.34 1.7 1.7 0 0 0-1.04 1.57V21a2 2 0 0 1-4 0v-.09a1.7 1.7 0 0 0-1.1-1.57 1.7 1.7 0 0 0-1.86.34l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.7 1.7 0 0 0 .34-1.86 1.7 1.7 0 0 0-1.57-1.04H3a2 2 0 0 1 0-4h.09A1.7 1.7 0 0 0 4.66 9a1.7 1.7 0 0 0-.34-1.86l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.7 1.7 0 0 0 1.86.34H9a1.7 1.7 0 0 0 1.04-1.57V3a2 2 0 0 1 4 0v.09c0 .68.42 1.29 1.04 1.57.62.27 1.36.13 1.86-.34l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.7 1.7 0 0 0-.34 1.86V9a1.7 1.7 0 0 0 1.57 1.04H21a2 2 0 0 1 0 4h-.09a1.7 1.7 0 0 0-1.51 1z"/></svg>
|
||
</button>
|
||
<button id="btnHelp" class="icon-btn icon-btn-header" title="Hilfe" aria-label="Hilfe öffnen">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
|
||
</button>
|
||
<button id="btnThemeToggle" class="theme-toggle" role="switch" aria-checked="false" title="Dark/Light Mode umschalten" aria-label="Dark/Light Mode umschalten">
|
||
<span class="theme-track">
|
||
<svg class="theme-ico theme-ico-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>
|
||
<svg class="theme-ico theme-ico-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
|
||
<span class="theme-knob"></span>
|
||
</span>
|
||
</button>
|
||
<div id="connStatus" class="conn-pill conn-off" title="Verbindungsstatus">
|
||
<span class="dot"></span>
|
||
<span class="conn-text">Offline</span>
|
||
</div>
|
||
<button id="btnDisconnect" class="icon-btn icon-btn-header hidden" title="Trennen" aria-label="Verbindung trennen">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- ONBOARDING (MSAL.js Browser-Popup) -->
|
||
<div id="onboarding" class="onboarding hidden">
|
||
<div class="onboarding-card">
|
||
|
||
<!-- View 1: idle -->
|
||
<div data-onboarding-view="idle">
|
||
<div class="onboarding-icon">
|
||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6">
|
||
<path d="M12 2 L4 6 v6 c0 5 3.5 9 8 10 c4.5-1 8-5 8-10 V6 z"/>
|
||
<path d="M9 12 l2 2 l4-4"/>
|
||
</svg>
|
||
</div>
|
||
<h1 class="onboarding-title">Mit Microsoft anmelden</h1>
|
||
<p class="onboarding-text">
|
||
Es öffnet sich ein Microsoft-Anmeldefenster. Wähle den gewünschten Account aus.
|
||
</p>
|
||
<div id="onboardingSetupHint" class="banner-warning hidden">
|
||
Konfiguration unvollständig — bitte zuerst <a href="#" id="onboardingOpenSettings">Einstellungen</a> öffnen und Tenant ID, Client ID und Abteilungs-Präfix setzen.
|
||
</div>
|
||
<button id="btnConnect" class="btn btn-primary btn-lg">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M11.4 2.4H2.4v9h9v-9zm10.2 0h-9v9h9v-9zm-10.2 10.2H2.4v9h9v-9zm10.2 0h-9v9h9v-9z"/></svg>
|
||
Anmeldung starten
|
||
</button>
|
||
<div class="onboarding-meta">
|
||
Lese- und Zuweisungs-Rechte für Intune-Apps und Gruppenmitgliedschaften.
|
||
</div>
|
||
</div>
|
||
|
||
<!-- View 2: waiting -->
|
||
<div data-onboarding-view="awaiting" class="hidden">
|
||
<div class="dc-spinner-wrap">
|
||
<div class="spinner"></div>
|
||
</div>
|
||
<h2 class="onboarding-title">Anmeldung läuft</h2>
|
||
<p class="onboarding-text">
|
||
Bitte im Microsoft-Anmeldefenster fortfahren. Wir holen es automatisch in den Vordergrund —
|
||
falls es trotzdem im Hintergrund liegt, schau in der Taskleiste.
|
||
</p>
|
||
<button id="btnCancelConnect" class="btn btn-text">Abbrechen</button>
|
||
</div>
|
||
|
||
<!-- View 3: error -->
|
||
<div data-onboarding-view="error" class="hidden">
|
||
<div class="onboarding-icon onboarding-icon-error">
|
||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 8v4M12 16h.01"/></svg>
|
||
</div>
|
||
<h2 class="onboarding-title">Anmeldung fehlgeschlagen</h2>
|
||
<p class="onboarding-text" id="dcError">Unbekannter Fehler.</p>
|
||
<button id="btnRetryConnect" class="btn btn-primary btn-lg">Erneut versuchen</button>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MAIN -->
|
||
<main class="layout" id="layout">
|
||
|
||
<!-- LEFT: TARGETS -->
|
||
<section class="panel panel-targets">
|
||
<div class="panel-head">
|
||
<div class="panel-title">
|
||
<h2>Abteilungen & Benutzer</h2>
|
||
<span id="targetCount" class="count-badge">0</span>
|
||
</div>
|
||
<button id="btnReloadTargets" class="icon-btn" title="Neu laden" aria-label="Neu laden">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-3-6.7L21 8"/><path d="M21 3v5h-5"/></svg>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="mode-segment" role="tablist">
|
||
<button class="seg active" data-mode="dept" role="tab">Abteilung</button>
|
||
<button class="seg" data-mode="rpa" role="tab">RPA</button>
|
||
<button class="seg" data-mode="user" role="tab">Benutzer</button>
|
||
</div>
|
||
|
||
<div class="search-wrap">
|
||
<svg class="search-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
|
||
<input type="search" id="targetSearch" placeholder="Filtern..." class="input input-search" autocomplete="off">
|
||
<span id="userSearchHint" class="search-hint hidden">min. 2 Zeichen</span>
|
||
</div>
|
||
|
||
<div class="select-toolbar">
|
||
<div class="select-actions">
|
||
<button id="btnTargetsSelectAll" class="link-btn">Alle</button>
|
||
<span class="dotsep">·</span>
|
||
<button id="btnTargetsClear" class="link-btn">Leeren</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="pinnedTargets" class="pinned-targets hidden" aria-label="Aktuelle Auswahl">
|
||
<!-- populated by JS -->
|
||
</div>
|
||
|
||
<div id="targetList" class="list-area target-list">
|
||
<!-- populated by JS -->
|
||
</div>
|
||
</section>
|
||
|
||
<!-- CENTER: APPS -->
|
||
<section class="panel panel-apps">
|
||
<div class="panel-head">
|
||
<div class="panel-title">
|
||
<h2>Apps</h2>
|
||
<span id="appCount" class="count-badge">0</span>
|
||
</div>
|
||
<div class="panel-head-actions">
|
||
<button id="btnReloadApps" class="icon-btn" title="Neu laden (Cache umgehen)" aria-label="Neu laden">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-3-6.7L21 8"/><path d="M21 3v5h-5"/></svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="apps-toolbar">
|
||
<div class="search-wrap flex-1">
|
||
<svg class="search-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
|
||
<input type="search" id="appSearch" placeholder="Apps durchsuchen..." class="input input-search" autocomplete="off">
|
||
</div>
|
||
<select id="appFilter" class="select">
|
||
<option value="all">Alle Apps</option>
|
||
<option value="none">Ohne Zuweisungen</option>
|
||
<option value="available">Nur Available</option>
|
||
<option value="required">Nur Required</option>
|
||
<option value="multipleAvail">Mehrere Available</option>
|
||
<option value="multipleReq">Mehrere Required</option>
|
||
<option value="allUsers">All Users</option>
|
||
<option value="allDevices">All Devices</option>
|
||
<option value="srcIntune">Nur Intune-Apps</option>
|
||
<!-- Vendor-Optionen werden dynamisch von rebuildVendorFilterOptions() ergaenzt -->
|
||
</select>
|
||
<select id="appCategoryFilter" class="select" title="Nach App-Kategorie filtern">
|
||
<option value="">Alle Kategorien</option>
|
||
<!-- Kategorie-Optionen werden dynamisch von rebuildCategoryFilterOptions() befuellt -->
|
||
</select>
|
||
</div>
|
||
|
||
<div class="filter-chips">
|
||
<label class="check-chip">
|
||
<input type="checkbox" id="chkOnlyMember">
|
||
<span>Bereits Gruppenmitglied</span>
|
||
</label>
|
||
<label class="check-chip">
|
||
<input type="checkbox" id="chkHideNative">
|
||
<span>All Users/Devices ausblenden</span>
|
||
</label>
|
||
<div class="date-filter-chip" id="dateFilterChip">
|
||
<span class="date-filter-label" title="Nur Apps anzeigen die im angegebenen Zeitraum geändert wurden">Geändert</span>
|
||
<input type="date" id="dateModFrom" class="date-input" title="Von (inkl.)">
|
||
<span class="date-sep">–</span>
|
||
<input type="date" id="dateModTo" class="date-input" title="Bis (inkl.)">
|
||
<button type="button" id="dateModClear" class="date-clear" hidden title="Datum-Filter zurücksetzen">×</button>
|
||
</div>
|
||
<div class="legend-group">
|
||
<span class="legend-item" title="Alle ausgewählten Empfänger sind bereits Mitglied"><span class="row-status full">✓</span> bereits zugewiesen</span>
|
||
<span class="legend-item" title="Teilweise Mitglied"><span class="row-status partial">◐</span> teilweise</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="appList" class="list-area apps-list">
|
||
<!-- populated by JS -->
|
||
</div>
|
||
</section>
|
||
|
||
<!-- RIGHT: SESSION -->
|
||
<aside class="panel panel-side">
|
||
<div class="panel-head">
|
||
<div class="panel-title">
|
||
<h2>Geplante Zuweisungen</h2>
|
||
<span id="sessionCount" class="count-badge accent">0</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="side-pane" id="sidePaneSession">
|
||
<div id="sessionList" class="session-list">
|
||
<!-- populated by JS -->
|
||
</div>
|
||
|
||
<div class="session-summary">
|
||
<div class="summary-section">
|
||
<div class="summary-section-title">Ausgewählte Empfänger</div>
|
||
<div class="summary-row sub">
|
||
<span class="muted">Abteilungsgruppen</span>
|
||
<span id="sumGroupsSel">0</span>
|
||
</div>
|
||
<div class="summary-row sub">
|
||
<span class="muted">Einzelbenutzer</span>
|
||
<span id="sumUsersSel">0</span>
|
||
</div>
|
||
</div>
|
||
<div class="summary-section">
|
||
<div class="summary-section-title">Zuweisungen erhalten</div>
|
||
<div class="summary-row sub">
|
||
<span class="muted">Abteilungsgruppen</span>
|
||
<span id="sumGroupsOps">0</span>
|
||
</div>
|
||
<div class="summary-row sub">
|
||
<span class="muted">Einzelbenutzer</span>
|
||
<span id="sumUsersOps">0</span>
|
||
</div>
|
||
</div>
|
||
<div class="summary-row total">
|
||
<span>Gesamt-Vorgänge</span>
|
||
<span id="sumTotal">0</span>
|
||
</div>
|
||
</div>
|
||
|
||
<button id="btnApply" class="btn btn-apply" disabled>
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M5 12l5 5L20 7"/></svg>
|
||
<span id="btnApplyLabel">Zuweisungen ausführen</span>
|
||
</button>
|
||
<button id="btnSessionClear" class="link-btn link-btn-danger" hidden>Geplante Zuweisungen leeren</button>
|
||
</div>
|
||
|
||
<div class="side-pane hidden" id="sidePaneExisting">
|
||
<div class="side-pane-hint">Apps, in denen die ausgewählten Empfänger bereits Mitglied einer zugewiesenen Gruppe sind.</div>
|
||
<div id="existingList" class="existing-list">
|
||
<!-- populated by JS -->
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
</main>
|
||
|
||
<!-- TOAST CONTAINER -->
|
||
<div id="toasts" class="toasts"></div>
|
||
|
||
<!-- LOADING OVERLAY -->
|
||
<div id="loading" class="loading hidden">
|
||
<div class="loading-card">
|
||
<div class="spinner"></div>
|
||
<div id="loadingText" class="loading-text">Bitte warten...</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODAL: Create Required Group -->
|
||
<div id="modalCreateGroup" class="modal hidden">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-box modal-lg">
|
||
<div class="modal-head">
|
||
<h3>Zuweisung hinzufügen</h3>
|
||
<button class="modal-close" data-close aria-label="Schließen">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label class="lbl" for="cgAppName">App</label>
|
||
<input type="text" id="cgAppName" class="input" readonly>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="lbl">Typ der Zuweisung</label>
|
||
<div class="cg-intent-grid">
|
||
|
||
<div class="cg-intent-col cg-intent-required">
|
||
<div class="cg-intent-head">
|
||
<span class="cg-intent-badge req">REQUIRED</span>
|
||
<span class="cg-intent-sub">App wird zwingend installiert</span>
|
||
</div>
|
||
<label class="cg-mode">
|
||
<input type="radio" name="cgMode" value="req-group" checked>
|
||
<span class="cg-mode-body">
|
||
<span class="cg-mode-title">Neue Gruppe</span>
|
||
<span class="cg-mode-desc">Neue Required-Gruppe nach Naming-Schema erstellen und zuweisen.</span>
|
||
</span>
|
||
</label>
|
||
<label class="cg-mode">
|
||
<input type="radio" name="cgMode" value="req-allusers">
|
||
<span class="cg-mode-body">
|
||
<span class="cg-mode-title">All Users</span>
|
||
<span class="cg-mode-desc">Native Required-Zuweisung an alle lizenzierten Benutzer.</span>
|
||
</span>
|
||
</label>
|
||
<label class="cg-mode">
|
||
<input type="radio" name="cgMode" value="req-alldevices">
|
||
<span class="cg-mode-body">
|
||
<span class="cg-mode-title">All Devices</span>
|
||
<span class="cg-mode-desc">Native Required-Zuweisung an alle Geräte des Tenants.</span>
|
||
</span>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="cg-intent-col cg-intent-available">
|
||
<div class="cg-intent-head">
|
||
<span class="cg-intent-badge avail">AVAILABLE</span>
|
||
<span class="cg-intent-sub">Selbstinstallation via Firmenportal</span>
|
||
</div>
|
||
<label class="cg-mode">
|
||
<input type="radio" name="cgMode" value="avail-group">
|
||
<span class="cg-mode-body">
|
||
<span class="cg-mode-title">Neue Gruppe</span>
|
||
<span class="cg-mode-desc">Neue Available-Gruppe nach Naming-Schema erstellen und zuweisen.</span>
|
||
</span>
|
||
</label>
|
||
<label class="cg-mode">
|
||
<input type="radio" name="cgMode" value="avail-allusers">
|
||
<span class="cg-mode-body">
|
||
<span class="cg-mode-title">All Users</span>
|
||
<span class="cg-mode-desc">App erscheint im Firmenportal aller Benutzer.</span>
|
||
</span>
|
||
</label>
|
||
<label class="cg-mode">
|
||
<input type="radio" name="cgMode" value="avail-alldevices">
|
||
<span class="cg-mode-body">
|
||
<span class="cg-mode-title">All Devices</span>
|
||
<span class="cg-mode-desc">App ist auf allen Geräten verfügbar.</span>
|
||
</span>
|
||
</label>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="form-group" id="cgGroupNameGroup">
|
||
<label class="lbl" for="cgGroupName">Gruppen-Name (Vorschlag — kann angepasst werden)</label>
|
||
<input type="text" id="cgGroupName" class="input">
|
||
<div id="cgStatus" class="form-hint"></div>
|
||
</div>
|
||
<label class="check-chip" id="cgAssignToAppWrap">
|
||
<input type="checkbox" id="cgAssignToApp" checked>
|
||
<span>Gruppe direkt der App zuweisen</span>
|
||
</label>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="btn btn-secondary" data-close>Abbrechen</button>
|
||
<button class="btn btn-primary" id="cgCreate">Erstellen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODAL: Apply Result -->
|
||
<div id="modalResult" class="modal hidden">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-box modal-lg">
|
||
<div class="modal-head">
|
||
<h3 id="resHeadline">Ergebnis</h3>
|
||
<button class="modal-close" data-close aria-label="Schließen">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div id="resBanner" class="res-banner"></div>
|
||
|
||
<div class="result-stats">
|
||
<div class="rstat ok"><div class="num" id="resOk">0</div><div class="lbl">Erfolgreich</div></div>
|
||
<div class="rstat warn"><div class="num" id="resSkip">0</div><div class="lbl">Übersprungen</div></div>
|
||
<div class="rstat err"><div class="num" id="resErr">0</div><div class="lbl">Fehler</div></div>
|
||
<div class="rstat tot"><div class="num" id="resTotal">0</div><div class="lbl">Gesamt</div></div>
|
||
</div>
|
||
|
||
<div id="resDetails" class="res-details"></div>
|
||
|
||
<div id="resReportLink" class="report-link"></div>
|
||
|
||
<div id="resDebug" class="res-debug"></div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="btn btn-secondary" data-close>Schließen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODAL: Mitglieder einer Gruppe einsehen / einzelne User uebernehmen -->
|
||
<div id="modalMembers" class="modal hidden">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-box modal-lg">
|
||
<div class="modal-head">
|
||
<h3 id="mmTitle">Mitglieder</h3>
|
||
<button class="modal-close" data-close aria-label="Schließen">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="search-wrap" style="margin: 0 0 12px 0;">
|
||
<svg class="search-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
|
||
<input type="search" id="mmSearch" class="input input-search" placeholder="Mitglieder filtern...">
|
||
</div>
|
||
<div class="mm-toolbar">
|
||
<button class="link-btn" id="mmSelectAll">Alle</button>
|
||
<span class="dotsep">·</span>
|
||
<button class="link-btn" id="mmClear">Leeren</button>
|
||
</div>
|
||
<div id="mmList" class="member-list"></div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<span id="mmInfo" class="muted" style="margin-right: auto;">0 ausgewählt</span>
|
||
<button class="btn btn-secondary" data-close>Abbrechen</button>
|
||
<button class="btn btn-primary" id="mmApply">Als Benutzer-Auswahl übernehmen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODAL: Confirm Apply -->
|
||
<div id="modalConfirm" class="modal hidden">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-box">
|
||
<div class="modal-head">
|
||
<h3>Zuweisungen ausführen?</h3>
|
||
<button class="modal-close" data-close aria-label="Schließen">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div id="confirmText" class="confirm-text"></div>
|
||
<div class="confirm-warn">Hinzufügen ist sofort wirksam — Mitgliedschaften können später manuell entfernt werden.</div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="btn btn-secondary" data-close>Abbrechen</button>
|
||
<button class="btn btn-apply" id="btnConfirmApply">Ja, ausführen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODAL: Hilfe -->
|
||
<div id="modalHelp" class="modal hidden">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-box modal-xl">
|
||
<div class="modal-head">
|
||
<h3>Hilfe & Funktionsverzeichnis</h3>
|
||
<button class="modal-close" data-close aria-label="Schließen">×</button>
|
||
</div>
|
||
<div class="modal-body help-body">
|
||
<aside class="help-toc" id="helpToc">
|
||
<div class="help-toc-title">Inhalt</div>
|
||
<ul></ul>
|
||
</aside>
|
||
<div class="help-content md-content" id="helpContent">
|
||
<div class="help-loading">Hilfe wird geladen…</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<span id="helpVersion" class="help-version-badge"></span>
|
||
<span class="modal-foot-spacer"></span>
|
||
<button class="btn btn-primary" data-close>Schließen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODAL: App umbenennen -->
|
||
<div id="modalRenameApp" class="modal hidden">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-box">
|
||
<div class="modal-head">
|
||
<h3>App umbenennen</h3>
|
||
<button class="modal-close" data-close aria-label="Schließen">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label class="lbl">Aktueller Name</label>
|
||
<div id="raOldName" class="ra-old-name"></div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="lbl" for="raNewName">Neuer Name</label>
|
||
<input type="text" id="raNewName" class="input" maxlength="256" autocomplete="off" spellcheck="false">
|
||
<div id="raError" class="form-hint form-hint-err"></div>
|
||
</div>
|
||
<div class="form-hint">Wirkt sofort in Intune. Vendor-verwaltete Apps (PatchMyPC, Robopack, …) können nicht hier umbenannt werden.</div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="btn btn-secondary" data-close>Abbrechen</button>
|
||
<button class="btn btn-primary" id="raSubmit">Umbenennen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODAL: Setup-Datei aktualisieren -->
|
||
<div id="modalUpdateContent" class="modal hidden">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-box">
|
||
<div class="modal-head">
|
||
<h3>Setup-Datei aktualisieren</h3>
|
||
<button class="modal-close" data-close aria-label="Schließen">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label class="lbl">App</label>
|
||
<div id="ucAppName" class="ra-old-name"></div>
|
||
</div>
|
||
<div class="banner-warning">
|
||
<strong>Achtung:</strong> Dies lädt eine neue Setup-Datei hoch und aktiviert sie als neue Version der App. Alle zugewiesenen Geräte erhalten die neue Version beim nächsten Intune-Sync. Vorgang ist nicht rückgängig zu machen.
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="lbl" for="ucFilePath">Pfad zur neuen Datei (lokal auf dem Server-Rechner)</label>
|
||
<div class="uc-path-row">
|
||
<input type="text" id="ucFilePath" class="input mono" spellcheck="false" autocomplete="off" placeholder="C:\Pakete\app-2.0\app.intunewin">
|
||
<button type="button" class="btn btn-secondary" id="ucBrowse">Durchsuchen…</button>
|
||
</div>
|
||
<div class="form-hint" id="ucFileHint">Phase 1: nur <code>.intunewin</code> (win32LobApp). Der Server liest die Datei direkt von diesem Pfad. <em>Durchsuchen</em> öffnet den Datei-Dialog auf dem Rechner, auf dem das Tool läuft.</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="lbl" for="ucVersion">Neue Versionsnummer (optional)</label>
|
||
<input type="text" id="ucVersion" class="input mono" spellcheck="false" autocomplete="off" placeholder="z.B. 2.0.1">
|
||
</div>
|
||
<label class="check-chip"><input type="checkbox" id="ucConfirm"><span>Ich weiß, dass die ausgerollte Version ersetzt wird</span></label>
|
||
<div id="ucError" class="form-hint form-hint-err"></div>
|
||
<div id="ucProgress" class="content-update-progress hidden"></div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="btn btn-secondary" data-close>Abbrechen</button>
|
||
<button class="btn btn-primary" id="ucSubmit" disabled>Hochladen & aktivieren</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODAL: Einstellungen -->
|
||
<div id="modalSettings" class="modal hidden">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-box modal-lg">
|
||
<div class="modal-head">
|
||
<h3>Einstellungen</h3>
|
||
<button class="modal-close" data-close aria-label="Schließen">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div id="settingsForm" class="settings-form">
|
||
|
||
<div id="setupBanner" class="banner-warning hidden">
|
||
<strong>Erste Konfiguration noetig.</strong>
|
||
Bitte mindestens Tenant ID, Client ID und Abteilungs-Praefix setzen. Danach <em>Verbindung + Lookups testen</em> ganz unten, dann speichern.
|
||
</div>
|
||
|
||
<section class="settings-sec">
|
||
<div class="settings-sec-head">
|
||
<h4>Microsoft Graph Verbindung</h4>
|
||
<div class="settings-sec-sub">Tenant- und App-Registrierung. Änderungen erzwingen einen Disconnect.</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="lbl" for="setTenantId">Tenant ID (GUID)</label>
|
||
<input type="text" id="setTenantId" class="input mono" placeholder="00000000-0000-0000-0000-000000000000" spellcheck="false" autocomplete="off">
|
||
<div class="form-hint">Findest du im Entra Admin Center → Übersicht → Tenant-Informationen.</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="lbl" for="setClientId">Client ID (GUID)</label>
|
||
<input type="text" id="setClientId" class="input mono" placeholder="00000000-0000-0000-0000-000000000000" spellcheck="false" autocomplete="off">
|
||
<div class="form-hint">App-Registrierung im Entra Admin Center. Erforderlich: <em>Allow public client flows</em> = Ja, Redirect URI <code>http://localhost</code>.</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="lbl" for="setScopes">Scopes (eine pro Zeile)</label>
|
||
<textarea id="setScopes" class="input mono" rows="4" spellcheck="false" placeholder="Group.ReadWrite.All GroupMember.ReadWrite.All User.Read.All DeviceManagementApps.Read.All"></textarea>
|
||
<div class="form-hint">Die Standard-Liste passt fuer den vollen Funktionsumfang. Weniger Scopes = weniger Rechte.</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="settings-sec">
|
||
<div class="settings-sec-head">
|
||
<h4>Abteilungs-Gruppen</h4>
|
||
<div class="settings-sec-sub">Ein oder mehrere Präfixe für den Abteilungs-Modus (linke Spalte). Alle Gruppen, deren <code>displayName</code> mit einem dieser Präfixe beginnt, werden geladen.</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="lbl" for="setDeptPrefixes">Präfixe (eine pro Zeile)</label>
|
||
<textarea id="setDeptPrefixes" class="input mono" rows="3" spellcheck="false" placeholder="abt-hm abt-extern"></textarea>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="settings-sec">
|
||
<div class="settings-sec-head">
|
||
<h4>RPA-Gruppen</h4>
|
||
<div class="settings-sec-sub">Genaue <code>displayName</code>-Werte der Gruppen, die im RPA-Tab angezeigt werden. Eine pro Zeile.</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="lbl" for="setRpaGroups">RPA-Gruppennamen</label>
|
||
<textarea id="setRpaGroups" class="input mono" rows="4" spellcheck="false" placeholder="intune-userrole-windowsclientuser-rpa intune-userrole-windowsclientuser-rpa-fbt intune-userrole-windowsclientuser-rpa-pro"></textarea>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="settings-sec">
|
||
<div class="settings-sec-head">
|
||
<h4>Benutzer-Suche</h4>
|
||
<div class="settings-sec-sub">In welchen Feldern bei der Benutzersuche gesucht wird (alle markierten werden mit <code>OR</code> verknüpft).</div>
|
||
</div>
|
||
<div class="form-group form-check-grid">
|
||
<label class="check-chip"><input type="checkbox" id="setUsfDisplayName"><span>Display Name</span></label>
|
||
<label class="check-chip"><input type="checkbox" id="setUsfUpn"><span>UPN</span></label>
|
||
<label class="check-chip"><input type="checkbox" id="setUsfMail"><span>E-Mail</span></label>
|
||
<label class="check-chip"><input type="checkbox" id="setUsfDepartment"><span>Department</span></label>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="settings-sec">
|
||
<div class="settings-sec-head">
|
||
<h4>Required-Gruppen-Naming</h4>
|
||
<div class="settings-sec-sub">Schema für neue Required-Gruppen aus dem App-Manager. Format: <code>{Präfix}{App-Slug}{Suffix}</code>.</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label class="lbl" for="setReqPrefix">Präfix</label>
|
||
<input type="text" id="setReqPrefix" class="input mono" placeholder="intune-win-app-">
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="lbl" for="setReqSuffix">Suffix</label>
|
||
<input type="text" id="setReqSuffix" class="input mono" placeholder="-required">
|
||
</div>
|
||
</div>
|
||
<div class="form-hint">Vorschau: <code id="setReqPreview" class="set-preview"></code></div>
|
||
</section>
|
||
|
||
<section class="settings-sec">
|
||
<div class="settings-sec-head">
|
||
<h4>Available-Gruppen-Naming</h4>
|
||
<div class="settings-sec-sub">Schema für neue Available-Gruppen aus dem App-Manager. Format: <code>{Präfix}{App-Slug}{Suffix}</code>.</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label class="lbl" for="setAvailPrefix">Präfix</label>
|
||
<input type="text" id="setAvailPrefix" class="input mono" placeholder="intune-win-app-">
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="lbl" for="setAvailSuffix">Suffix</label>
|
||
<input type="text" id="setAvailSuffix" class="input mono" placeholder="-available">
|
||
</div>
|
||
</div>
|
||
<div class="form-hint">Vorschau: <code id="setAvailPreview" class="set-preview"></code></div>
|
||
</section>
|
||
|
||
<section class="settings-sec">
|
||
<div class="settings-sec-head">
|
||
<h4>Branding — Logo</h4>
|
||
<div class="settings-sec-sub">Eigenes Logo links oben im Header. Wird im Projekt-Verzeichnis als <code>branding-logo.<ext></code> gespeichert. Max. 2 MB.</div>
|
||
</div>
|
||
<div class="logo-row">
|
||
<div class="logo-preview" id="logoPreview">
|
||
<span class="logo-letter">I</span>
|
||
</div>
|
||
<div class="logo-actions">
|
||
<input type="file" id="logoFile" accept="image/png,image/jpeg,image/svg+xml,image/webp,image/gif" class="hidden">
|
||
<button type="button" class="btn btn-secondary btn-sm" id="btnLogoPick">Bild auswählen…</button>
|
||
<button type="button" class="btn btn-text link-btn-danger" id="btnLogoRemove" hidden>Entfernen</button>
|
||
<div class="form-hint" id="logoHint">PNG, JPG, SVG, WEBP, GIF</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="settings-sec">
|
||
<div class="settings-sec-head settings-sec-head-row">
|
||
<div>
|
||
<h4>Farben</h4>
|
||
<div class="settings-sec-sub">Akzentfarben für Highlights. Soft/Hover/Background-Varianten werden automatisch abgeleitet.</div>
|
||
</div>
|
||
<button type="button" class="btn btn-secondary btn-sm" id="btnRestoreColors" title="Alle Farben auf die Firmen-Standardwerte zurücksetzen">↺ Standardfarben wiederherstellen</button>
|
||
</div>
|
||
<div class="color-grid">
|
||
<div class="color-row" data-color-key="brand">
|
||
<span class="color-swatch" data-swatch="brand"></span>
|
||
<div class="color-meta">
|
||
<div class="color-name">Brand (Firmenfarbe)</div>
|
||
<div class="color-desc">Logo-Hintergrund, Stepper-Indikator</div>
|
||
</div>
|
||
<input type="color" id="setColBrand" class="color-picker">
|
||
<input type="text" id="setColBrandHex" class="input mono color-hex" maxlength="7" spellcheck="false">
|
||
<button type="button" class="link-btn" data-color-reset="brand" title="Auf Standard zurücksetzen">↺</button>
|
||
</div>
|
||
<div class="color-row" data-color-key="accent">
|
||
<span class="color-swatch" data-swatch="accent"></span>
|
||
<div class="color-meta">
|
||
<div class="color-name">Available</div>
|
||
<div class="color-desc">Available-Buttons, Links, Akzent-Hover</div>
|
||
</div>
|
||
<input type="color" id="setColAccent" class="color-picker">
|
||
<input type="text" id="setColAccentHex" class="input mono color-hex" maxlength="7" spellcheck="false">
|
||
<button type="button" class="link-btn" data-color-reset="accent" title="Auf Standard zurücksetzen">↺</button>
|
||
</div>
|
||
<div class="color-row" data-color-key="required">
|
||
<span class="color-swatch" data-swatch="required"></span>
|
||
<div class="color-meta">
|
||
<div class="color-name">Required</div>
|
||
<div class="color-desc">Required-Buttons und -Badges</div>
|
||
</div>
|
||
<input type="color" id="setColRequired" class="color-picker">
|
||
<input type="text" id="setColRequiredHex" class="input mono color-hex" maxlength="7" spellcheck="false">
|
||
<button type="button" class="link-btn" data-color-reset="required">↺</button>
|
||
</div>
|
||
<div class="color-row" data-color-key="success">
|
||
<span class="color-swatch" data-swatch="success"></span>
|
||
<div class="color-meta">
|
||
<div class="color-name">Erfolg / Bereits zugewiesen</div>
|
||
<div class="color-desc">Member-Indikatoren, Erfolgs-Toasts</div>
|
||
</div>
|
||
<input type="color" id="setColSuccess" class="color-picker">
|
||
<input type="text" id="setColSuccessHex" class="input mono color-hex" maxlength="7" spellcheck="false">
|
||
<button type="button" class="link-btn" data-color-reset="success">↺</button>
|
||
</div>
|
||
<div class="color-row" data-color-key="warning">
|
||
<span class="color-swatch" data-swatch="warning"></span>
|
||
<div class="color-meta">
|
||
<div class="color-name">Warnung / Teilweise</div>
|
||
<div class="color-desc">Partial-Member, Skip-Statuses</div>
|
||
</div>
|
||
<input type="color" id="setColWarning" class="color-picker">
|
||
<input type="text" id="setColWarningHex" class="input mono color-hex" maxlength="7" spellcheck="false">
|
||
<button type="button" class="link-btn" data-color-reset="warning">↺</button>
|
||
</div>
|
||
<div class="color-row" data-color-key="error">
|
||
<span class="color-swatch" data-swatch="error"></span>
|
||
<div class="color-meta">
|
||
<div class="color-name">Fehler</div>
|
||
<div class="color-desc">Fehler-Toasts, Delete-Hover, Validation</div>
|
||
</div>
|
||
<input type="color" id="setColError" class="color-picker">
|
||
<input type="text" id="setColErrorHex" class="input mono color-hex" maxlength="7" spellcheck="false">
|
||
<button type="button" class="link-btn" data-color-reset="error">↺</button>
|
||
</div>
|
||
<div class="color-row" data-color-key="rowSelected">
|
||
<span class="color-swatch" data-swatch="rowSelected"></span>
|
||
<div class="color-meta">
|
||
<div class="color-name">Markierte App-Zeile</div>
|
||
<div class="color-desc">Hintergrund der aufgeklappten App-Zeile + linker Akzentstreifen. Schrift/Icons passen sich automatisch dem Kontrast an.</div>
|
||
</div>
|
||
<input type="color" id="setColRowSelected" class="color-picker">
|
||
<input type="text" id="setColRowSelectedHex" class="input mono color-hex" maxlength="7" spellcheck="false">
|
||
<button type="button" class="link-btn" data-color-reset="rowSelected">↺</button>
|
||
</div>
|
||
<div class="color-row" data-color-key="detailPanel">
|
||
<span class="color-swatch" data-swatch="detailPanel"></span>
|
||
<div class="color-meta">
|
||
<div class="color-name">Details-Bereich</div>
|
||
<div class="color-desc">Hintergrund des aufgeklappten Detail-Blocks unter der App. Text-Farbe wird automatisch nach Lesbarkeit gewählt.</div>
|
||
</div>
|
||
<input type="color" id="setColDetailPanel" class="color-picker">
|
||
<input type="text" id="setColDetailPanelHex" class="input mono color-hex" maxlength="7" spellcheck="false">
|
||
<button type="button" class="link-btn" data-color-reset="detailPanel">↺</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="settings-sec">
|
||
<div class="settings-sec-head">
|
||
<h4>Konfiguration pruefen</h4>
|
||
<div class="settings-sec-sub">Prueft die aktuell eingetragenen Werte gegen Microsoft Graph: Abteilungs-Gruppen-Lookup, RPA-Gruppen und Benutzer-Suche. Voraussetzung: Verbindung steht.</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<button type="button" class="btn btn-secondary" id="btnSettingsTest">Verbindung + Lookups testen</button>
|
||
<div id="settingsTestResult" class="settings-test-result"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="settings-sec settings-sec-meta">
|
||
<div class="settings-sec-head">
|
||
<h4>Datei</h4>
|
||
</div>
|
||
<div class="form-hint">Gespeichert in: <code id="setFilePath" class="set-path">…</code></div>
|
||
</section>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="btn btn-text link-btn-danger" id="btnSettingsReset" type="button">Auf Standardwerte zurücksetzen</button>
|
||
<span class="modal-foot-spacer"></span>
|
||
<button class="btn btn-secondary" data-close>Abbrechen</button>
|
||
<button class="btn btn-primary" id="btnSettingsSave">Speichern</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODAL: Gruppen-Export / Benutzer hinzufügen -->
|
||
<div id="modalGroupExport" class="modal hidden">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-box modal-xl">
|
||
<div class="modal-head">
|
||
<h3>Gruppen-Verwaltung</h3>
|
||
<button class="modal-close" data-close aria-label="Schließen">×</button>
|
||
</div>
|
||
<div class="modal-body gex-body">
|
||
|
||
<!-- Tab-Leiste -->
|
||
<div class="gex-tab-bar">
|
||
<button class="gex-tab active" data-tab="export">Mitglieder & Export</button>
|
||
<button class="gex-tab" data-tab="add">Benutzer hinzufügen</button>
|
||
</div>
|
||
|
||
<div class="gex-columns">
|
||
|
||
<!-- Linke Spalte: Gruppensuche (geteilt) -->
|
||
<div class="gex-left">
|
||
<div class="gex-section-title">Gruppe suchen</div>
|
||
<div class="search-wrap" style="margin-bottom:10px;">
|
||
<svg class="search-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
|
||
<input type="search" id="gexSearch" class="input input-search" placeholder="Gruppenname eingeben…" autocomplete="off">
|
||
</div>
|
||
<div id="gexSearchResults" class="gex-results">
|
||
<div class="gex-hint">Mindestens 2 Zeichen eingeben.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Rechte Spalte: Export-Tab -->
|
||
<div class="gex-right" id="gexPaneExport">
|
||
<div class="gex-section-title" id="gexMembersTitle">Mitglieder</div>
|
||
<div class="search-wrap" style="margin-bottom:10px;">
|
||
<svg class="search-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
|
||
<input type="search" id="gexMemberFilter" class="input input-search" placeholder="Mitglieder filtern…" autocomplete="off">
|
||
</div>
|
||
<div id="gexMembersArea" class="gex-members-area">
|
||
<div class="gex-hint">Gruppe auswählen um Mitglieder zu laden.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Rechte Spalte: Benutzer-hinzufügen-Tab -->
|
||
<div class="gex-right hidden" id="gexPaneAdd">
|
||
<div class="gex-section-title">Benutzer suchen</div>
|
||
<div class="search-wrap" style="margin-bottom:10px;">
|
||
<svg class="search-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
|
||
<input type="search" id="gexUserSearch" class="input input-search" placeholder="Name, UPN oder E-Mail…" autocomplete="off">
|
||
</div>
|
||
<div id="gexUserResults" class="gex-results gex-add-results">
|
||
<div class="gex-hint">Mindestens 2 Zeichen eingeben.</div>
|
||
</div>
|
||
<div class="gex-section-title" style="margin-top:12px;">
|
||
Ausgewählt <span id="gexSelectedCount" class="count-badge">0</span>
|
||
</div>
|
||
<div id="gexSelectedUsers" class="gex-selected-area">
|
||
<div class="gex-hint">Noch keine Benutzer ausgewählt.</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<span id="gexStats" class="muted" style="margin-right:auto;"></span>
|
||
<button class="btn btn-secondary" data-close>Schließen</button>
|
||
<button class="btn btn-primary" id="gexExportBtn" disabled>
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||
Als CSV exportieren
|
||
</button>
|
||
<button class="btn btn-primary hidden" id="gexAddBtn" disabled>
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><line x1="19" y1="8" x2="19" y2="14"/><line x1="22" y1="11" x2="16" y2="11"/></svg>
|
||
Zu Gruppe hinzufügen
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Markdown-Renderer (bundled, MIT, marked v15) — wird im App-Detail-Panel
|
||
fuer die Beschreibung benutzt. Vor app.js laden damit window.marked
|
||
beim ersten render() bereitsteht. -->
|
||
<script src="/vendor/marked.min.js"></script>
|
||
<!-- Date-Picker (bundled, MIT, flatpickr v4) — ersetzt den nativen
|
||
date-input Popup, vermeidet Browser-Quirks (schwarzes Initial-Render). -->
|
||
<script src="/vendor/flatpickr.min.js"></script>
|
||
<script src="/app.js"></script>
|
||
</body>
|
||
</html>
|