From 64e4e02a9abde6d173711ec8674fd4cf2b16a6ec Mon Sep 17 00:00:00 2001 From: Esdras Renan Date: Mon, 13 Oct 2025 15:45:24 -0300 Subject: [PATCH] Expose detailed Windows OS info in machine inventory --- .../machines/admin-machines-overview.tsx | 251 +++++++++++++++--- 1 file changed, 216 insertions(+), 35 deletions(-) diff --git a/src/components/admin/machines/admin-machines-overview.tsx b/src/components/admin/machines/admin-machines-overview.tsx index 003fb5a..c0859d2 100644 --- a/src/components/admin/machines/admin-machines-overview.tsx +++ b/src/components/admin/machines/admin-machines-overview.tsx @@ -116,17 +116,6 @@ type WindowsDiskEntry = { MediaType?: string } -type WindowsOsInfo = { - ProductName?: string - CurrentBuild?: string | number - CurrentBuildNumber?: string | number - DisplayVersion?: string - ReleaseId?: string - EditionID?: string - LicenseStatus?: number - IsActivated?: boolean -} - type WindowsExtended = { software?: MachineSoftware[] services?: Array<{ name?: string; status?: string; displayName?: string }> @@ -213,6 +202,105 @@ function readNumber(record: Record, ...keys: string[]): number return undefined } +function parseWindowsInstallDate(value: unknown): Date | null { + if (!value) return null + if (value instanceof Date) return value + if (typeof value === "number" && Number.isFinite(value)) { + return new Date(value) + } + if (typeof value !== "string") return null + const trimmed = value.trim() + if (!trimmed) return null + const wmiMatch = trimmed.match(/Date\((\d+)\)/) + if (wmiMatch) { + const timestamp = Number(wmiMatch[1]) + if (Number.isFinite(timestamp)) { + return new Date(timestamp) + } + } + const digitsOnly = trimmed.replace(/[^0-9]/g, "") + if (digitsOnly.length >= 8 && digitsOnly.length <= 14) { + const year = Number(digitsOnly.slice(0, 4)) + const month = Number(digitsOnly.slice(4, 6)) - 1 + const day = Number(digitsOnly.slice(6, 8)) + const hours = Number(digitsOnly.slice(8, 10) || "0") + const minutes = Number(digitsOnly.slice(10, 12) || "0") + const seconds = Number(digitsOnly.slice(12, 14) || "0") + const parsed = new Date(Date.UTC(year, month, day, hours, minutes, seconds)) + if (!Number.isNaN(parsed.getTime())) { + return parsed + } + } + const parsed = new Date(trimmed) + return Number.isNaN(parsed.getTime()) ? null : parsed +} + +type WindowsOsInfo = { + productName?: string + editionId?: string + displayVersion?: string + releaseId?: string + currentBuild?: string + currentBuildNumber?: string + licenseStatus?: number + isActivated?: boolean + installDate?: Date | null + experience?: string +} + +function parseWindowsOsInfo(raw: unknown): WindowsOsInfo | null { + if (!raw) return null + const parseRecord = (value: Record) => { + const productName = readString(value, "ProductName", "productName") + const editionId = readString(value, "EditionID", "editionId") + const displayVersion = readString(value, "DisplayVersion", "displayVersion") + const releaseId = readString(value, "ReleaseId", "releaseId") + const currentBuild = readString(value, "CurrentBuild", "currentBuild") + const currentBuildNumber = readString(value, "CurrentBuildNumber", "currentBuildNumber") + const licenseStatus = readNumber(value, "LicenseStatus", "licenseStatus") + const isActivatedRaw = value["IsActivated"] + const isActivated = + typeof isActivatedRaw === "boolean" + ? isActivatedRaw + : typeof isActivatedRaw === "string" + ? isActivatedRaw.toLowerCase() === "true" + : undefined + const installDate = + parseWindowsInstallDate(value["InstallDate"]) ?? + parseWindowsInstallDate(value["InstallationDate"]) ?? + parseWindowsInstallDate(value["InstallDateTime"]) + const experience = readString(value, "Experience", "experience", "UBR") + return { + productName, + editionId, + displayVersion, + releaseId, + currentBuild, + currentBuildNumber, + licenseStatus, + isActivated, + installDate, + experience, + } + } + + if (Array.isArray(raw)) { + for (const entry of raw) { + const record = toRecord(entry) + if (record) { + return parseRecord(record) + } + } + return null + } + if (typeof raw === "string") { + return { productName: raw } + } + const record = toRecord(raw) + if (!record) return null + return parseRecord(record) +} + function parseBytesLike(value: unknown): number | undefined { if (typeof value === "number" && Number.isFinite(value)) return value if (typeof value === "string") { @@ -383,6 +471,14 @@ function formatDate(date?: Date | null) { return format(date, "dd/MM/yyyy HH:mm") } +function formatAbsoluteDateTime(date?: Date | null) { + if (!date) return "—" + return new Intl.DateTimeFormat("pt-BR", { + dateStyle: "long", + timeStyle: "short", + }).format(date) +} + function formatBytes(bytes?: number | null) { if (!bytes || Number.isNaN(bytes)) return "—" const units = ["B", "KB", "MB", "GB", "TB"] @@ -631,24 +727,50 @@ export function MachineDetails({ machine }: MachineDetailsProps) { const linuxExt = extended?.linux ?? null const windowsExt = extended?.windows ?? null const macosExt = extended?.macos ?? null + const windowsOsInfo = parseWindowsOsInfo(windowsExt?.osInfo) + const windowsActivationStatus = windowsOsInfo?.isActivated ?? (typeof windowsOsInfo?.licenseStatus === "number" ? windowsOsInfo.licenseStatus === 1 : null) const windowsMemoryModulesRaw = windowsExt?.memoryModules const windowsVideoControllersRaw = windowsExt?.videoControllers const windowsDiskEntriesRaw = windowsExt?.disks - const windowsMemoryModules = Array.isArray(windowsMemoryModulesRaw) - ? windowsMemoryModulesRaw - : windowsMemoryModulesRaw && typeof windowsMemoryModulesRaw === "object" - ? [windowsMemoryModulesRaw] - : [] - const windowsVideoControllers = Array.isArray(windowsVideoControllersRaw) - ? windowsVideoControllersRaw - : windowsVideoControllersRaw && typeof windowsVideoControllersRaw === "object" - ? [windowsVideoControllersRaw] - : [] - const windowsDiskEntries = Array.isArray(windowsDiskEntriesRaw) - ? windowsDiskEntriesRaw - : windowsDiskEntriesRaw && typeof windowsDiskEntriesRaw === "object" - ? [windowsDiskEntriesRaw] - : [] + const windowsServicesRaw = windowsExt?.services + const windowsSoftwareRaw = windowsExt?.software + const windowsBaseboardRaw = windowsExt?.baseboard + const windowsBaseboard = Array.isArray(windowsBaseboardRaw) + ? windowsBaseboardRaw[0] + : windowsBaseboardRaw && typeof windowsBaseboardRaw === "object" + ? windowsBaseboardRaw + : null + const windowsSerialNumber = windowsBaseboard ? readString(toRecord(windowsBaseboard) ?? {}, "SerialNumber", "serialNumber") : undefined + const windowsMemoryModules = useMemo(() => { + if (Array.isArray(windowsMemoryModulesRaw)) return windowsMemoryModulesRaw + if (windowsMemoryModulesRaw && typeof windowsMemoryModulesRaw === "object") return [windowsMemoryModulesRaw] + return [] + }, [windowsMemoryModulesRaw]) + const windowsVideoControllers = useMemo(() => { + if (Array.isArray(windowsVideoControllersRaw)) return windowsVideoControllersRaw + if (windowsVideoControllersRaw && typeof windowsVideoControllersRaw === "object") return [windowsVideoControllersRaw] + return [] + }, [windowsVideoControllersRaw]) + const windowsDiskEntries = useMemo(() => { + if (Array.isArray(windowsDiskEntriesRaw)) return windowsDiskEntriesRaw + if (windowsDiskEntriesRaw && typeof windowsDiskEntriesRaw === "object") return [windowsDiskEntriesRaw] + return [] + }, [windowsDiskEntriesRaw]) + const windowsServices = useMemo(() => { + if (Array.isArray(windowsServicesRaw)) return windowsServicesRaw + if (windowsServicesRaw && typeof windowsServicesRaw === "object") return [windowsServicesRaw] + return [] + }, [windowsServicesRaw]) + const windowsSoftware = useMemo(() => { + if (Array.isArray(windowsSoftwareRaw)) return windowsSoftwareRaw + if (windowsSoftwareRaw && typeof windowsSoftwareRaw === "object") return [windowsSoftwareRaw] + return [] + }, [windowsSoftwareRaw]) + const windowsEditionLabel = windowsOsInfo?.productName ?? windowsOsInfo?.editionId ?? null + const windowsVersionLabel = windowsOsInfo?.displayVersion ?? windowsOsInfo?.releaseId ?? null + const windowsBuildLabel = windowsOsInfo?.currentBuildNumber ?? windowsOsInfo?.currentBuild ?? null + const windowsInstallDateLabel = windowsOsInfo?.installDate ? formatAbsoluteDateTime(windowsOsInfo.installDate) : null + const windowsExperienceLabel = windowsOsInfo?.experience ?? null const linuxLsblk = linuxExt?.lsblk ?? [] const linuxSmartEntries = linuxExt?.smart ?? [] const normalizedHardwareGpus = Array.isArray(hardware?.gpus) @@ -684,8 +806,6 @@ export function MachineDetails({ machine }: MachineDetailsProps) { ? windowsCpuRaw : [windowsCpuRaw] : [] - const windowsServices = windowsExt?.services ?? [] - const windowsSoftware = windowsExt?.software ?? [] const winDiskStats = windowsDiskEntries.length > 0 ? { count: windowsDiskEntries.length, @@ -753,6 +873,7 @@ export function MachineDetails({ machine }: MachineDetailsProps) { (machine?.persona === "manager" || collaborator?.role === "manager") ? "manager" : "collaborator" ) const [savingAccess, setSavingAccess] = useState(false) + const [showAllWindowsSoftware, setShowAllWindowsSoftware] = useState(false) const jsonText = useMemo(() => { const payload = { id: machine?.id, @@ -781,6 +902,15 @@ export function MachineDetails({ machine }: MachineDetailsProps) { setAccessRole((machine?.persona === "manager" || collaborator?.role === "manager") ? "manager" : "collaborator") }, [machine?.id, machine?.persona, collaborator?.email, collaborator?.name, collaborator?.role]) + useEffect(() => { + setShowAllWindowsSoftware(false) + }, [machine?.id]) + + const displayedWindowsSoftware = useMemo( + () => (showAllWindowsSoftware ? windowsSoftware : windowsSoftware.slice(0, 8)), + [showAllWindowsSoftware, windowsSoftware] + ) + const handleSaveAccess = async () => { if (!machine) return if (!accessEmail.trim()) { @@ -853,14 +983,20 @@ export function MachineDetails({ machine }: MachineDetailsProps) { {machine.architecture?.toUpperCase() ?? "Arquitetura indefinida"} - {windowsExt?.osInfo ? ( + {windowsOsInfo ? ( - Build: {String(windowsExt.osInfo?.CurrentBuildNumber ?? windowsExt.osInfo?.CurrentBuild ?? "—")} + Build: {windowsBuildLabel ?? "—"} ) : null} - {windowsExt?.osInfo ? ( + {windowsOsInfo ? ( - Ativado: {windowsExt.osInfo?.IsActivated === true ? "Sim" : "Não"} + Ativado: { + windowsActivationStatus == null + ? "—" + : windowsActivationStatus + ? "Sim" + : "Não" + } ) : null} {primaryGpu?.name ? ( @@ -1251,7 +1387,7 @@ export function MachineDetails({ machine }: MachineDetailsProps) { {windowsExt ? (
{/* Cards resumidos: CPU / RAM / GPU / Discos */} -
+
@@ -1270,6 +1406,20 @@ export function MachineDetails({ machine }: MachineDetailsProps) {
+ + + +
+

Windows

+

+ {windowsEditionLabel ?? machine?.osName ?? "—"} +

+

+ {windowsVersionLabel ?? windowsBuildLabel ?? "Versão desconhecida"} +

+
+
+
@@ -1289,6 +1439,24 @@ export function MachineDetails({ machine }: MachineDetailsProps) {
+ {windowsOsInfo ? ( +
+

Informações do Windows

+
+ + + + + + + +
+
+ ) : null} + {windowsCpuDetails.length > 0 ? (

CPU

@@ -1359,9 +1527,22 @@ export function MachineDetails({ machine }: MachineDetailsProps) { {windowsSoftware.length > 0 ? (
-

Softwares (amostra)

+
+

Aplicativos instalados

+ {windowsSoftware.length > 8 ? ( + + ) : null} +
    - {windowsSoftware.slice(0, 8).map((softwareItem, index) => { + {displayedWindowsSoftware.map((softwareItem, index) => { const record = toRecord(softwareItem) ?? {} const name = readString(record, "DisplayName", "name") ?? "—" const version = readString(record, "DisplayVersion", "version")