5 Commits
Author SHA1 Message Date
marcoandClaude Opus 4.8 af301925e9 v0.1.35 — Settings-Catalog-Import-Fix und Update-Check hinter Proxy
Build & Release MSI / build-msi (push) Canceled after 0s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-15 17:50:27 +02:00
marcoandClaude Opus 4.8 0ae20211c7 Settings-Catalog-Import: choiceSettingCollectionValue als Array erzwingen
choiceSettingCollectionValue fehlte in der Array-Key-Liste von
Repair-SettingsCatalogArrays. Beim PS-5.1-JSON-Roundtrip wurde ein
Ein-Element-choiceSettingCollectionValue zum Einzelobjekt entpackt, worauf
Graph den Import mit 400 "Property choiceSettingCollectionValue in payload has
a value that does not match schema" ablehnte (z. B. Default Defender Antivirus
Policy). Jetzt analog zu group-/simpleSettingCollectionValue behandelt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-15 17:49:30 +02:00
marcoandClaude Opus 4.8 de32f6201c Update-Check: Proxy-Authentifizierung unterstützen
Hinter authentifizierten Unternehmens-Proxys (NTLM/Kerberos) scheiterte der
Update-Check mit HTTP 407, weil Invoke-RestMethod keine Anmeldeinformationen
an den Proxy sendet. Jetzt werden die angemeldeten Windows-Credentials an den
System-Proxy durchgereicht; optionaler expliziter Proxy via settings.json ->
update.proxy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-14 10:59:02 +02:00
marcoandClaude Opus 4.8 e110763e6a v0.1.34 — Verwaltungsart-Spalte und Autopilot-Zeitstempel
Build & Release MSI / build-msi (push) Canceled after 0s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-14 09:49:43 +02:00
marcoandClaude Opus 4.8 dfc1128f31 Geräte-Tab: Verwaltungsart-Spalte und Autopilot-Zeitstempel
- Neue Spalte "Verwaltung" (Intune vs. Co-Managed) in der Geräteliste,
  abgeleitet aus managementAgent; farbige Badges, sortierbar, im CSV-Export
  und im Detail-Panel.
- Detail-Panel zeigt Autopilot-Daten (Registriert, Profil zugewiesen am,
  letzter Kontakt) per Lookup über die Seriennummer. Ein echtes Importdatum
  liefert Graph nicht; verfügbar sind nur diese Zeitstempel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-14 07:49:14 +02:00
