mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 12:42:38 +00:00
Added oobeConfig modal dialog
This commit is contained in:
+24
-1
@@ -2,10 +2,12 @@
|
||||
|
||||
import { useState, useMemo } from "react";
|
||||
import { EnrollmentEvent } from "@/types";
|
||||
import OobeConfigModal from "./OobeConfigModal";
|
||||
|
||||
export default function DeviceDetailsCard({ events }: { events: EnrollmentEvent[] }) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [showIpv6, setShowIpv6] = useState<Record<number, boolean>>({});
|
||||
const [showOobeModal, setShowOobeModal] = useState(false);
|
||||
|
||||
const getEventData = (eventType: string): Record<string, any> | null => {
|
||||
const matchingEvents = events.filter(e => e.eventType === eventType);
|
||||
@@ -346,7 +348,28 @@ export default function DeviceDetailsCard({ events }: { events: EnrollmentEvent[
|
||||
{hasValue(autopilotProfile.DeploymentProfileName) && <DetailRow label="Profile Name" value={`${autopilotProfile.DeploymentProfileName}`} />}
|
||||
{hasValue(autopilotProfile.CloudAssignedTenantId) && <DetailRow label="Tenant ID" value={`${autopilotProfile.CloudAssignedTenantId}`} />}
|
||||
{hasValue(autopilotProfile.PolicyDownloadDate) && <DetailRow label="Policy Downloaded" value={new Date(autopilotProfile.PolicyDownloadDate).toLocaleString()} />}
|
||||
{hasValue(autopilotProfile.CloudAssignedOobeConfig) && <DetailRow label="OOBE Config" value={`${autopilotProfile.CloudAssignedOobeConfig}`} />}
|
||||
{hasValue(autopilotProfile.CloudAssignedOobeConfig) && (
|
||||
<>
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<span className="text-xs text-gray-500 min-w-[120px]">OOBE Config</span>
|
||||
<button
|
||||
onClick={() => setShowOobeModal(true)}
|
||||
className="text-xs text-right text-blue-600 hover:text-blue-800 hover:underline cursor-pointer font-mono transition-colors"
|
||||
title="Click to decode bitmask"
|
||||
>
|
||||
{`${autopilotProfile.CloudAssignedOobeConfig}`}
|
||||
<svg className="w-3 h-3 inline ml-1 -mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<OobeConfigModal
|
||||
show={showOobeModal}
|
||||
value={Number(autopilotProfile.CloudAssignedOobeConfig)}
|
||||
onClose={() => setShowOobeModal(false)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{hasValue(autopilotProfile.ZtdRegistrationId) && <DetailRow label="ZTD Registration ID" value={`${autopilotProfile.ZtdRegistrationId}`} />}
|
||||
{hasValue(autopilotProfile.AadDeviceId) && <DetailRow label="AAD Device ID" value={`${autopilotProfile.AadDeviceId}`} />}
|
||||
{autopilotProfile.AutopilotMode !== undefined && (
|
||||
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
"use client";
|
||||
|
||||
interface OobeConfigBit {
|
||||
bit: number;
|
||||
hex: string;
|
||||
value: number;
|
||||
name: string;
|
||||
description: string;
|
||||
confidence: "confirmed" | "likely" | "speculated";
|
||||
source: string;
|
||||
}
|
||||
|
||||
const OOBE_CONFIG_BITS: OobeConfigBit[] = [
|
||||
{ bit: 0, hex: "0x0001", value: 1, name: "SkipCortanaOptIn", description: "Skip Cortana opt-in during OOBE", confidence: "confirmed", source: "Microsoft Docs" },
|
||||
{ bit: 1, hex: "0x0002", value: 2, name: "OobeUserNotLocalAdmin", description: "User is standard user (not local admin)", confidence: "confirmed", source: "Microsoft Docs" },
|
||||
{ bit: 2, hex: "0x0004", value: 4, name: "SkipExpressSettings", description: "Skip Privacy/Express settings page", confidence: "confirmed", source: "Microsoft Docs" },
|
||||
{ bit: 3, hex: "0x0008", value: 8, name: "SkipOemRegistration", description: "Skip OEM registration page", confidence: "confirmed", source: "Microsoft Docs" },
|
||||
{ bit: 4, hex: "0x0010", value: 16, name: "SkipEula", description: "Skip EULA acceptance page", confidence: "confirmed", source: "Microsoft Docs" },
|
||||
{ bit: 5, hex: "0x0020", value: 32, name: "TPM Attestation", description: "Enable TPM attestation (Self-Deploying)", confidence: "likely", source: "WindowsAutoPilotIntune / Niehaus" },
|
||||
{ bit: 6, hex: "0x0040", value: 64, name: "AAD Device Auth", description: "AAD device authentication (Self-Deploying)", confidence: "likely", source: "WindowsAutoPilotIntune / Niehaus" },
|
||||
{ bit: 7, hex: "0x0080", value: 128, name: "AAD TPM Required", description: "Require TPM for AAD join (Self-Deploying)", confidence: "likely", source: "WindowsAutoPilotIntune / Niehaus" },
|
||||
{ bit: 8, hex: "0x0100", value: 256, name: "SkipWindowsUpgradeUX", description: "Suppress Windows feature update check during OOBE", confidence: "confirmed", source: "WindowsAutoPilotIntune" },
|
||||
{ bit: 9, hex: "0x0200", value: 512, name: "EnablePatchDownload", description: "Enable quality update download during OOBE", confidence: "likely", source: "Convert-WindowsAutopilotProfile / Niehaus" },
|
||||
{ bit: 10, hex: "0x0400", value: 1024, name: "SkipKeyboardSelection", description: "Skip keyboard/input method selection page", confidence: "confirmed", source: "WindowsAutoPilotIntune" },
|
||||
];
|
||||
|
||||
const CONFIDENCE_CONFIG = {
|
||||
confirmed: { label: "Confirmed", bg: "bg-green-100", text: "text-green-800", dot: "bg-green-500" },
|
||||
likely: { label: "Likely", bg: "bg-yellow-100", text: "text-yellow-800", dot: "bg-yellow-500" },
|
||||
speculated: { label: "Speculated", bg: "bg-orange-100", text: "text-orange-800", dot: "bg-orange-500" },
|
||||
};
|
||||
|
||||
interface OobeConfigModalProps {
|
||||
show: boolean;
|
||||
value: number;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default function OobeConfigModal({ show, value, onClose }: OobeConfigModalProps) {
|
||||
if (!show) return null;
|
||||
|
||||
const hexValue = `0x${value.toString(16).toUpperCase().padStart(4, "0")}`;
|
||||
const binaryValue = value.toString(2).padStart(11, "0");
|
||||
|
||||
const isSelfDeploying = (value & 0x0020) !== 0 && (value & 0x0040) !== 0;
|
||||
const profileType = isSelfDeploying ? "Self-Deploying" : "User-Driven";
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" onClick={onClose}>
|
||||
<div className="bg-white rounded-lg shadow-xl max-w-lg w-full mx-4 max-h-[90vh] overflow-y-auto" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="p-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex-shrink-0 w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center">
|
||||
<svg className="w-5 h-5 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900">OOBE Config Breakdown</h3>
|
||||
<p className="text-sm text-gray-500">
|
||||
{value} ({hexValue}) · {profileType}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Legend */}
|
||||
<div className="flex flex-wrap gap-3 mb-4 text-xs">
|
||||
{(Object.entries(CONFIDENCE_CONFIG) as [keyof typeof CONFIDENCE_CONFIG, typeof CONFIDENCE_CONFIG[keyof typeof CONFIDENCE_CONFIG]][]).map(([key, conf]) => (
|
||||
<span key={key} className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-full ${conf.bg} ${conf.text}`}>
|
||||
<span className={`w-1.5 h-1.5 rounded-full ${conf.dot}`} />
|
||||
{conf.label}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Bit table */}
|
||||
<div className="border rounded-lg overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="bg-gray-50 text-left text-xs text-gray-500 uppercase tracking-wider">
|
||||
<th className="px-3 py-2 w-8"></th>
|
||||
<th className="px-3 py-2">Flag</th>
|
||||
<th className="px-3 py-2 text-right">Source</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-100">
|
||||
{OOBE_CONFIG_BITS.map((bit) => {
|
||||
const isSet = (value & bit.value) !== 0;
|
||||
const conf = CONFIDENCE_CONFIG[bit.confidence];
|
||||
return (
|
||||
<tr key={bit.bit} className={isSet ? "bg-blue-50" : "text-gray-400"}>
|
||||
<td className="px-3 py-2 text-center">
|
||||
{isSet ? (
|
||||
<svg className="w-4 h-4 text-blue-600 inline" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
) : (
|
||||
<span className="text-gray-300">—</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<div className={`font-medium ${isSet ? "text-gray-900" : ""}`}>{bit.name}</div>
|
||||
<div className={`text-xs ${isSet ? "text-gray-600" : "text-gray-400"}`}>
|
||||
{bit.description}
|
||||
<span className="ml-1 text-gray-400">({bit.hex})</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-3 py-2 text-right align-top">
|
||||
<span className={`inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] whitespace-nowrap ${conf.bg} ${conf.text}`}>
|
||||
<span className={`w-1 h-1 rounded-full ${conf.dot}`} />
|
||||
{bit.source}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Unknown bits warning */}
|
||||
{(() => {
|
||||
const knownMask = OOBE_CONFIG_BITS.reduce((acc, b) => acc | b.value, 0);
|
||||
const unknownBits = value & ~knownMask;
|
||||
if (unknownBits === 0) return null;
|
||||
return (
|
||||
<div className="mt-3 p-2 bg-amber-50 border border-amber-200 rounded text-xs text-amber-800">
|
||||
Unknown bits set: {unknownBits} (0x{unknownBits.toString(16).toUpperCase()}) — these flags are not yet documented.
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Footer */}
|
||||
<div className="mt-4 flex items-center justify-between">
|
||||
<p className="text-[10px] text-gray-400 leading-tight max-w-[280px]">
|
||||
Sources: Microsoft Docs, WindowsAutoPilotIntune (Niehaus), Convert-WindowsAutopilotProfile.
|
||||
Confidence reflects how well each flag is documented.
|
||||
</p>
|
||||
<button onClick={onClose} className="px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300 transition-colors text-sm">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user