Geräte-Tab: Wipe-Aktion mit Benutzerdaten-Option, Suchfeld vergrößert

Wipe-Button mit eigenem Modal: Checkbox ob Benutzerdaten behalten werden
(keepUserData). Suchfeld auf max-width 600px vergrößert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-08-12 14:34:38 +02:00
co-authored by Claude Sonnet 4.6
parent f94141dbe0
commit 337709b685
4 changed files with 80 additions and 5 deletions
+5 -1
View File
@@ -1622,7 +1622,7 @@ function Invoke-DeviceActionEndpoint {
if ($err) { return $err } if ($err) { return $err }
$action = [string]$Body.action $action = [string]$Body.action
$allowed = @('syncDevice','rebootNow','remoteLock','collectDiagnostics','rotateBitLockerKeys','retire','autopilotReset') $allowed = @('syncDevice','rebootNow','remoteLock','collectDiagnostics','rotateBitLockerKeys','retire','autopilotReset','wipe')
if ($action -notin $allowed) { if ($action -notin $allowed) {
return @{ statusCode = 400; error = "Unbekannte Aktion: $action" } return @{ statusCode = 400; error = "Unbekannte Aktion: $action" }
} }
@@ -1630,6 +1630,10 @@ function Invoke-DeviceActionEndpoint {
if ($action -eq 'autopilotReset') { if ($action -eq 'autopilotReset') {
$uri = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/$DeviceId/wipe" $uri = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/$DeviceId/wipe"
$bodyJson = '{"keepEnrollmentData":true,"keepUserData":false}' $bodyJson = '{"keepEnrollmentData":true,"keepUserData":false}'
} elseif ($action -eq 'wipe') {
$keepUserData = if ($Body.keepUserData) { 'true' } else { 'false' }
$uri = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/$DeviceId/wipe"
$bodyJson = "{""keepEnrollmentData"":false,""keepUserData"":$keepUserData}"
} else { } else {
$uri = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/$DeviceId/$action" $uri = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/$DeviceId/$action"
$bodyJson = '{}' $bodyJson = '{}'
+49 -3
View File
@@ -5395,6 +5395,25 @@ function devRenderDetail(d) {
if (autopilotBtn) autopilotBtn.style.display = isWin ? '' : 'none'; if (autopilotBtn) autopilotBtn.style.display = isWin ? '' : 'none';
} }
// Wipe-Dialog (Promise: true=keepUserData, false=löschen, null=abgebrochen)
function devWipeDialog(deviceName) {
return new Promise(resolve => {
document.getElementById('wipeDeviceName').textContent = deviceName;
document.getElementById('wipeKeepUserData').checked = false;
const backdrop = document.getElementById('wipeModalBackdrop');
backdrop.classList.remove('hidden');
const close = (result) => {
backdrop.classList.add('hidden');
resolve(result);
};
document.getElementById('wipeCancel').onclick = () => close(null);
document.getElementById('wipeModalClose').onclick = () => close(null);
document.getElementById('wipeConfirm').onclick = () => close(document.getElementById('wipeKeepUserData').checked);
backdrop.onclick = (e) => { if (e.target === backdrop) close(null); };
});
}
// Aktion ausführen // Aktion ausführen
document.getElementById('devActions')?.addEventListener('click', async (e) => { document.getElementById('devActions')?.addEventListener('click', async (e) => {
const btn = e.target.closest('.dev-action-btn'); const btn = e.target.closest('.dev-action-btn');
@@ -5409,10 +5428,38 @@ document.getElementById('devActions')?.addEventListener('click', async (e) => {
collectDiagnostics: 'Diagnose sammeln', collectDiagnostics: 'Diagnose sammeln',
rotateBitLockerKeys: 'BitLocker-Schlüssel rotieren', rotateBitLockerKeys: 'BitLocker-Schlüssel rotieren',
autopilotReset: 'Autopilot Reset', autopilotReset: 'Autopilot Reset',
retire: 'Retire (MDM entfernen)' retire: 'Retire (MDM entfernen)',
wipe: 'Wipe'
}; };
const isDangerous = action === 'retire' || action === 'autopilotReset';
const devName = DevState.selected.DeviceName || DevState.selected.Id; const devName = DevState.selected.DeviceName || DevState.selected.Id;
// Wipe: eigener Dialog mit Benutzerdaten-Option
if (action === 'wipe') {
const keepUserData = await devWipeDialog(devName);
if (keepUserData === null) return;
btn.disabled = true;
const origText2 = btn.textContent.trim();
btn.textContent = '…';
try {
await api(`/api/devices/${encodeURIComponent(DevState.selected.Id)}/action`, {
method: 'POST',
body: JSON.stringify({ action: 'wipe', keepUserData })
});
toast(`Wipe gesendet${keepUserData ? ' (Benutzerdaten behalten)' : ''}`, 'ok');
DevState.items = DevState.items.filter(d => d.Id !== DevState.selected.Id);
DevState.selected = null;
document.getElementById('devDetail').classList.add('hidden');
devApplyFilters();
} catch (e) {
toast(`Fehler bei Wipe: ${e}`, 'error');
} finally {
btn.disabled = false;
btn.textContent = origText2;
}
return;
}
const isDangerous = action === 'retire' || action === 'autopilotReset';
const dangerMsg = { const dangerMsg = {
retire: `ACHTUNG: "Retire" auf "${devName}" ausführen?\n\nDas MDM-Profil wird entfernt — Gerät wird nicht mehr verwaltet.`, retire: `ACHTUNG: "Retire" auf "${devName}" ausführen?\n\nDas MDM-Profil wird entfernt — Gerät wird nicht mehr verwaltet.`,
autopilotReset: `ACHTUNG: "Autopilot Reset" auf "${devName}" ausführen?\n\nDas Gerät wird auf OOBE zurückgesetzt. Enrollment-Daten bleiben erhalten, Benutzerdaten werden gelöscht.` autopilotReset: `ACHTUNG: "Autopilot Reset" auf "${devName}" ausführen?\n\nDas Gerät wird auf OOBE zurückgesetzt. Enrollment-Daten bleiben erhalten, Benutzerdaten werden gelöscht.`
@@ -5431,7 +5478,6 @@ document.getElementById('devActions')?.addEventListener('click', async (e) => {
}); });
toast(`${labels[action]}: Aktion wurde gesendet`, 'ok'); toast(`${labels[action]}: Aktion wurde gesendet`, 'ok');
if (action === 'retire') { if (action === 'retire') {
// Gerät aus Liste entfernen
DevState.items = DevState.items.filter(d => d.Id !== DevState.selected.Id); DevState.items = DevState.items.filter(d => d.Id !== DevState.selected.Id);
DevState.selected = null; DevState.selected = null;
document.getElementById('devDetail').classList.add('hidden'); document.getElementById('devDetail').classList.add('hidden');
+25
View File
@@ -548,12 +548,37 @@
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4h6v2"/></svg> <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4h6v2"/></svg>
Retire Retire
</button> </button>
<button class="btn btn-danger btn-sm dev-action-btn" data-action="wipe" title="Gerät vollständig zurücksetzen (Wipe)">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/></svg>
Wipe
</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<!-- MODAL: Wipe -->
<div class="modal-backdrop hidden" id="wipeModalBackdrop">
<div class="modal" style="max-width:420px;">
<div class="modal-header">
<span class="modal-title">Gerät zurücksetzen (Wipe)</span>
<button class="modal-close" id="wipeModalClose">&times;</button>
</div>
<div class="modal-body" style="padding:16px 20px;">
<p style="margin:0 0 12px;font-size:13.5px;">ACHTUNG: Das Gerät <strong id="wipeDeviceName"></strong> wird vollständig zurückgesetzt und aus dem Enrollment entfernt.</p>
<label style="display:flex;align-items:center;gap:8px;font-size:13px;cursor:pointer;">
<input type="checkbox" id="wipeKeepUserData">
Benutzerdaten behalten (nur bei Windows Fresh Start)
</label>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" id="wipeCancel">Abbrechen</button>
<button class="btn btn-danger" id="wipeConfirm">Jetzt zurücksetzen</button>
</div>
</div>
</div>
<!-- TOAST CONTAINER --> <!-- TOAST CONTAINER -->
<div id="toasts" class="toasts"></div> <div id="toasts" class="toasts"></div>
+1 -1
View File
@@ -5214,7 +5214,7 @@ body.read-only .app-row {
border-bottom: 1px solid var(--hairline-soft); border-bottom: 1px solid var(--hairline-soft);
flex-shrink: 0; flex-shrink: 0;
} }
.dev-toolbar .search-wrap { flex: 1; max-width: 380px; } .dev-toolbar .search-wrap { flex: 1; max-width: 600px; }
.input-select { .input-select {
padding: 5px 8px; padding: 5px 8px;
border: 1px solid var(--hairline); border: 1px solid var(--hairline);