8 changed files with 108 additions and 9 deletions
+22
View File
@@ -5,6 +5,28 @@ Versionierung folgt [Semantic Versioning](https://semver.org/lang/de/) — solan
--- ---
## [0.1.35] - 2026-09-15
Settings-Catalog-Import-Fix und Update-Check hinter Proxy.
### Behoben
- **Settings-Catalog-Import** (400 „Property choiceSettingCollectionValue … does not match schema"): `choiceSettingCollectionValue` fehlte in der Array-Normalisierung. Beim JSON-Roundtrip unter PowerShell 5.1 wurde ein Ein-Element-`choiceSettingCollectionValue` zum Einzelobjekt entpackt und von Graph abgelehnt (z. B. „Default Defender Antivirus Policy"). Wird jetzt wie `group-`/`simpleSettingCollectionValue` behandelt.
- **Update-Check hinter authentifiziertem Proxy** (HTTP 407): Der Versions-Check schlug hinter Unternehmens-Proxys (NTLM/Kerberos) still fehl, weil `Invoke-RestMethod` keine Anmeldeinformationen an den Proxy sendete. Die angemeldeten Windows-Credentials werden jetzt an den System-Proxy durchgereicht; optionaler expliziter Proxy über `settings.json → update.proxy`.
---
## [0.1.34] - 2026-09-14
Verwaltungsart in der Geräteliste, Autopilot-Zeitstempel im Detail.
### Neu
- **Spalte „Verwaltung"** in der Geräteliste: zeigt je Gerät, ob es **Intune** (reines MDM) oder **Co-Managed** (ConfigMgr + Intune) ist — abgeleitet aus `managementAgent`. Farbige Badges, sortierbar, auch im CSV-Export und im Detail-Panel.
- **Autopilot-Infos im Geräte-Detail**: Beim Auswählen eines Windows-Geräts wird die Autopilot-Identity per Seriennummer nachgeladen und zeigt **Registrierungsstatus**, **Profil zugewiesen am** (`deploymentProfileAssignedDateTime`) und **letzter Autopilot-Kontakt** (`lastContactedDateTime`). Hinweis: Ein exaktes Autopilot-Importdatum liefert Microsoft Graph nicht; angezeigt werden die verfügbaren Zeitstempel. Kein Performance-Einfluss auf die Liste (Lookup nur pro ausgewähltem Gerät), keine neue Berechtigung nötig.
---
## [0.1.33] - 2026-09-13 ## [0.1.33] - 2026-09-13
Offboarding-Report und Update-Hinweis. Offboarding-Report und Update-Hinweis.
+1 -1
View File
@@ -189,7 +189,7 @@ $script:State = [pscustomobject]@{
Session = @() # geplante Zuweisungen Session = @() # geplante Zuweisungen
} }
$script:ToolVersion = "0.1.33" $script:ToolVersion = "0.1.35"
$script:BuildStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $script:BuildStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Host "" Write-Host ""
+1 -1
View File
@@ -12,7 +12,7 @@ if (Test-Path "$x64Dotnet\dotnet.exe") {
$env:DOTNET_ROOT = "C:\Program Files\dotnet" $env:DOTNET_ROOT = "C:\Program Files\dotnet"
} }
$version = "0.1.33" $version = "0.1.35"
$src = "\\Mac\Home\ClaudePRJ\Intune Manager" $src = "\\Mac\Home\ClaudePRJ\Intune Manager"
$stage = "$env:TEMP\IntuneManagerStage" $stage = "$env:TEMP\IntuneManagerStage"
$installerDir = "$src\installer" $installerDir = "$src\installer"
+65 -4
View File
@@ -183,8 +183,26 @@ function Get-UpdateCheckEndpoint {
if ($token) { $headers['Authorization'] = "token $token" } if ($token) { $headers['Authorization'] = "token $token" }
try { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } catch {} try { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } catch {}
# Proxy: Hinter authentifizierten Unternehmens-Proxys (NTLM/Kerberos) scheitert
# Invoke-RestMethod sonst mit "407 Proxyauthentifizierung erforderlich", weil
# standardmaessig keine Anmeldeinformationen an den Proxy gesendet werden. Die
# angemeldeten Windows-Credentials an den (System-)Proxy durchreichen. Optional
# laesst sich ein expliziter Proxy ueber Settings.update.proxy setzen.
$proxyArgs = @{}
try { try {
$rel = Invoke-RestMethod -Uri $api -Headers $headers -Method GET -TimeoutSec 8 $proxyUrl = ''
if ($script:Settings.update -and $script:Settings.update.proxy) { $proxyUrl = [string]$script:Settings.update.proxy }
if ($proxyUrl) {
$proxyArgs['Proxy'] = $proxyUrl
$proxyArgs['ProxyUseDefaultCredentials'] = $true
} elseif ([System.Net.WebRequest]::DefaultWebProxy) {
[System.Net.WebRequest]::DefaultWebProxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
}
} catch {}
try {
$rel = Invoke-RestMethod -Uri $api -Headers $headers -Method GET -TimeoutSec 8 @proxyArgs
$latest = ([string]$rel.tag_name).TrimStart('v','V') $latest = ([string]$rel.tag_name).TrimStart('v','V')
$out.latest = $latest $out.latest = $latest
$out.releaseUrl = [string]$rel.html_url $out.releaseUrl = [string]$rel.html_url
@@ -1710,7 +1728,7 @@ function Get-GroupDevicesExportEndpoint {
return @{ ok = $true; groupId = $GroupId; groupName = $groupName; userCount = 0; items = @(); count = 0; resolvedMs = [int]$sw.ElapsedMilliseconds } return @{ ok = $true; groupId = $GroupId; groupName = $groupName; userCount = 0; items = @(); count = 0; resolvedMs = [int]$sw.ElapsedMilliseconds }
} }
$select = 'id,deviceName,userDisplayName,userPrincipalName,operatingSystem,osVersion,complianceState,serialNumber,model,manufacturer,lastSyncDateTime,enrolledDateTime' $select = 'id,deviceName,userDisplayName,userPrincipalName,operatingSystem,osVersion,complianceState,serialNumber,model,manufacturer,lastSyncDateTime,enrolledDateTime,managementAgent'
$batchSize = 20 $batchSize = 20
$devSeen = @{} $devSeen = @{}
$items = [System.Collections.Generic.List[object]]::new() $items = [System.Collections.Generic.List[object]]::new()
@@ -1757,6 +1775,8 @@ function Get-GroupDevicesExportEndpoint {
OS = [string]$d.operatingSystem OS = [string]$d.operatingSystem
OSVersion = [string]$d.osVersion OSVersion = [string]$d.osVersion
ComplianceState = [string]$d.complianceState ComplianceState = [string]$d.complianceState
ManagementAgent = [string]$d.managementAgent
ManagementType = Get-ManagementType ([string]$d.managementAgent)
SerialNumber = [string]$d.serialNumber SerialNumber = [string]$d.serialNumber
Model = [string]$d.model Model = [string]$d.model
Manufacturer = [string]$d.manufacturer Manufacturer = [string]$d.manufacturer
@@ -1840,6 +1860,15 @@ function Get-UserMemberOfEndpoint {
# Devices # Devices
# ============================================================ # ============================================================
# Verwaltungsart aus dem Graph-Feld 'managementAgent' ableiten.
# 'configurationManagerClientMdm' / 'configurationManagerClientMdmEas' -> Co-Managed
# (ConfigMgr + Intune), alles andere -> reines Intune (MDM).
function Get-ManagementType {
param([string]$Agent)
if ($Agent -match 'configurationManager') { return 'Co-Managed' }
return 'Intune'
}
function Search-DevicesEndpoint { function Search-DevicesEndpoint {
param($Query) param($Query)
$err = Test-Connected $err = Test-Connected
@@ -1852,7 +1881,7 @@ function Search-DevicesEndpoint {
# HINWEIS: 'managementState' ist KEIN gueltiges $select-Feld auf managedDevices # HINWEIS: 'managementState' ist KEIN gueltiges $select-Feld auf managedDevices
# -> fuehrt zu 400 BadRequest. Bewusst weggelassen. # -> fuehrt zu 400 BadRequest. Bewusst weggelassen.
$select = 'id,deviceName,userDisplayName,userPrincipalName,operatingSystem,osVersion,complianceState,lastSyncDateTime,serialNumber,model,manufacturer,enrolledDateTime' $select = 'id,deviceName,userDisplayName,userPrincipalName,operatingSystem,osVersion,complianceState,lastSyncDateTime,serialNumber,model,manufacturer,enrolledDateTime,managementAgent'
# Ohne Suchbegriff: alle Geräte (erste Seite) # Ohne Suchbegriff: alle Geräte (erste Seite)
# Mit Suchbegriff: Graph unterstuetzt startswith nur auf deviceName/userDisplayName/userPrincipalName. # Mit Suchbegriff: Graph unterstuetzt startswith nur auf deviceName/userDisplayName/userPrincipalName.
@@ -1924,6 +1953,8 @@ function Search-DevicesEndpoint {
ComplianceState = $d.complianceState ComplianceState = $d.complianceState
LastSync = ConvertTo-IsoDate $d.lastSyncDateTime LastSync = ConvertTo-IsoDate $d.lastSyncDateTime
ManagementState = $d.managementState ManagementState = $d.managementState
ManagementAgent = [string]$d.managementAgent
ManagementType = Get-ManagementType ([string]$d.managementAgent)
SerialNumber = $d.serialNumber SerialNumber = $d.serialNumber
Model = $d.model Model = $d.model
Manufacturer = $d.manufacturer Manufacturer = $d.manufacturer
@@ -1939,12 +1970,37 @@ function Get-DeviceEndpoint {
if ($err) { return $err } if ($err) { return $err }
# 'managementState' entfernt: kein gueltiges $select-Feld auf managedDevices (400). # 'managementState' entfernt: kein gueltiges $select-Feld auf managedDevices (400).
$select = 'id,deviceName,userDisplayName,userPrincipalName,operatingSystem,osVersion,complianceState,lastSyncDateTime,serialNumber,model,manufacturer,enrolledDateTime,imei,wiFiMacAddress,azureADDeviceId,joinType,deviceEnrollmentType,managedDeviceOwnerType,totalStorageSpaceInBytes,freeStorageSpaceInBytes' $select = 'id,deviceName,userDisplayName,userPrincipalName,operatingSystem,osVersion,complianceState,lastSyncDateTime,serialNumber,model,manufacturer,enrolledDateTime,imei,wiFiMacAddress,azureADDeviceId,joinType,deviceEnrollmentType,managedDeviceOwnerType,managementAgent,totalStorageSpaceInBytes,freeStorageSpaceInBytes'
$d = Invoke-MgGraphRequestRetry -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$DeviceId`?`$select=$select" -Method GET $d = Invoke-MgGraphRequestRetry -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$DeviceId`?`$select=$select" -Method GET
$totalGB = if ($d.totalStorageSpaceInBytes) { [math]::Round($d.totalStorageSpaceInBytes / 1GB, 1) } else { $null } $totalGB = if ($d.totalStorageSpaceInBytes) { [math]::Round($d.totalStorageSpaceInBytes / 1GB, 1) } else { $null }
$freeGB = if ($d.freeStorageSpaceInBytes) { [math]::Round($d.freeStorageSpaceInBytes / 1GB, 1) } else { $null } $freeGB = if ($d.freeStorageSpaceInBytes) { [math]::Round($d.freeStorageSpaceInBytes / 1GB, 1) } else { $null }
# Autopilot-Identity per Seriennummer nachladen (nur Windows, fehlertolerant).
# HINWEIS: Ein echtes "Import-/Registrierungsdatum" liefert Graph nicht; verfuegbar
# sind nur Profil-Zuweisung (deploymentProfileAssignedDateTime) und letzter Kontakt.
$inAutopilot = $false
$apProfileAssign = $null
$apLastContact = $null
$sn = [string]$d.serialNumber
if ($sn -and ([string]$d.operatingSystem).ToLower() -eq 'windows') {
try {
$snEsc = $sn -replace "'", "''"
$apFilt = [Uri]::EscapeDataString("contains(serialNumber,'$snEsc')")
$apSel = 'id,serialNumber,deploymentProfileAssignedDateTime,lastContactedDateTime,enrollmentState'
$apUri = "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities?`$filter=$apFilt&`$select=$apSel&`$top=1"
$apResp = Invoke-MgGraphRequestRetry -Uri $apUri -Method GET
$autop = @($apResp.value)[0]
if ($autop) {
$inAutopilot = $true
$apProfileAssign = ConvertTo-IsoDate $autop.deploymentProfileAssignedDateTime
$apLastContact = ConvertTo-IsoDate $autop.lastContactedDateTime
}
} catch {
Write-Host " [DEVICE] Autopilot-Lookup (SN=$sn): $($_.Exception.Message)" -ForegroundColor DarkYellow
}
}
return @{ return @{
Id = $d.id Id = $d.id
DeviceName = $d.deviceName DeviceName = $d.deviceName
@@ -1955,6 +2011,8 @@ function Get-DeviceEndpoint {
ComplianceState = $d.complianceState ComplianceState = $d.complianceState
LastSync = ConvertTo-IsoDate $d.lastSyncDateTime LastSync = ConvertTo-IsoDate $d.lastSyncDateTime
ManagementState = $d.managementState ManagementState = $d.managementState
ManagementAgent = [string]$d.managementAgent
ManagementType = Get-ManagementType ([string]$d.managementAgent)
SerialNumber = $d.serialNumber SerialNumber = $d.serialNumber
Model = $d.model Model = $d.model
Manufacturer = $d.manufacturer Manufacturer = $d.manufacturer
@@ -1967,6 +2025,9 @@ function Get-DeviceEndpoint {
OwnerType = $d.managedDeviceOwnerType OwnerType = $d.managedDeviceOwnerType
TotalStorageGB = $totalGB TotalStorageGB = $totalGB
FreeStorageGB = $freeGB FreeStorageGB = $freeGB
InAutopilot = $inAutopilot
AutopilotProfileAssigned = $apProfileAssign
AutopilotLastContacted = $apLastContact
} }
} }
+1 -1
View File
@@ -230,7 +230,7 @@ function ConvertTo-ImportBody {
# Arrays. Idempotent: bereits korrekte Arrays bleiben unveraendert. # Arrays. Idempotent: bereits korrekte Arrays bleiben unveraendert.
function Repair-SettingsCatalogArrays { function Repair-SettingsCatalogArrays {
param($Node) param($Node)
$arrayKeys = @('settings','children','groupSettingCollectionValue','simpleSettingCollectionValue','values') $arrayKeys = @('settings','children','groupSettingCollectionValue','simpleSettingCollectionValue','choiceSettingCollectionValue','values')
# *TemplateReference-Keys tragen ein Objekt ODER null. Ein alter Export mit zu # *TemplateReference-Keys tragen ein Objekt ODER null. Ein alter Export mit zu
# geringer ConvertTo-Json-Tiefe hat solche (tief liegenden) Objekte zu ".ToString()" # geringer ConvertTo-Json-Tiefe hat solche (tief liegenden) Objekte zu ".ToString()"
# stringifiziert -> "System.Collections.Hashtable". Graph lehnt das ab # stringifiziert -> "System.Collections.Hashtable". Graph lehnt das ab
+15 -2
View File
@@ -5872,12 +5872,20 @@ const DEV_COL_MAP = {
deviceName: 'DeviceName', deviceName: 'DeviceName',
userDisplayName: 'UserDisplayName', userDisplayName: 'UserDisplayName',
operatingSystem: 'OS', operatingSystem: 'OS',
managementType: 'ManagementType',
complianceState: 'ComplianceState', complianceState: 'ComplianceState',
lastSyncDateTime: 'LastSync', lastSyncDateTime: 'LastSync',
enrolledDateTime: 'EnrolledDateTime', enrolledDateTime: 'EnrolledDateTime',
}; };
const DEV_DATE_COLS = new Set(['LastSync', 'EnrolledDateTime']); const DEV_DATE_COLS = new Set(['LastSync', 'EnrolledDateTime']);
// Verwaltungsart als Badge: Co-Managed (ConfigMgr + Intune) vs. reines Intune.
function devMgmtBadge(type) {
if (type === 'Co-Managed') return '<span class="dev-badge dev-badge-comgmt">Co-Managed</span>';
if (type === 'Intune') return '<span class="dev-badge dev-badge-intune">Intune</span>';
return '<span class="dev-badge dev-badge-unk">—</span>';
}
function devFmtDate(iso) { function devFmtDate(iso) {
if (!iso) return '—'; if (!iso) return '—';
const d = new Date(iso); const d = new Date(iso);
@@ -5941,6 +5949,7 @@ function devRender() {
<td>${escapeHtml(d.DeviceName || '—')}</td> <td>${escapeHtml(d.DeviceName || '—')}</td>
<td>${escapeHtml(d.UserDisplayName || '—')}</td> <td>${escapeHtml(d.UserDisplayName || '—')}</td>
<td>${escapeHtml(d.OS || '—')}</td> <td>${escapeHtml(d.OS || '—')}</td>
<td>${devMgmtBadge(d.ManagementType)}</td>
<td><span class="dev-badge ${cmpCls}">${escapeHtml(d.ComplianceState || '—')}</span></td> <td><span class="dev-badge ${cmpCls}">${escapeHtml(d.ComplianceState || '—')}</span></td>
<td>${sync}</td> <td>${sync}</td>
</tr>`; </tr>`;
@@ -6018,12 +6027,16 @@ function devRenderDetail(d) {
['Benutzer', d.UserDisplayName], ['Benutzer', d.UserDisplayName],
['UPN', d.UserPrincipalName], ['UPN', d.UserPrincipalName],
['OS', d.OS ? `${d.OS} ${d.OSVersion || ''}`.trim() : null], ['OS', d.OS ? `${d.OS} ${d.OSVersion || ''}`.trim() : null],
['Verwaltung', d.ManagementType],
['Compliance', d.ComplianceState], ['Compliance', d.ComplianceState],
['Seriennummer', d.SerialNumber], ['Seriennummer', d.SerialNumber],
['IMEI', d.Imei], ['IMEI', d.Imei],
['Wi-Fi MAC', d.WiFiMac], ['Wi-Fi MAC', d.WiFiMac],
['Speicher', storage], ['Speicher', storage],
['Eingeschrieben', enrolled], ['Eingeschrieben', enrolled],
['Autopilot', d.InAutopilot ? 'Registriert' : (d.InAutopilot === false ? 'Nicht in Autopilot' : null)],
['Autopilot-Profil zugewiesen', d.AutopilotProfileAssigned ? devFmtDate(d.AutopilotProfileAssigned) : null],
['Autopilot letzter Kontakt', d.AutopilotLastContacted ? devFmtDate(d.AutopilotLastContacted) : null],
['Letzter Sync', sync], ['Letzter Sync', sync],
['Join-Typ', d.JoinType], ['Join-Typ', d.JoinType],
['Besitzertyp', d.OwnerType], ['Besitzertyp', d.OwnerType],
@@ -6222,12 +6235,12 @@ document.addEventListener('click', e => {
function devExportCsv() { function devExportCsv() {
const items = DevState.filtered || []; const items = DevState.filtered || [];
if (items.length === 0) { toast('Keine Geräte zum Exportieren.', 'warn'); return; } if (items.length === 0) { toast('Keine Geräte zum Exportieren.', 'warn'); return; }
const header = ['DeviceName','UserDisplayName','UserPrincipalName','OS','OSVersion','ComplianceState','SerialNumber','Model','Manufacturer','LastSync','EnrolledDateTime']; const header = ['DeviceName','UserDisplayName','UserPrincipalName','OS','OSVersion','ManagementType','ComplianceState','SerialNumber','Model','Manufacturer','LastSync','EnrolledDateTime'];
const rows = [header]; const rows = [header];
for (const d of items) { for (const d of items) {
rows.push([ rows.push([
d.DeviceName || '', d.UserDisplayName || '', d.UserPrincipalName || '', d.DeviceName || '', d.UserDisplayName || '', d.UserPrincipalName || '',
d.OS || '', d.OSVersion || '', d.ComplianceState || '', d.OS || '', d.OSVersion || '', d.ManagementType || '', d.ComplianceState || '',
d.SerialNumber || '', d.Model || '', d.Manufacturer || '', d.SerialNumber || '', d.Model || '', d.Manufacturer || '',
d.LastSync ? devFmtDate(d.LastSync) : '', d.EnrolledDateTime ? devFmtDate(d.EnrolledDateTime) : '', d.LastSync ? devFmtDate(d.LastSync) : '', d.EnrolledDateTime ? devFmtDate(d.EnrolledDateTime) : '',
]); ]);
+1
View File
@@ -527,6 +527,7 @@
<th class="sortable" data-col="deviceName">Gerät <span class="sort-ico"></span></th> <th class="sortable" data-col="deviceName">Gerät <span class="sort-ico"></span></th>
<th class="sortable" data-col="userDisplayName">Benutzer <span class="sort-ico"></span></th> <th class="sortable" data-col="userDisplayName">Benutzer <span class="sort-ico"></span></th>
<th class="sortable" data-col="operatingSystem">OS <span class="sort-ico"></span></th> <th class="sortable" data-col="operatingSystem">OS <span class="sort-ico"></span></th>
<th class="sortable" data-col="managementType">Verwaltung <span class="sort-ico"></span></th>
<th class="sortable" data-col="complianceState">Compliance <span class="sort-ico"></span></th> <th class="sortable" data-col="complianceState">Compliance <span class="sort-ico"></span></th>
<th class="sortable" data-col="lastSyncDateTime">Letzter Sync <span class="sort-ico"></span></th> <th class="sortable" data-col="lastSyncDateTime">Letzter Sync <span class="sort-ico"></span></th>
</tr> </tr>
+2
View File
@@ -5427,6 +5427,8 @@ body.read-only .app-row {
.dev-badge-ok { background: rgba(74,222,128,.15); color: #4ade80; } .dev-badge-ok { background: rgba(74,222,128,.15); color: #4ade80; }
.dev-badge-err { background: rgba(248,113,113,.15); color: #f87171; } .dev-badge-err { background: rgba(248,113,113,.15); color: #f87171; }
.dev-badge-unk { background: var(--hairline-soft); color: var(--muted); } .dev-badge-unk { background: var(--hairline-soft); color: var(--muted); }
.dev-badge-intune { background: rgba(96,165,250,.15); color: #60a5fa; }
.dev-badge-comgmt { background: rgba(251,191,36,.15); color: #fbbf24; }
/* Detail-Panel */ /* Detail-Panel */
.dev-detail { .dev-detail {