Policy-Import haerten, Admin-Vorlagen, Zuweisung nach Import, Git-Snapshot

Neu:
- Administrative Vorlagen (ADMX / groupPolicyConfigurations) als vierter
  Policy-Typ fuer Export/Import (GET /api/policies/administrativetemplate)
- Zuweisung direkt nach Import: pro Policy Include-/Exclude-Gruppen
  (POST /api/policies/assign, typ-spezifische /assign-Action)
- Git-Snapshot: versioniertes Policy-Backup in lokalen Git-Ordner
  (Settings-Sektion policyBackup)
- Import akzeptiert Git-Snapshot-Dateien (policyType neben exportType)

Behoben:
- Settings-Catalog-Import schema-konform: Collection-Properties immer als
  Array (repariert PS-5.1-Roundtrip), null -> [], Wrapper-@odata.type,
  read-only id entfernt
- Import-Phantom-Fehler (@($null)-Geisterdurchlauf im Archiv-Pfad)
- Frontend-Cache-Reset beim Tenant-Wechsel (resetClientState)
- WAM-Anmeldefenster zuverlaessig im Vordergrund (AttachThreadInput)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-23 11:25:19 +02:00
co-authored by Claude Opus 4.8
parent 450428e7f7
commit 4efc71376b
7 changed files with 790 additions and 31 deletions
+70
View File
@@ -572,6 +572,7 @@
<option value="settingscatalog">Settings Catalog</option>
<option value="compliance">Compliance</option>
<option value="configuration">Konfigurationsprofil</option>
<option value="administrativetemplate">Administrative Vorlage</option>
</select>
<span class="pol-toolbar-spacer" style="flex:1;"></span>
<button class="btn btn-secondary btn-sm" id="btnPolRefresh" title="Policies neu laden">
@@ -583,6 +584,10 @@
Import…
</button>
<input type="file" id="polImportFile" accept=".json,application/json" multiple style="display:none;">
<button class="btn btn-secondary btn-sm" id="btnPolGitSnapshot" title="Alle Policies als Snapshot in den konfigurierten Git-Ordner schreiben und committen">
<svg width="13" height="13" 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"/><line x1="12" y1="3" x2="12" y2="9"/><line x1="12" y1="15" x2="12" y2="21"/><circle cx="12" cy="3" r="1"/><circle cx="12" cy="21" r="1"/></svg>
Git-Snapshot
</button>
<button class="btn btn-primary btn-sm" id="btnPolExport" disabled title="Ausgewählte Policies als JSON exportieren">
<svg width="13" height="13" 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>
Export (<span id="polSelCount">0</span>)
@@ -808,6 +813,52 @@
</div>
</div>
<!-- MODAL: Policy-Zuweisung nach Import -->
<div id="modalPolAssign" class="modal hidden">
<div class="modal-backdrop" data-close></div>
<div class="modal-box modal-lg">
<div class="modal-head">
<h3>Importierte Policies zuweisen</h3>
<button class="modal-close" data-close aria-label="Schließen">×</button>
</div>
<div class="modal-body">
<div class="form-hint" style="margin-bottom:12px;">
Optional: jeder importierten Policy Include- und/oder Exclude-Gruppen im aktuellen Tenant zuweisen.
Felder leer lassen = keine Zuweisung. Original-Zuweisungen des Quell-Tenants werden nicht übernommen.
</div>
<div id="polAssignList"></div>
</div>
<div class="modal-foot">
<span id="polAssignInfo" class="muted" style="margin-right:auto;"></span>
<button class="btn btn-secondary" data-close>Überspringen</button>
<button class="btn btn-primary" id="polAssignApply">Zuweisen</button>
</div>
</div>
</div>
<style>
.pol-assign-row { border:1px solid var(--border, #d9dce3); border-radius:8px; padding:12px 14px; margin-bottom:12px; }
.pol-assign-head { display:flex; align-items:center; gap:8px; margin-bottom:10px; }
.pol-assign-head .pol-name { font-weight:600; }
.pol-assign-pickers { display:flex; gap:16px; flex-wrap:wrap; }
.pol-assign-picker { flex:1; min-width:220px; }
.pol-assign-picker > .lbl { display:block; font-size:12px; margin-bottom:4px; opacity:.85; }
.pol-assign-picker[data-kind="exclude"] > .lbl { color:#cb2a7a; }
.pol-assign-chips { display:flex; flex-wrap:wrap; gap:6px; margin-bottom:6px; }
.pol-assign-chip { display:inline-flex; align-items:center; gap:6px; padding:2px 8px; border-radius:12px;
background:var(--chip-bg, #eef0f5); font-size:12px; }
.pol-assign-chip.exclude { background:#fbe3ef; }
.pol-assign-chip button { border:none; background:none; cursor:pointer; font-size:14px; line-height:1; padding:0; opacity:.6; }
.pol-assign-chip button:hover { opacity:1; }
.pol-assign-search-wrap { position:relative; }
.pol-assign-dropdown { position:absolute; z-index:20; left:0; right:0; top:100%; margin-top:2px;
background:var(--panel, #fff); border:1px solid var(--border, #d9dce3); border-radius:6px;
max-height:200px; overflow-y:auto; box-shadow:0 6px 18px rgba(0,0,0,.12); }
.pol-assign-dropdown .opt { padding:6px 10px; cursor:pointer; font-size:13px; }
.pol-assign-dropdown .opt:hover { background:var(--chip-bg, #eef0f5); }
.pol-assign-dropdown .opt .desc { font-size:11px; opacity:.6; }
.pol-assign-dropdown .msg { padding:6px 10px; font-size:12px; opacity:.6; }
</style>
<!-- MODAL: Hilfe -->
<div id="modalHelp" class="modal hidden">
<div class="modal-backdrop" data-close></div>
@@ -1033,6 +1084,25 @@
<div class="form-hint">Vorschau: <code id="setAvailPreview" class="set-preview"></code></div>
</section>
<section class="settings-sec">
<div class="settings-sec-head">
<h4>Policy-Backup (Git)</h4>
<div class="settings-sec-sub">Lokaler Git-Ordner für Policy-Snapshots. Der „Git-Snapshot"-Button im Policies-Tab schreibt alle Policies als JSON dorthin und committet sie (optional Push).</div>
</div>
<div class="form-group">
<label class="lbl" for="setPolicyGitPath">Git-Repo-Ordner (lokal)</label>
<div class="form-row" style="gap:8px;align-items:center;">
<input type="text" id="setPolicyGitPath" class="input mono" placeholder="z. B. C:\Intune-Backup" spellcheck="false" autocomplete="off" style="flex:1;">
<button type="button" class="btn btn-secondary btn-sm" id="btnPickPolicyGit">Ordner…</button>
</div>
<div class="form-hint">Wird bei Bedarf angelegt und als Git-Repo initialisiert. Leer = Funktion deaktiviert.</div>
</div>
<div class="form-group">
<label class="check-chip"><input type="checkbox" id="setPolicyGitPush"><span>Nach dem Commit automatisch <code>git push</code></span></label>
<div class="form-hint">Nutzt deinen vorhandenen Git-Credential-Helper (Remote muss im Repo konfiguriert sein). Kein Token in der App.</div>
</div>
</section>
<section class="settings-sec">
<div class="settings-sec-head">
<h4>Branding — Logo</h4>