"use client" import { useCallback, useEffect, useMemo, useState } from "react" import type { ReactNode } from "react" import { useQuery } from "convex/react" import { format, formatDistanceToNowStrict } from "date-fns" import { ptBR } from "date-fns/locale" import { toast } from "sonner" import { ClipboardCopy, ServerCog, Cpu, MemoryStick, Monitor, HardDrive, Pencil, ShieldCheck, ShieldAlert, Shield, ShieldOff, ShieldQuestion, Lock, Cloud, RefreshCcw, AlertTriangle, Key, Globe, Apple, Terminal, Power, PlayCircle, Download, } from "lucide-react" import { api } from "@/convex/_generated/api" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" import { Checkbox } from "@/components/ui/checkbox" import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { Separator } from "@/components/ui/separator" import { ChartContainer } from "@/components/ui/chart" import { cn } from "@/lib/utils" import { RadialBarChart, RadialBar, PolarAngleAxis } from "recharts" import Link from "next/link" import { useRouter } from "next/navigation" import { useAuth } from "@/lib/auth-client" import type { Id } from "@/convex/_generated/dataModel" type MachineMetrics = Record | null type MachineLabel = { id?: number | string name?: string } type MachineSoftware = { name?: string version?: string source?: string } type NormalizedSoftwareEntry = { name: string version?: string publisher?: string installDate?: Date | null source?: string } type MachineAlertEntry = { id: string kind: string message: string severity: string createdAt: number } type DetailLineProps = { label: string value?: string | number | null classNameValue?: string layout?: "spread" | "compact" } type GpuAdapter = { name?: string vendor?: string driver?: string memoryBytes?: number } type LinuxLsblkEntry = { name?: string mountPoint?: string mountpoint?: string fs?: string fstype?: string sizeBytes?: number size?: number } type LinuxSmartEntry = { smart_status?: { passed?: boolean } model_name?: string model_family?: string serial_number?: string device?: { name?: string } } type LinuxExtended = { lsblk?: LinuxLsblkEntry[] lspci?: string lsusb?: string pciList?: Array<{ text: string }> usbList?: Array<{ text: string }> smart?: LinuxSmartEntry[] } type WindowsCpuInfo = { Name?: string Manufacturer?: string SocketDesignation?: string NumberOfCores?: number NumberOfLogicalProcessors?: number L2CacheSize?: number L3CacheSize?: number MaxClockSpeed?: number } type WindowsMemoryModule = { BankLabel?: string Capacity?: number Manufacturer?: string PartNumber?: string SerialNumber?: string ConfiguredClockSpeed?: number Speed?: number ConfiguredVoltage?: number } type WindowsVideoController = { Name?: string AdapterRAM?: number DriverVersion?: string PNPDeviceID?: string } type WindowsDiskEntry = { Model?: string SerialNumber?: string Size?: number InterfaceType?: string MediaType?: string } type WindowsExtended = { software?: MachineSoftware[] services?: Array<{ name?: string; status?: string; displayName?: string }> defender?: Record hotfix?: Array> cpu?: WindowsCpuInfo | WindowsCpuInfo[] baseboard?: Record | Array> bios?: Record | Array> memoryModules?: WindowsMemoryModule[] videoControllers?: WindowsVideoController[] disks?: WindowsDiskEntry[] osInfo?: WindowsOsInfo bitLocker?: Array> | Record bitlocker?: Array> | Record tpm?: Record secureBoot?: Record deviceGuard?: Array> | Record firewallProfiles?: Array> | Record windowsUpdate?: Record computerSystem?: Record azureAdStatus?: Record } type MacExtended = { systemProfiler?: Record packages?: string[] launchctl?: string } type NetworkInterface = { name?: string; mac?: string; ip?: string } type MachineInventory = { hardware?: { vendor?: string model?: string serial?: string cpuType?: string physicalCores?: number logicalCores?: number memoryBytes?: number memory?: number primaryGpu?: GpuAdapter gpus?: GpuAdapter[] } network?: { primaryIp?: string; publicIp?: string; macAddresses?: string[] } | NetworkInterface[] software?: MachineSoftware[] labels?: MachineLabel[] fleet?: { id?: number | string teamId?: number | string detailUpdatedAt?: string osqueryVersion?: string } disks?: Array<{ name?: string; mountPoint?: string; fs?: string; interface?: string | null; serial?: string | null; totalBytes?: number; availableBytes?: number }> extended?: { linux?: LinuxExtended; windows?: WindowsExtended; macos?: MacExtended } services?: Array<{ name?: string; status?: string; displayName?: string }> collaborator?: { email?: string; name?: string; role?: string } } function collectInitials(name: string): string { const words = name.split(/\s+/).filter(Boolean) if (words.length === 0) return "?" if (words.length === 1) return words[0].slice(0, 2).toUpperCase() return (words[0][0] + words[1][0]).toUpperCase() } function toRecord(value: unknown): Record | null { if (!value || typeof value !== "object") return null return value as Record } function readString(record: Record, ...keys: string[]): string | undefined { for (const key of keys) { const raw = record[key] if (typeof raw === "string" && raw.trim().length > 0) { return raw } } return undefined } function readNumber(record: Record, ...keys: string[]): number | undefined { for (const key of keys) { const raw = record[key] if (typeof raw === "number" && Number.isFinite(raw)) { return raw } if (typeof raw === "string") { const trimmed = raw.trim() if (!trimmed) continue const parsed = Number(trimmed) if (!Number.isNaN(parsed)) return parsed const digits = trimmed.replace(/[^0-9.]/g, "") if (digits) { const fallback = Number(digits) if (!Number.isNaN(fallback)) return fallback } } } return undefined } function readText(record: Record, ...keys: string[]): string | undefined { const stringValue = readString(record, ...keys) if (stringValue) return stringValue const numberValue = readNumber(record, ...keys) if (typeof numberValue === "number") return String(numberValue) 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)) { if (value > 10_000_000_000) { return new Date(value) } if (value > 1_000_000_000) { return new Date(value * 1000) } return new Date(value * 1000) } 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]) return Number.isFinite(timestamp) ? new Date(timestamp) : null } const isoValue = Date.parse(trimmed) if (!Number.isNaN(isoValue)) return new Date(isoValue) const digitsOnly = trimmed.replace(/[^0-9]/g, "") if (digitsOnly.length >= 8) { const yyyy = Number(digitsOnly.slice(0, 4)) const mm = Number(digitsOnly.slice(4, 6)) const dd = Number(digitsOnly.slice(6, 8)) const hh = Number(digitsOnly.slice(8, 10) || "0") const mi = Number(digitsOnly.slice(10, 12) || "0") const ss = Number(digitsOnly.slice(12, 14) || "0") if (yyyy > 1900 && mm >= 1 && mm <= 12 && dd >= 1 && dd <= 31) { const parsed = new Date(Date.UTC(yyyy, mm - 1, dd, hh, mi, ss)) if (!Number.isNaN(parsed.getTime())) return parsed } const dd2 = Number(digitsOnly.slice(0, 2)) const mm2 = Number(digitsOnly.slice(2, 4)) const yyyy2 = Number(digitsOnly.slice(4, 8)) if (yyyy2 > 1900 && mm2 >= 1 && mm2 <= 12 && dd2 >= 1 && dd2 <= 31) { const parsed = new Date(Date.UTC(yyyy2, mm2 - 1, dd2)) if (!Number.isNaN(parsed.getTime())) return parsed } } const localMatch = trimmed.match( /^(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})(?:\s+(\d{1,2}):(\d{2})(?::(\d{2}))?)?$/ ) if (localMatch) { const dd = Number(localMatch[1]) const mm = Number(localMatch[2]) const yyyy = Number(localMatch[3]) const hh = Number(localMatch[4] || "0") const mi = Number(localMatch[5] || "0") const ss = Number(localMatch[6] || "0") if (yyyy > 1900 && mm >= 1 && mm <= 12 && dd >= 1 && dd <= 31) { const parsed = new Date(Date.UTC(yyyy, mm - 1, dd, hh, mi, ss)) if (!Number.isNaN(parsed.getTime())) return parsed } } return null } type WindowsOsInfo = { productName?: string caption?: string editionId?: string displayVersion?: string releaseId?: string version?: string currentBuild?: string currentBuildNumber?: string licenseStatus?: number isActivated?: boolean licenseStatusText?: string productId?: string partialProductKey?: string computerName?: string registeredOwner?: string installDate?: Date | null experience?: string } function formatOsVersionDisplay(osName: string | null | undefined, osVersion: string | null | undefined) { const name = (osName ?? "").trim() const version = (osVersion ?? "").trim() if (!version) return "" // If Windows and version redundantly starts with the same major (e.g., "11 (26100)"), drop leading major const winMatch = name.match(/^windows\s+(\d+)\b/i) if (winMatch) { const major = winMatch[1] const re = new RegExp(`^\\s*${major}(?:\\b|\\.|-_|\\s)+(.*)$`, "i") const m = version.match(re) if (m) { const rest = (m[1] ?? "").trim() return rest } } return version } function parseWindowsOsInfo(raw: unknown): WindowsOsInfo | null { if (!raw) return null const parseRecord = (value: Record) => { const read = (k: string, ...alts: string[]) => readString(value, k, ...alts) const readNum = (...keys: string[]) => readNumber(value, ...keys) const readFlexible = (...keys: string[]) => readText(value, ...keys) const family = read("Family") const edition = read("Edition") const captionRaw = readFlexible("Caption", "caption") const captionNormalized = captionRaw ? captionRaw.replace(/^Microsoft\s+/i, "").trim() : undefined let productName = readFlexible("ProductName", "productName", "Name", "name") ?? (family && edition ? `${family} ${edition}` : family ?? edition ?? undefined) if (captionNormalized && /windows/i.test(captionNormalized)) { productName = captionNormalized } const editionId = readFlexible("EditionID", "editionId", "Edition", "edition", "SkuEdition", "skuEdition", "CompositionEditionID", "compositionEditionId") ?? undefined const displayVersion = readFlexible("DisplayVersion", "displayVersion") const versionValue = readFlexible("Version", "version", "ReleaseId", "releaseId") ?? displayVersion const baseBuild = readFlexible("CurrentBuildNumber", "currentBuildNumber", "CurrentBuild", "currentBuild", "OSBuild", "osBuild", "BuildNumber", "buildNumber") ?? undefined const ubrRaw = readFlexible("UBR") const licenseStatus = readNum("LicenseStatus", "licenseStatus") const licenseStatusTextRaw = readFlexible( "LicenseStatusDescription", "licenseStatusDescription", "StatusDescription", "statusDescription", "Status", "status" ) const licenseStatusText = licenseStatusTextRaw ? licenseStatusTextRaw.trim() : undefined const currentBuildNumber = baseBuild && ubrRaw && /^\d+$/.test(ubrRaw) ? `${baseBuild}.${ubrRaw}` : baseBuild ?? readFlexible("BuildNumber", "buildNumber") const currentBuild = baseBuild const isActivatedRaw = value["IsActivated"] ?? value["isActivated"] const isLicensedRaw = value["IsLicensed"] ?? value["isLicensed"] const isActivated = typeof isActivatedRaw === "boolean" ? isActivatedRaw : typeof isActivatedRaw === "number" ? isActivatedRaw === 1 : typeof isActivatedRaw === "string" ? isActivatedRaw.toLowerCase() === "true" : typeof isLicensedRaw === "boolean" ? isLicensedRaw : typeof isLicensedRaw === "number" ? isLicensedRaw === 1 : typeof isLicensedRaw === "string" ? ["1", "true", "licensed", "license", "activated"].includes(isLicensedRaw.toLowerCase()) : licenseStatus === 1 || Boolean(licenseStatusText && /licensed|activated|licenciado/i.test(licenseStatusText)) const installDate = parseWindowsInstallDate(value["InstallDate"]) ?? parseWindowsInstallDate(value["InstallationDate"]) ?? parseWindowsInstallDate(value["InstallDateTime"]) ?? parseWindowsInstallDate(value["InstalledOn"]) const experience = (() => { const exp = readFlexible("Experience", "experience") if (exp) return exp const pack = readFlexible("FeatureExperiencePack", "featureExperiencePack") if (pack) { const trimmed = pack.trim() if (trimmed) return `Pacote de experiência ${trimmed}` } return currentBuildNumber ? `OS Build ${currentBuildNumber}` : undefined })() const productId = readFlexible("ProductID", "productID", "ProductId", "productId", "ProductKeyId", "productKeyId") const partialProductKey = readFlexible("PartialProductKey", "partialProductKey") const computerName = readFlexible("DeviceName", "deviceName", "ComputerName", "computerName", "CSName", "csName", "HostName", "hostName") const registeredOwner = readFlexible("RegisteredOwner", "registeredOwner", "RegisteredOrganization", "registeredOrganization") return { productName, editionId, displayVersion, version: versionValue, releaseId: read("ReleaseId", "releaseId"), currentBuild, currentBuildNumber, licenseStatus, isActivated, licenseStatusText, productId, partialProductKey, computerName, registeredOwner, installDate, experience, caption: captionNormalized ?? captionRaw ?? undefined, } } 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 normalizeWindowsSoftwareEntry(value: unknown): NormalizedSoftwareEntry | null { const record = toRecord(value) if (!record) return null const name = readString(record, "DisplayName", "displayName", "Name", "name") ?? readText(record, "Title", "title") ?? "" if (!name) return null const version = readString(record, "DisplayVersion", "displayVersion", "Version", "version") ?? undefined const publisher = readString(record, "Publisher", "publisher", "Vendor", "vendor") ?? undefined const installDate = parseWindowsInstallDate(record["InstallDate"]) ?? parseWindowsInstallDate(record["InstallDateUTC"]) ?? parseWindowsInstallDate(record["InstallDateTime"]) ?? parseWindowsInstallDate(record["InstallDateFromRegistry"]) ?? parseWindowsInstallDate(record["InstallDateFromRegistryUTC"]) ?? parseWindowsInstallDate(record["InstalledDate"]) ?? parseWindowsInstallDate(record["InstalledOn"]) ?? null const source = readString(record, "ParentDisplayName", "ParentKeyName", "SystemComponent") ?? undefined return { name, version, publisher, installDate, source, } } function parseBytesLike(value: unknown): number | undefined { if (typeof value === "number" && Number.isFinite(value)) return value if (typeof value === "string") { const trimmed = value.trim() if (!trimmed) return undefined const normalized = trimmed.replace(",", ".") const match = normalized.match(/^([\d.]+)\s*(ti|tb|tib|gb|gib|mb|mib|kb|kib|b)?$/i) if (match) { const amount = Number(match[1]) if (Number.isNaN(amount)) return undefined const unit = match[2]?.toLowerCase() const base = 1024 const unitMap: Record = { b: 1, kb: base, kib: base, mb: base ** 2, mib: base ** 2, gb: base ** 3, gib: base ** 3, tb: base ** 4, tib: base ** 4, ti: base ** 4, } if (unit) { const multiplier = unitMap[unit] if (multiplier) { return amount * multiplier } } return amount } const digits = normalized.replace(/[^0-9.]/g, "") if (digits) { const fallback = Number(digits) if (!Number.isNaN(fallback)) return fallback } } return undefined } function deriveVendor(record: Record): string | undefined { const direct = readString(record, "vendor", "Vendor", "AdapterCompatibility") if (direct) return direct const pnp = readString(record, "PNPDeviceID") if (!pnp) return undefined const match = pnp.match(/VEN_([0-9A-F]{4})/i) if (match) { const vendorCode = match[1].toUpperCase() const vendorMap: Record = { "10DE": "NVIDIA", "1002": "AMD", "1022": "AMD", "8086": "Intel", "8087": "Intel", "1AF4": "Red Hat", } return vendorMap[vendorCode] ?? `VEN_${vendorCode}` } const segments = pnp.split("\\") const last = segments.pop() return last && last.trim().length > 0 ? last : pnp } function normalizeGpuSource(value: unknown): GpuAdapter | null { if (typeof value === "string") { const name = value.trim() return name ? { name } : null } const record = toRecord(value) if (!record) return null const name = readString(record, "name", "Name", "_name", "AdapterCompatibility") const vendor = deriveVendor(record) const driver = readString(record, "driver", "DriverVersion", "driverVersion") const memoryBytes = readNumber(record, "memoryBytes", "MemoryBytes", "AdapterRAM", "VRAM", "vramBytes") ?? parseBytesLike(record["AdapterRAM"] ?? record["VRAM"] ?? record["vram"]) if (!name && !vendor && !driver && memoryBytes === undefined) { return null } return { name, vendor, driver, memoryBytes } } function uniqueBy(items: T[], keyFn: (item: T) => string): T[] { const seen = new Set() const result: T[] = [] items.forEach((item) => { const key = keyFn(item) if (key && !seen.has(key)) { seen.add(key) result.push(item) } }) return result } export type MachinesQueryItem = { id: string tenantId: string hostname: string companyId: string | null companySlug: string | null osName: string | null osVersion: string | null architecture: string | null macAddresses: string[] serialNumbers: string[] authUserId: string | null authEmail: string | null persona: string | null assignedUserId: string | null assignedUserEmail: string | null assignedUserName: string | null assignedUserRole: string | null status: string | null isActive: boolean lastHeartbeatAt: number | null heartbeatAgeMs: number | null registeredBy: string | null createdAt: number updatedAt: number token: { expiresAt: number lastUsedAt: number | null usageCount: number } | null metrics: MachineMetrics inventory: MachineInventory | null postureAlerts?: Array> | null lastPostureAt?: number | null linkedUsers?: Array<{ id: string; email: string; name: string }> } function useMachinesQuery(tenantId: string): MachinesQueryItem[] { return ( (useQuery(api.machines.listByTenant, { tenantId, includeMetadata: true, }) ?? []) as MachinesQueryItem[] ) } const DEFAULT_OFFLINE_THRESHOLD_MS = 10 * 60 * 1000 const DEFAULT_STALE_THRESHOLD_MS = DEFAULT_OFFLINE_THRESHOLD_MS * 12 function parseThreshold(raw: string | undefined, fallback: number) { if (!raw) return fallback const parsed = Number(raw) if (!Number.isFinite(parsed) || parsed <= 0) return fallback return parsed } const MACHINE_OFFLINE_THRESHOLD_MS = parseThreshold(process.env.NEXT_PUBLIC_MACHINE_OFFLINE_THRESHOLD_MS, DEFAULT_OFFLINE_THRESHOLD_MS) const MACHINE_STALE_THRESHOLD_MS = parseThreshold(process.env.NEXT_PUBLIC_MACHINE_STALE_THRESHOLD_MS, DEFAULT_STALE_THRESHOLD_MS) const statusLabels: Record = { online: "Online", offline: "Offline", stale: "Sem sinal", maintenance: "Manutenção", blocked: "Bloqueada", deactivated: "Desativada", unknown: "Desconhecida", } const statusClasses: Record = { online: "border-emerald-500/20 bg-emerald-500/15 text-emerald-600", offline: "border-rose-500/20 bg-rose-500/15 text-rose-600", stale: "border-slate-400/30 bg-slate-200 text-slate-700", maintenance: "border-amber-500/20 bg-amber-500/15 text-amber-600", blocked: "border-orange-500/20 bg-orange-500/15 text-orange-600", deactivated: "border-slate-400/40 bg-slate-100 text-slate-600", unknown: "border-slate-300 bg-slate-200 text-slate-700", } const POSTURE_ALERT_LABELS: Record = { CPU_HIGH: "CPU alta", SERVICE_DOWN: "Serviço indisponível", SMART_FAIL: "Falha SMART", } function formatPostureAlertKind(raw?: string | null): string { if (!raw) return "Alerta" const normalized = raw.toUpperCase() if (POSTURE_ALERT_LABELS[normalized]) { return POSTURE_ALERT_LABELS[normalized] } return raw .toLowerCase() .replace(/_/g, " ") .replace(/\b\w/g, (char) => char.toUpperCase()) } function postureSeverityClass(severity?: string | null) { return (severity ?? "warning").toLowerCase() === "critical" ? "border-rose-500/20 bg-rose-500/10" : "border-amber-500/20 bg-amber-500/10" } function formatRelativeTime(date?: Date | null) { if (!date) return "Nunca" try { return formatDistanceToNowStrict(date, { addSuffix: true, locale: ptBR }) } catch { return "—" } } function formatInstallDate(date?: Date | null) { if (!date) return null return `${formatAbsoluteDateTime(date)} (${formatRelativeTime(date)})` } function formatDate(date?: Date | null) { if (!date) return "—" 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 parseDateish(value: unknown): Date | null { if (!value) return null if (value instanceof Date && !Number.isNaN(value.getTime())) return value if (typeof value === "number" && Number.isFinite(value)) { const numeric = value > 1e12 ? value : value > 1e9 ? value * 1000 : value const date = new Date(numeric) return Number.isNaN(date.getTime()) ? null : date } if (typeof value === "string") { const trimmed = value.trim() if (!trimmed) return null const numericValue = Number(trimmed) if (Number.isFinite(numericValue)) { const asMs = trimmed.length >= 13 ? numericValue : numericValue * 1000 const date = new Date(asMs) if (!Number.isNaN(date.getTime())) return date } const date = new Date(trimmed) if (!Number.isNaN(date.getTime())) return date } return null } function getMetricsTimestamp(metrics: MachineMetrics): Date | null { if (!metrics || typeof metrics !== "object") return null const data = metrics as Record const candidates = [ data["collectedAt"], data["collected_at"], data["collected_at_iso"], data["collected_at_ms"], data["timestamp"], data["updatedAt"], data["updated_at"], data["createdAt"], data["created_at"], ] for (const candidate of candidates) { const parsed = parseDateish(candidate) if (parsed) return parsed } return null } function formatBytes(bytes?: number | null) { if (!bytes || Number.isNaN(bytes)) return "—" const units = ["B", "KB", "MB", "GB", "TB"] let value = bytes let unitIndex = 0 while (value >= 1024 && unitIndex < units.length - 1) { value /= 1024 unitIndex += 1 } return `${value.toFixed(value >= 10 || unitIndex === 0 ? 0 : 1)} ${units[unitIndex]}` } function formatPercent(value?: number | null) { if (value === null || value === undefined || Number.isNaN(value)) return "—" const normalized = value > 1 ? value : value * 100 return `${normalized.toFixed(0)}%` } const BADGE_POSITIVE = "gap-1 border-emerald-500/20 bg-emerald-500/15 text-emerald-700" const BADGE_WARNING = "gap-1 border-amber-500/20 bg-amber-100 text-amber-700" const BADGE_NEUTRAL = "gap-1 border-slate-300 bg-slate-100 text-slate-600" function parseBooleanLike(value: unknown): boolean | undefined { if (typeof value === "boolean") return value if (typeof value === "number") { if (value === 1) return true if (value === 0) return false } if (typeof value === "string") { const normalized = value.trim().toLowerCase() if (["yes", "sim", "true", "enabled", "on", "ativo", "active", "y", "1"].includes(normalized)) return true if (["no", "não", "nao", "false", "disabled", "off", "inativo", "not joined", "n", "0"].includes(normalized)) return false } return undefined } function toNumberArray(value: unknown): number[] { if (!value) return [] if (Array.isArray(value)) { return value .map((item) => { if (typeof item === "number") return item if (typeof item === "string") { const parsed = Number(item) return Number.isNaN(parsed) ? null : parsed } return null }) .filter((item): item is number => item !== null) } if (typeof value === "number") return [value] if (typeof value === "string" && value.trim().length > 0) { return value .replace(/[^\d,]/g, " ") .split(/[,\s]+/) .map((part) => Number(part)) .filter((num) => !Number.isNaN(num)) } return [] } function describeDomainRole(role?: number | null): string | null { if (role === null || role === undefined) return null const map: Record = { 0: "Estação isolada", 1: "Estação em domínio", 2: "Servidor isolado", 3: "Servidor em domínio", 4: "Controlador de domínio (backup)", 5: "Controlador de domínio (primário)", } return map[role] ?? `Função ${role}` } function describePcSystemType(code?: number | null): string | null { if (code === null || code === undefined) return null const map: Record = { 0: "Não especificado", 1: "Desktop", 2: "Portátil / Laptop", 3: "Workstation", 4: "Servidor corporativo", 5: "Servidor SOHO", 8: "Tablet / Slate", 9: "Conversível", 10: "Sistema baseado em detecção", } return map[code] ?? `Tipo ${code}` } function describeDeviceGuardService(code: number): string { const map: Record = { 1: "Credential Guard", 2: "HVCI (Kernel Integrity)", 3: "Secure Boot com DMA", 4: "Hypervisor com Device Guard", 5: "Aplicação protegida", } return map[code] ?? `Serviço ${code}` } function describeVbsStatus(code?: number | null): string | null { switch (code) { case 0: return "VBS desabilitado" case 1: return "VBS habilitado (inativo)" case 2: return "VBS ativo (sem serviços)" case 3: return "VBS ativo com proteções" default: return code != null ? `VBS status ${code}` : null } } function describeAuOption(value?: number | null): string | null { switch (value ?? -1) { case 1: return "Não configurado" case 2: return "Notificar antes de baixar" case 3: return "Baixar automático e notificar" case 4: return "Baixar e instalar automaticamente" case 5: return "Administrador local define" default: return value != null ? `Opção ${value}` : null } } function describeScheduledDay(value?: number | null): string | null { if (value === null || value === undefined) return null if (value === 0) return "Todos os dias" const map = ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"] return map[value - 1] ?? `Dia ${value}` } function getStatusVariant(status?: string | null) { if (!status) return { label: statusLabels.unknown, className: statusClasses.unknown } const normalized = status.toLowerCase() return { label: statusLabels[normalized] ?? status, className: statusClasses[normalized] ?? statusClasses.unknown, } } function resolveMachineStatus(machine: { status?: string | null; lastHeartbeatAt?: number | null; isActive?: boolean | null }): string { if (machine.isActive === false) return "deactivated" const manualStatus = (machine.status ?? "").toLowerCase() if (["maintenance", "blocked"].includes(manualStatus)) { return manualStatus } const heartbeat = machine.lastHeartbeatAt if (typeof heartbeat === "number" && Number.isFinite(heartbeat) && heartbeat > 0) { const age = Date.now() - heartbeat if (age <= MACHINE_OFFLINE_THRESHOLD_MS) return "online" if (age <= MACHINE_STALE_THRESHOLD_MS) return "offline" return "stale" } return machine.status ?? "unknown" } function OsIcon({ osName }: { osName?: string | null }) { const name = (osName ?? "").toLowerCase() if (name.includes("mac") || name.includes("darwin") || name.includes("macos")) return if (name.includes("linux")) return // fallback para Windows/outros como monitor genérico return } export function AdminMachinesOverview({ tenantId }: { tenantId: string }) { const machines = useMachinesQuery(tenantId) const [q, setQ] = useState("") const [statusFilter, setStatusFilter] = useState("all") const [companyFilterSlug, setCompanyFilterSlug] = useState("all") const [companySearch, setCompanySearch] = useState("") const [onlyAlerts, setOnlyAlerts] = useState(false) const { convexUserId } = useAuth() const companies = useQuery( convexUserId ? api.companies.list : "skip", convexUserId ? { tenantId, viewerId: convexUserId as Id<"users"> } : ("skip" as const) ) as Array<{ id: string; name: string; slug?: string }> | undefined const companyNameBySlug = useMemo(() => { const map = new Map() ;(companies ?? []).forEach((c) => c.slug && map.set(c.slug, c.name)) return map }, [companies]) const companyOptions = useMemo(() => (companies ?? []).map((c) => ({ slug: c.slug ?? c.id, name: c.name })).sort((a,b)=>a.name.localeCompare(b.name,"pt-BR")), [companies]) const filteredMachines = useMemo(() => { const text = q.trim().toLowerCase() return machines.filter((m) => { if (onlyAlerts && !(Array.isArray(m.postureAlerts) && m.postureAlerts.length > 0)) return false if (statusFilter !== "all") { const s = resolveMachineStatus(m).toLowerCase() if (s !== statusFilter) return false } if (companyFilterSlug !== "all" && (m.companySlug ?? "") !== companyFilterSlug) return false if (!text) return true const hay = [ m.hostname, m.authEmail ?? "", (m.macAddresses ?? []).join(" "), (m.serialNumbers ?? []).join(" "), ] .join(" ") .toLowerCase() return hay.includes(text) }) }, [machines, q, statusFilter, companyFilterSlug, onlyAlerts]) return (
Máquinas registradas Sincronizadas via agente local ou Fleet. Atualiza em tempo real.
setQ(e.target.value)} placeholder="Buscar hostname, e-mail, MAC, serial..." />
setCompanySearch(e.target.value)} placeholder="Buscar empresa..." />
{companyOptions .filter((c) => c.name.toLowerCase().includes(companySearch.toLowerCase())) .map((c) => ( ))}
{machines.length === 0 ? ( ) : ( )}
) } function MachineStatusBadge({ status }: { status?: string | null }) { const { label, className } = getStatusVariant(status) const s = String(status ?? "").toLowerCase() const colorClass = s === "online" ? "bg-emerald-500" : s === "offline" ? "bg-rose-500" : s === "maintenance" ? "bg-amber-500" : s === "blocked" ? "bg-orange-500" : s === "deactivated" ? "bg-slate-500" : "bg-slate-400" const ringClass = s === "online" ? "bg-emerald-400/30" : s === "offline" ? "bg-rose-400/30" : s === "maintenance" ? "bg-amber-400/30" : s === "blocked" ? "bg-orange-400/30" : s === "deactivated" ? "bg-slate-400/40" : "bg-slate-300/30" const isOnline = s === "online" return ( {isOnline ? ( ) : null} {label} ) } function EmptyState() { return (

Nenhuma máquina registrada ainda

Execute o agente local ou o webhook do Fleet para registrar as máquinas do tenant.

) } type MachineDetailsProps = { machine: MachinesQueryItem | null } export function MachineDetails({ machine }: MachineDetailsProps) { const { convexUserId } = useAuth() const router = useRouter() const effectiveStatus = machine ? resolveMachineStatus(machine) : "unknown" const isActive = machine?.isActive ?? true const isDeactivated = !isActive || effectiveStatus === "deactivated" // Company name lookup (by slug) const companyQueryArgs = convexUserId && machine ? { tenantId: machine.tenantId, viewerId: convexUserId as Id<"users"> } : undefined const companies = useQuery( api.companies.list, companyQueryArgs ?? ("skip" as const) ) as Array<{ id: string; name: string; slug?: string }> | undefined const alertsHistory = useQuery( machine ? api.machines.listAlerts : "skip", machine ? { machineId: machine.id as Id<"machines">, limit: 50 } : ("skip" as const) ) as MachineAlertEntry[] | undefined const machineAlertsHistory = alertsHistory ?? [] const metadata = machine?.inventory ?? null const metrics = machine?.metrics ?? null const metricsCapturedAt = useMemo(() => getMetricsTimestamp(metrics), [metrics]) // Live refresh the relative time label every second when we have a capture timestamp const [, setRelativeTick] = useState(0) useEffect(() => { if (!metricsCapturedAt) return const id = setInterval(() => setRelativeTick((t) => t + 1), 1000) return () => clearInterval(id) }, [metricsCapturedAt]) const lastUpdateRelative = metricsCapturedAt ? formatRelativeTime(metricsCapturedAt) : null const hardware = metadata?.hardware const network = metadata?.network ?? null const networkInterfaces = Array.isArray(network) ? network : null const networkSummary = !Array.isArray(network) && network ? network : null const software = metadata?.software ?? null const labels = metadata?.labels ?? null const fleet = metadata?.fleet ?? null const disks = Array.isArray(metadata?.disks) ? metadata.disks : [] const extended = metadata?.extended ?? null 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 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 normalizedWindowsSoftware = useMemo(() => { return windowsSoftware .map((item) => normalizeWindowsSoftwareEntry(item)) .filter((entry): entry is NormalizedSoftwareEntry => Boolean(entry)) .sort((a, b) => { const aTime = a.installDate ? a.installDate.getTime() : 0 const bTime = b.installDate ? b.installDate.getTime() : 0 if (aTime !== bTime) return bTime - aTime return a.name.localeCompare(b.name, "pt-BR") }) }, [windowsSoftware]) const windowsEditionLabel = useMemo(() => { const raw = windowsOsInfo?.productName ?? windowsOsInfo?.caption ?? windowsOsInfo?.editionId ?? null if (!raw) return null return raw.replace(/^Microsoft\s+/i, "").trim() }, [windowsOsInfo?.productName, windowsOsInfo?.caption, windowsOsInfo?.editionId]) const windowsVersionLabel = windowsOsInfo?.displayVersion ?? windowsOsInfo?.version ?? windowsOsInfo?.releaseId ?? null const windowsBuildLabel = windowsOsInfo?.currentBuildNumber ?? windowsOsInfo?.currentBuild ?? null const windowsInstallDateLabel = windowsOsInfo?.installDate ? formatAbsoluteDateTime(windowsOsInfo.installDate) : null const windowsExperienceLabel = windowsOsInfo?.experience ?? null const windowsProductId = windowsOsInfo?.productId ?? null const windowsPartialProductKey = windowsOsInfo?.partialProductKey ?? null const windowsComputerName = windowsOsInfo?.computerName ?? hardware?.model ?? machine?.hostname ?? null const windowsRegisteredOwner = windowsOsInfo?.registeredOwner ?? null const windowsLicenseStatusLabel = (() => { if (windowsOsInfo?.licenseStatusText) { return windowsOsInfo.licenseStatusText } switch (windowsOsInfo?.licenseStatus) { case 0: return "Sem licença" case 1: return "Licenciado" case 2: return "Período inicial (OOB Grace)" case 3: return "Período exposto (OOT Grace)" case 4: return "Período não genuíno" case 5: return "Notificação" case 6: return "Período estendido" default: return null } })() const windowsBitLockerRaw = windowsExt?.bitLocker ?? windowsExt?.bitlocker ?? null const windowsBitLockerVolumes = useMemo(() => { if (Array.isArray(windowsBitLockerRaw)) return windowsBitLockerRaw if (windowsBitLockerRaw && typeof windowsBitLockerRaw === "object") return [windowsBitLockerRaw] return [] }, [windowsBitLockerRaw]) const windowsBitLockerSummary = useMemo(() => { if (windowsBitLockerVolumes.length === 0) return null let protectedCount = 0 let lockedCount = 0 windowsBitLockerVolumes.forEach((volume) => { const record = toRecord(volume) ?? {} const protection = readString(record, "ProtectionStatus", "protectionStatus")?.toLowerCase() if (protection && (protection.includes("on") || protection.includes("ativo"))) { protectedCount += 1 } const lockStatus = readString(record, "LockStatus", "lockStatus")?.toLowerCase() if (lockStatus && (lockStatus.includes("locked") || lockStatus.includes("bloqueado"))) { lockedCount += 1 } }) return { total: windowsBitLockerVolumes.length, protectedCount, lockedCount } }, [windowsBitLockerVolumes]) const windowsTpm = toRecord(windowsExt?.tpm ?? (windowsExt as Record | undefined)?.["TPM"]) const windowsSecureBoot = toRecord(windowsExt?.secureBoot ?? (windowsExt as Record | undefined)?.["secureboot"]) const windowsDeviceGuardRaw = windowsExt?.deviceGuard ?? (windowsExt as Record | undefined)?.["deviceguard"] ?? null const windowsDeviceGuard = useMemo(() => { if (Array.isArray(windowsDeviceGuardRaw)) return windowsDeviceGuardRaw if (windowsDeviceGuardRaw && typeof windowsDeviceGuardRaw === "object") return [windowsDeviceGuardRaw] return [] }, [windowsDeviceGuardRaw]) const windowsDeviceGuardDetails = useMemo(() => { if (!windowsDeviceGuard.length) return null const primary = toRecord(windowsDeviceGuard[0]) ?? {} const configured = toNumberArray(primary?.["SecurityServicesConfigured"]) const running = toNumberArray(primary?.["SecurityServicesRunning"]) const required = toNumberArray(primary?.["RequiredSecurityProperties"]) const available = toNumberArray(primary?.["AvailableSecurityProperties"]) const vbsRaw = primary?.["VirtualizationBasedSecurityStatus"] const vbs = typeof vbsRaw === "number" ? vbsRaw : typeof vbsRaw === "string" && vbsRaw.trim().length > 0 ? Number(vbsRaw) : undefined return { primary, configured, running, required, available, vbs } }, [windowsDeviceGuard]) const deviceGuardConfiguredLabels = windowsDeviceGuardDetails?.configured?.map((code) => describeDeviceGuardService(code)) ?? [] const deviceGuardRunningLabels = windowsDeviceGuardDetails?.running?.map((code) => describeDeviceGuardService(code)) ?? [] const windowsFirewallProfilesRaw = windowsExt?.firewallProfiles ?? (windowsExt as Record | undefined)?.["firewallprofiles"] ?? null const windowsFirewallProfiles = useMemo(() => { if (Array.isArray(windowsFirewallProfilesRaw)) return windowsFirewallProfilesRaw if (windowsFirewallProfilesRaw && typeof windowsFirewallProfilesRaw === "object") return [windowsFirewallProfilesRaw] return [] }, [windowsFirewallProfilesRaw]) const windowsUpdateSettings = toRecord(windowsExt?.windowsUpdate ?? (windowsExt as Record | undefined)?.["windowsupdate"]) const windowsUpdateLastSuccess = useMemo(() => { if (!windowsUpdateSettings) return null const candidates = [ windowsUpdateSettings["LastSuccessTime"], windowsUpdateSettings["lastSuccessTime"], windowsUpdateSettings["LastSuccessTimeUtc"], windowsUpdateSettings["lastSuccessTimeUtc"], ] for (const candidate of candidates) { const parsed = parseDateish(candidate) if (parsed) return parsed if (typeof candidate === "string" && candidate.trim().length > 0) { const parsedIso = new Date(candidate) if (!Number.isNaN(parsedIso.getTime())) return parsedIso } } return null }, [windowsUpdateSettings]) const windowsUpdateLastSuccessLabel = windowsUpdateLastSuccess ? formatAbsoluteDateTime(windowsUpdateLastSuccess) : null const windowsComputerSystem = toRecord( windowsExt?.computerSystem ?? (windowsExt as Record | undefined)?.["computersystem"] ) const windowsAzureAdStatusRaw = toRecord( windowsExt?.azureAdStatus ?? (windowsExt as Record | undefined)?.["azureadstatus"] ) const windowsAzureAdStatus = useMemo(() => { if (!windowsAzureAdStatusRaw) return null return Object.entries(windowsAzureAdStatusRaw).reduce>>((acc, [section, value]) => { acc[section] = toRecord(value) ?? {} return acc }, {}) }, [windowsAzureAdStatusRaw]) const secureBootSupported = windowsSecureBoot ? parseBooleanLike(windowsSecureBoot["Supported"] ?? windowsSecureBoot["supported"]) : undefined const secureBootEnabled = windowsSecureBoot ? parseBooleanLike(windowsSecureBoot["Enabled"] ?? windowsSecureBoot["enabled"]) : undefined const secureBootError = windowsSecureBoot ? readString(windowsSecureBoot, "Error", "error") : undefined const tpmPresent = windowsTpm ? parseBooleanLike(windowsTpm["TpmPresent"] ?? windowsTpm["tpmPresent"]) : undefined const tpmReady = windowsTpm ? parseBooleanLike(windowsTpm["TpmReady"] ?? windowsTpm["tpmReady"]) : undefined const tpmEnabled = windowsTpm ? parseBooleanLike(windowsTpm["TpmEnabled"] ?? windowsTpm["tpmEnabled"]) : undefined const tpmActivated = windowsTpm ? parseBooleanLike(windowsTpm["TpmActivated"] ?? windowsTpm["tpmActivated"]) : undefined const tpmManufacturer = windowsTpm ? readString(windowsTpm, "ManufacturerIdTxt", "manufacturerIdTxt", "ManufacturerId", "manufacturerId") : undefined const tpmVersion = windowsTpm ? readString(windowsTpm, "ManufacturerVersionFull20", "manufacturerVersionFull20", "ManufacturerVersion", "manufacturerVersion", "SpecVersion") : undefined const windowsFirewallNormalized = useMemo(() => { return windowsFirewallProfiles.map((profile) => { const record = toRecord(profile) ?? {} return { name: readString(record, "Name", "name") ?? "Perfil", enabled: parseBooleanLike(record["Enabled"] ?? record["enabled"]), inboundAction: readString(record, "DefaultInboundAction", "defaultInboundAction"), outboundAction: readString(record, "DefaultOutboundAction", "defaultOutboundAction"), notifyOnListen: parseBooleanLike(record["NotifyOnListen"] ?? record["notifyOnListen"]), } }) }, [windowsFirewallProfiles]) const firewallEnabledCount = windowsFirewallNormalized.filter((profile) => profile.enabled !== false).length const windowsUpdateMode = windowsUpdateSettings ? describeAuOption(readNumber(windowsUpdateSettings, "AUOptions", "auOptions")) : null const windowsUpdateDay = windowsUpdateSettings ? describeScheduledDay(readNumber(windowsUpdateSettings, "ScheduledInstallDay", "scheduledInstallDay")) : null const windowsUpdateHourRaw = windowsUpdateSettings ? readNumber(windowsUpdateSettings, "ScheduledInstallTime", "scheduledInstallTime") : undefined const windowsUpdateHour = windowsUpdateHourRaw != null ? `${windowsUpdateHourRaw.toString().padStart(2, "0")}h` : null const windowsUpdateDisabled = windowsUpdateSettings ? parseBooleanLike(windowsUpdateSettings["NoAutoUpdate"] ?? windowsUpdateSettings["noAutoUpdate"]) : undefined const windowsUpdateDetectionEnabled = windowsUpdateSettings ? parseBooleanLike( windowsUpdateSettings["DetectionFrequency"] ?? windowsUpdateSettings["DetectionFrequencyEnabled"] ?? windowsUpdateSettings["detectionFrequencyEnabled"], ) : undefined const windowsUpdateScheduleLabel = windowsUpdateDay || windowsUpdateHour ? `${windowsUpdateDay ?? ""}${windowsUpdateDay && windowsUpdateHour ? ` · ${windowsUpdateHour}` : windowsUpdateHour ?? ""}`.trim() : null const computerDomain = windowsComputerSystem ? readString(windowsComputerSystem, "Domain", "domain") : undefined const computerWorkgroup = windowsComputerSystem ? readString(windowsComputerSystem, "Workgroup", "workgroup") : undefined const computerPartOfDomain = windowsComputerSystem ? parseBooleanLike(windowsComputerSystem["PartOfDomain"] ?? windowsComputerSystem["partOfDomain"]) : undefined const computerDomainRole = windowsComputerSystem ? readNumber(windowsComputerSystem, "DomainRole", "domainRole") : undefined const computerManufacturer = windowsComputerSystem ? readString(windowsComputerSystem, "Manufacturer", "manufacturer") : undefined const computerModel = windowsComputerSystem ? readString(windowsComputerSystem, "Model", "model") : undefined const computerPcType = windowsComputerSystem ? readNumber(windowsComputerSystem, "PCSystemType", "pcSystemType", "PCSystemTypeEx", "pcSystemTypeEx") : undefined const computerTotalMemory = windowsComputerSystem ? readNumber(windowsComputerSystem, "TotalPhysicalMemory", "totalPhysicalMemory") : undefined const computerDomainRoleLabel = describeDomainRole(computerDomainRole) const computerPcTypeLabel = describePcSystemType(computerPcType) const computerTotalMemoryLabel = typeof computerTotalMemory === "number" ? formatBytes(computerTotalMemory) : null const azureDeviceState = windowsAzureAdStatus ? windowsAzureAdStatus["Device State"] ?? null : null const azureTenantDetails = windowsAzureAdStatus ? windowsAzureAdStatus["Tenant Details"] ?? null : null const azureUserState = windowsAzureAdStatus ? windowsAzureAdStatus["User State"] ?? null : null const azureAdJoined = azureDeviceState ? parseBooleanLike(azureDeviceState["AzureAdJoined"]) : undefined const azureDomainJoined = azureDeviceState ? parseBooleanLike(azureDeviceState["DomainJoined"]) : undefined const azureEnterpriseJoined = azureDeviceState ? parseBooleanLike(azureDeviceState["EnterpriseJoined"]) : undefined const azureTenantName = (azureDeviceState ? readString(azureDeviceState, "TenantName") : undefined) ?? (azureTenantDetails ? readString(azureTenantDetails, "TenantName") : undefined) const azureDeviceId = (azureDeviceState ? readString(azureDeviceState, "DeviceId") : undefined) ?? (azureTenantDetails ? readString(azureTenantDetails, "DeviceId") : undefined) const azureUserSso = azureUserState ? readString(azureUserState, "AzureAdPrt") ?? (azureUserState["AzureAdPrt"] ? String(azureUserState["AzureAdPrt"]) : undefined) : undefined const windowsDefender = windowsExt?.defender ?? null const defenderAntivirus = windowsDefender ? parseBooleanLike(windowsDefender["AntivirusEnabled"] ?? windowsDefender["antivirusEnabled"]) : undefined const defenderRealtime = windowsDefender ? parseBooleanLike(windowsDefender["RealTimeProtectionEnabled"] ?? windowsDefender["realTimeProtectionEnabled"]) : undefined const defenderMode = windowsDefender ? readString(windowsDefender, "AMRunningMode", "amRunningMode") : undefined const windowsHotfixes = useMemo(() => { if (!Array.isArray(windowsExt?.hotfix)) return [] return windowsExt.hotfix .map((entry) => { const record = toRecord(entry) ?? {} const id = readString(record, "HotFixID", "hotFixId", "HotfixId", "Id", "id") ?? "Atualização" const installedAt = parseDateish(record["InstalledOn"] ?? record["installedOn"]) const installedLabel = installedAt ? formatAbsoluteDateTime(installedAt) : readString(record, "InstalledOn", "installedOn") ?? "—" return { id, installedAt, installedLabel } }) .sort((a, b) => { if (a.installedAt && b.installedAt) { return b.installedAt.getTime() - a.installedAt.getTime() } if (a.installedAt) return -1 if (b.installedAt) return 1 return a.id.localeCompare(b.id) }) }, [windowsExt?.hotfix]) const osNameDisplay = useMemo(() => { const base = machine?.osName?.trim() const edition = windowsEditionLabel?.trim() if (edition) { if (!base) return edition const baseLower = base.toLowerCase() const editionLower = edition.toLowerCase() if (editionLower.includes(baseLower) || baseLower.includes(editionLower)) { return edition } if (baseLower.startsWith("windows") && editionLower.startsWith("windows")) { return edition } return `${base} ${edition}`.replace(/\s+/g, " ").trim() } return base ?? "" }, [machine?.osName, windowsEditionLabel]) const linuxLsblk = linuxExt?.lsblk ?? [] const linuxSmartEntries = linuxExt?.smart ?? [] const normalizedHardwareGpus = Array.isArray(hardware?.gpus) ? hardware.gpus.map((gpu) => normalizeGpuSource(gpu)).filter((gpu): gpu is GpuAdapter => Boolean(gpu)) : [] const hardwarePrimaryGpu = hardware?.primaryGpu ? normalizeGpuSource(hardware.primaryGpu) : null const windowsCpuRaw = windowsExt?.cpu const winCpu = windowsCpuRaw ? (Array.isArray(windowsCpuRaw) ? windowsCpuRaw[0] ?? null : windowsCpuRaw) : null const winMemTotal = windowsMemoryModules.reduce((acc, module) => acc + (parseBytesLike(module?.Capacity) ?? 0), 0) const normalizedWindowsGpus = windowsVideoControllers .map((controller) => normalizeGpuSource(controller)) .filter((gpu): gpu is GpuAdapter => Boolean(gpu)) const combinedGpus = uniqueBy( [ ...(hardwarePrimaryGpu ? [hardwarePrimaryGpu] : []), ...normalizedHardwareGpus, ...normalizedWindowsGpus, ], (gpu) => `${gpu.name ?? ""}|${gpu.vendor ?? ""}|${gpu.driver ?? ""}` ) const displayGpus = [...combinedGpus].sort( (a, b) => (b.memoryBytes ?? 0) - (a.memoryBytes ?? 0) ) const primaryGpu = hardwarePrimaryGpu ?? displayGpus[0] ?? null const windowsPrimaryGpu = [...normalizedWindowsGpus].sort( (a, b) => (b.memoryBytes ?? 0) - (a.memoryBytes ?? 0) )[0] ?? null const windowsCpuDetails = windowsCpuRaw ? Array.isArray(windowsCpuRaw) ? windowsCpuRaw : [windowsCpuRaw] : [] const winDiskStats = windowsDiskEntries.length > 0 ? { count: windowsDiskEntries.length, total: windowsDiskEntries.reduce((acc, disk) => acc + (parseBytesLike(disk?.Size) ?? 0), 0), } : { count: 0, total: 0 } const lastHeartbeatDate = machine?.lastHeartbeatAt ? new Date(machine.lastHeartbeatAt) : null const tokenExpiry = machine?.token?.expiresAt ? new Date(machine.token.expiresAt) : null const tokenLastUsed = machine?.token?.lastUsedAt ? new Date(machine.token.lastUsedAt) : null const copyEmail = async () => { if (!machine?.authEmail) return try { await navigator.clipboard.writeText(machine.authEmail) toast.success("E-mail da máquina copiado.") } catch { toast.error("Não foi possível copiar o e-mail da máquina.") } } // collaborator (from machine assignment or metadata) type Collaborator = { email?: string; name?: string; role?: string } const collaborator: Collaborator | null = useMemo(() => { if (machine?.assignedUserEmail) { return { email: machine.assignedUserEmail ?? undefined, name: machine.assignedUserName ?? undefined, role: machine.persona ?? machine.assignedUserRole ?? undefined, } } if (!metadata || typeof metadata !== "object") return null const inv = metadata as Record const c = inv["collaborator"] if (c && typeof c === "object") { const base = c as Record return { email: typeof base.email === "string" ? base.email : undefined, name: typeof base.name === "string" ? base.name : undefined, role: typeof base.role === "string" ? (base.role as string) : undefined, } } return null }, [machine?.assignedUserEmail, machine?.assignedUserName, machine?.persona, machine?.assignedUserRole, metadata]) const personaLabel = collaborator?.role === "manager" ? "Gestor" : "Colaborador" const summaryChips = useMemo(() => { const chips: Array<{ key: string; label: string; value: string; icon: ReactNode; tone?: "warning" | "muted" }> = [] const osName = osNameDisplay || "Sistema desconhecido" const osVersionRaw = machine?.osVersion ?? windowsVersionLabel ?? "" const osVersion = formatOsVersionDisplay(osNameDisplay, osVersionRaw) chips.push({ key: "os", label: "Sistema", value: [osName, osVersion].filter(Boolean).join(" ").trim(), icon: , }) if (machine?.architecture) { chips.push({ key: "arch", label: "Arquitetura", value: machine.architecture.toUpperCase(), icon: , }) } if (windowsBuildLabel) { chips.push({ key: "build", label: "Build", value: windowsBuildLabel, icon: , }) } if (windowsActivationStatus !== null && windowsActivationStatus !== undefined) { chips.push({ key: "activation", label: "Licença", value: windowsActivationStatus ? "Ativada" : "Não ativada", icon: windowsActivationStatus ? : , tone: windowsActivationStatus ? undefined : "warning", }) } if (primaryGpu?.name) { chips.push({ key: "gpu", label: "GPU principal", value: `${primaryGpu.name}${typeof primaryGpu.memoryBytes === "number" ? ` · ${formatBytes(primaryGpu.memoryBytes)}` : ""}`, icon: , }) } if (collaborator?.email) { const collaboratorValue = collaborator.name ? `${collaborator.name} · ${collaborator.email}` : collaborator.email chips.push({ key: "collaborator", label: personaLabel, value: collaboratorValue, icon: , }) } return chips }, [osNameDisplay, machine?.osVersion, machine?.architecture, windowsVersionLabel, windowsBuildLabel, windowsActivationStatus, primaryGpu, collaborator?.email, collaborator?.name, personaLabel, machine?.osName]) const companyName = (() => { if (!companies || !machine?.companySlug) return machine?.companySlug ?? null const found = companies.find((c) => c.slug === machine.companySlug) return found?.name ?? machine.companySlug })() const [renaming, setRenaming] = useState(false) const [newName, setNewName] = useState(machine?.hostname ?? "") const [openDialog, setOpenDialog] = useState(false) const [dialogQuery, setDialogQuery] = useState("") const [deleteDialog, setDeleteDialog] = useState(false) const [deleting, setDeleting] = useState(false) const [accessDialog, setAccessDialog] = useState(false) const [accessEmail, setAccessEmail] = useState(collaborator?.email ?? "") const [accessName, setAccessName] = useState(collaborator?.name ?? "") const [accessRole, setAccessRole] = useState<"collaborator" | "manager">( (machine?.persona === "manager" || collaborator?.role === "manager") ? "manager" : "collaborator" ) const [savingAccess, setSavingAccess] = useState(false) const [togglingActive, setTogglingActive] = useState(false) const [showAllWindowsSoftware, setShowAllWindowsSoftware] = useState(false) const jsonText = useMemo(() => { const payload = { id: machine?.id, hostname: machine?.hostname, status: machine?.status, lastHeartbeatAt: machine?.lastHeartbeatAt, metrics, inventory: metadata, postureAlerts: machine?.postureAlerts ?? null, lastPostureAt: machine?.lastPostureAt ?? null, } return JSON.stringify(payload, null, 2) }, [machine, metrics, metadata]) const handleDownloadInventory = useCallback(() => { if (!machine) return const safeHostname = machine.hostname.replace(/[^a-z0-9_-]/gi, "-").replace(/-{2,}/g, "-").toLowerCase() const fileName = `${safeHostname || "machine"}_${machine.id}.json` const blob = new Blob([jsonText], { type: "application/json" }) const url = URL.createObjectURL(blob) const link = document.createElement("a") link.href = url link.download = fileName document.body.appendChild(link) link.click() document.body.removeChild(link) URL.revokeObjectURL(url) }, [jsonText, machine]) const filteredJsonHtml = useMemo(() => { if (!dialogQuery.trim()) return jsonText const q = dialogQuery.trim().toLowerCase() // highlight simples return jsonText.replace(new RegExp(q.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi"), (m) => `__HIGHLIGHT__${m}__END__`) }, [jsonText, dialogQuery]) // removed copy/export inventory JSON buttons as requested useEffect(() => { setAccessEmail(collaborator?.email ?? "") setAccessName(collaborator?.name ?? "") 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 ? normalizedWindowsSoftware : normalizedWindowsSoftware.slice(0, 12)), [showAllWindowsSoftware, normalizedWindowsSoftware] ) const handleSaveAccess = async () => { if (!machine) return if (!accessEmail.trim()) { toast.error("Informe o e-mail do colaborador ou gestor.") return } setSavingAccess(true) try { const response = await fetch("/api/admin/machines/access", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ machineId: machine.id, persona: accessRole, email: accessEmail.trim(), name: accessName.trim() || undefined, }), }) if (!response.ok) { throw new Error(await response.text()) } toast.success("Perfil de acesso atualizado.") setAccessDialog(false) } catch (error) { console.error(error) toast.error("Falha ao atualizar acesso da máquina.") } finally { setSavingAccess(false) } } const handleToggleActive = async () => { if (!machine) return setTogglingActive(true) try { const response = await fetch("/api/admin/machines/toggle-active", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ machineId: machine.id, active: !isActive }), credentials: "include", }) if (!response.ok) { const payload = await response.json().catch(() => ({})) as { error?: string } throw new Error(payload?.error ?? "Falha ao atualizar status") } toast.success(!isActive ? "Máquina reativada" : "Máquina desativada") } catch (error) { console.error(error) toast.error("Não foi possível atualizar o status da máquina.") } finally { setTogglingActive(false) } } return ( Detalhes Resumo da máquina selecionada {machine ? (
{companyName ? (
{companyName}
) : null} {!isDeactivated ? : null} {!isActive ? ( Máquina desativada ) : null}
) : null}
{!machine ? (

Selecione uma máquina para visualizar detalhes.

) : (

{machine.hostname}

{machine.authEmail ?? "E-mail não definido"}

{/* ping integrado na badge de status */}
{summaryChips.map((chip) => ( ))}
{machine.authEmail ? ( ) : null} {machine.registeredBy ? ( Registrada via {machine.registeredBy} ) : null}

Usuários vinculados

{collaborator?.email ? (
{collaborator.name || collaborator.email} {collaborator.name ? `· ${collaborator.email}` : ''} Principal
) : null} {Array.isArray(machine.linkedUsers) && machine.linkedUsers.length > 0 ? (
    {machine.linkedUsers.map((u) => (
  • {u.name || u.email} {u.name ? `· ${u.email}` : ''}
  • ))}
) : null} {!collaborator?.email && (!machine.linkedUsers || machine.linkedUsers.length === 0) ? (

Nenhum usuário vinculado.

) : null}
setAccessEmail(e.target.value)} placeholder="e-mail do usuário para vincular" className="max-w-xs" type="email" /> Somente colaboradores/gestores. Gerenciar usuários
{/* Renomear máquina */} Renomear máquina
setNewName(e.target.value)} placeholder="Novo hostname" />
Ajustar acesso da máquina
setAccessEmail(e.target.value)} placeholder="colaborador@empresa.com" />
setAccessName(e.target.value)} placeholder="Nome completo" />

Sincronização

Último heartbeat {formatRelativeTime(lastHeartbeatDate)}
Criada em {formatDate(new Date(machine.createdAt))}
Atualizada em {formatDate(new Date(machine.updatedAt))}
Token expira {tokenExpiry ? formatRelativeTime(tokenExpiry) : "—"}
Token usado por último {tokenLastUsed ? formatRelativeTime(tokenLastUsed) : "—"}
Uso do token {machine.token?.usageCount ?? 0} trocas

Métricas recentes

{lastUpdateRelative ? ( Última atualização {lastUpdateRelative} ) : null}
{hardware || network || (labels && labels.length > 0) ? (

Inventário

Dados sincronizados via agente ou Fleet.

{hardware ? (

Hardware

{displayGpus.length > 0 ? (

GPUs

    {displayGpus.slice(0, 3).map((gpu, idx) => { const { name, memoryBytes, driver, vendor } = gpu return (
  • {name ?? "Adaptador de vídeo"} {memoryBytes ? {formatBytes(memoryBytes)} : null} {vendor ? · {vendor} : null} {driver ? · Driver {driver} : null}
  • ) })} {displayGpus.length > 3 ? (
  • +{displayGpus.length - 3} adaptadores adicionais
  • ) : null}
) : null}
) : null} {networkInterfaces ? (

Rede (interfaces)

Interface MAC IP {networkInterfaces.map((iface, idx) => ( {iface?.name ?? "—"} {iface?.mac ?? "—"} {iface?.ip ?? "—"} ))}
) : networkSummary ? (

Rede

) : null} {labels && labels.length > 0 ? (

Labels

{labels.slice(0, 12).map((label, index) => ( {label.name ?? `Label ${index + 1}`} ))} {labels.length > 12 ? ( +{labels.length - 12} outras ) : null}
) : null}
) : null} {/* Discos (agente) */} {disks.length > 0 ? (

Discos e partições

Nome Mount FS Capacidade Livre {disks.map((d, idx) => ( {d.name ?? "—"} {d.mountPoint ?? "—"} {d.fs ?? "—"} {formatBytes(Number(d.totalBytes))} {formatBytes(Number(d.availableBytes))} ))}
) : null} {/* Inventário estendido por SO */} {extended ? (

Inventário estendido

Dados ricos coletados pelo agente, variam por sistema operacional.

{/* Linux */} {linuxExt ? (
{linuxLsblk.length > 0 ? (

Montagens (lsblk)

Nome Ponto de montagem FS Tamanho {linuxLsblk.slice(0, 18).map((entry, idx) => { const name = entry.name ?? "—" const mp = entry.mountPoint ?? entry.mountpoint ?? "—" const fs = entry.fs ?? entry.fstype ?? "—" const sizeRaw = typeof entry.sizeBytes === "number" ? entry.sizeBytes : entry.size return ( {name} {mp || "—"} {fs || "—"} {typeof sizeRaw === "number" ? formatBytes(sizeRaw) : "—"} ) })}
) : null} {linuxSmartEntries.length > 0 ? (

SMART

{linuxSmartEntries.map((smartEntry, idx) => { const ok = smartEntry.smart_status?.passed !== false const model = smartEntry.model_name ?? smartEntry.model_family ?? "Disco" const serial = smartEntry.serial_number ?? smartEntry.device?.name ?? "—" return (
{model} ({serial}) {ok ? "OK" : "ALERTA"}
) })}
) : null} {linuxExt.lspci ? (

PCI

{linuxExt.lspci}
) : null} {linuxExt.lsusb ? (

USB

{linuxExt.lsusb}
) : null}
) : null} {/* Windows */} {windowsExt ? (
{/* Cards resumidos: CPU / RAM / GPU / Discos */}

CPU

{winCpu?.Name ?? "—"}

Memória total

{formatBytes(winMemTotal)}

Windows

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

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

GPU

{(windowsPrimaryGpu ?? primaryGpu)?.name ?? "—"}

Discos

{winDiskStats.count} · {formatBytes(winDiskStats.total)}

Sistema operacional Build, licença e data de instalação
{windowsVersionLabel ? ( {windowsVersionLabel} ) : null} {windowsBuildLabel ? ( Build {windowsBuildLabel} ) : null} {windowsLicenseStatusLabel ? ( {windowsActivationStatus ? : } {windowsLicenseStatusLabel} ) : null}
Identidade e dispositivo Associação a domínio, Azure AD e detalhes físicos
{computerPartOfDomain !== undefined ? ( {computerPartOfDomain ? : } {computerPartOfDomain ? `Domínio ativo${computerDomain ? ` (${computerDomain})` : ""}` : "Fora de domínio"} ) : null} {azureAdJoined !== undefined ? ( {azureAdJoined ? "Azure AD conectado" : "Azure AD não conectado"} ) : null} {azureDomainJoined !== undefined ? ( Domínio local: {azureDomainJoined ? "Sim" : "Não"} ) : null} {azureEnterpriseJoined !== undefined ? ( Enterprise: {azureEnterpriseJoined ? "Sim" : "Não"} ) : null}
{azureTenantName ? (

Tenant: {azureTenantName}

) : null} {azureDeviceId ? (

Device ID: {azureDeviceId}

) : null} {azureUserSso ? (

Usuário SSO: {azureUserSso}

) : null}
Atualizações do Windows Configurações automáticas e histórico recente
{windowsUpdateDisabled !== undefined ? ( {windowsUpdateDisabled ? : } {windowsUpdateDisabled ? "Atualizações desativadas" : "Atualizações automáticas"} ) : null} {windowsUpdateDetectionEnabled === false ? ( Sem detecção automática ) : null}
{windowsHotfixes.length > 0 ? (

Atualizações recentes

    {windowsHotfixes.slice(0, 3).map((fix) => (
  • {fix.id} {fix.installedLabel}
  • ))}
) : null}
Segurança do dispositivo Proteções do Windows, criptografia e firewall
{windowsActivationStatus !== null && windowsActivationStatus !== undefined ? ( {windowsActivationStatus ? : } {windowsActivationStatus ? "Licença ativada" : "Licença pendente"} ) : null} {defenderAntivirus !== undefined ? ( {defenderAntivirus ? : } Antivírus {defenderAntivirus ? "ativo" : "inativo"} ) : null} {defenderRealtime !== undefined ? ( {defenderRealtime ? : } Tempo real {defenderRealtime ? "ativo" : "inativo"} ) : null} {secureBootSupported !== undefined ? ( {secureBootEnabled ? : secureBootSupported ? : } {secureBootSupported ? secureBootEnabled === true ? "Secure Boot ativo" : "Secure Boot desabilitado" : "Secure Boot não suportado"} ) : null} {windowsBitLockerSummary ? ( {windowsBitLockerSummary.protectedCount}/{windowsBitLockerSummary.total} BitLocker ) : null} {tpmPresent !== undefined ? ( {tpmPresent ? (tpmReady ? "TPM pronto" : "TPM detectado") : "TPM ausente"} ) : null} {windowsDeviceGuardDetails ? ( {describeVbsStatus(windowsDeviceGuardDetails.vbs) ?? "VBS desconhecido"} ) : null} {windowsFirewallNormalized.length > 0 ? ( Firewall {firewallEnabledCount}/{windowsFirewallNormalized.length} ) : null}
{windowsDefender ? (

Defender

Modo: {defenderMode ?? "—"} Antivírus: {defenderAntivirus === undefined ? "—" : defenderAntivirus ? "Ativo" : "Inativo"} Tempo real: {defenderRealtime === undefined ? "—" : defenderRealtime ? "Ativo" : "Inativo"}
) : null} {secureBootError ? (
{secureBootError}
) : null} {windowsBitLockerVolumes.length > 0 ? (

BitLocker

{windowsBitLockerSummary ? `${windowsBitLockerSummary.protectedCount}/${windowsBitLockerSummary.total} protegidos` : `${windowsBitLockerVolumes.length} volumes`}
Volume Proteção Bloqueio Método % Cript. {windowsBitLockerVolumes.map((volume, idx) => { const record = toRecord(volume) ?? {} const mount = readString(record, "MountPoint", "mountPoint") ?? "—" const protection = readString(record, "ProtectionStatus", "protectionStatus") ?? "—" const lock = readString(record, "LockStatus", "lockStatus") ?? "—" const method = readString(record, "EncryptionMethod", "encryptionMethod") ?? "—" const percent = readNumber(record, "EncryptionPercentage", "encryptionPercentage") return ( {mount} {protection} {lock} {method} {percent != null ? formatPercent(percent) : "—"} ) })}
) : null} {windowsTpm ? (

TPM

Fabricante: {tpmManufacturer ?? "—"} Versão: {tpmVersion ?? "—"} Status:{" "} {tpmPresent === undefined ? "—" : [ tpmPresent ? "Presente" : "Ausente", tpmEnabled ? "Habilitado" : "Desabilitado", tpmReady ? "Pronto" : "Não pronto", tpmActivated ? "Ativado" : "Inativo", ].join(" · ")}
) : (

TPM: não foi possível coletar informações (requer privilégios administrativos na máquina).

)} {windowsDeviceGuardDetails ? (

Device Guard

{describeVbsStatus(windowsDeviceGuardDetails.vbs) ?? "—"} {deviceGuardConfiguredLabels.length > 0 ? ( Configurado: {deviceGuardConfiguredLabels.join(", ")} ) : null} {deviceGuardRunningLabels.length > 0 ? ( Em execução: {deviceGuardRunningLabels.join(", ")} ) : null}
) : null} {windowsFirewallNormalized.length > 0 ? (

Perfis de firewall

{firewallEnabledCount}/{windowsFirewallNormalized.length} ativos
Perfil Entrada Saída {windowsFirewallNormalized.map((profile, idx) => ( {profile.name} {profile.inboundAction ?? "—"} {profile.outboundAction ?? "—"} ))}
) : null}
{windowsCpuDetails.length > 0 ? (

CPU

{windowsCpuDetails.slice(0, 1).map((cpuRecord, idx) => (
))}
) : null} {windowsExt.baseboard || windowsExt.bios ? (

Placa-mãe / BIOS

{(() => { const b = Array.isArray(windowsExt.baseboard) ? windowsExt.baseboard[0] : windowsExt.baseboard const bios = Array.isArray(windowsExt.bios) ? windowsExt.bios[0] : windowsExt.bios return ( <> ) })()}
) : null} {windowsServices.length > 0 ? (

Serviços

Nome Exibição Status {windowsServices.slice(0, 10).map((service, index) => { const record = toRecord(service) ?? {} const name = readString(record, "Name", "name") ?? "—" const displayName = readString(record, "DisplayName", "displayName") ?? "—" const status = readString(record, "Status", "status") ?? "—" return ( {name} {displayName} {status} ) })}
) : null} {normalizedWindowsSoftware.length > 0 ? (

Aplicativos instalados

{normalizedWindowsSoftware.length > 12 ? ( ) : null}
    {displayedWindowsSoftware.map((softwareItem, index) => { const initials = collectInitials(softwareItem.name) const installedAt = formatInstallDate(softwareItem.installDate) return (
  • {initials}
    {softwareItem.name} {softwareItem.version ? ( {softwareItem.version} ) : null}
    {softwareItem.publisher ? {softwareItem.publisher} : null} {installedAt ? Instalado em {installedAt} : null}
  • ) })}
) : null} {windowsMemoryModules.length > 0 ? (

Módulos de memória

Banco Capacidade Fabricante PartNumber Clock {windowsMemoryModules.map((module, idx) => { const record = toRecord(module) ?? {} const bank = readString(record, "BankLabel", "bankLabel") ?? "—" const capacityBytes = parseBytesLike(record["Capacity"]) ?? parseBytesLike(record["capacity"]) ?? 0 const manufacturer = readString(record, "Manufacturer", "manufacturer") ?? "—" const partNumber = readString(record, "PartNumber", "partNumber") ?? "—" const clockValue = readNumber( record, "ConfiguredClockSpeed", "configuredClockSpeed", "Speed", "speed" ) const clockLabel = typeof clockValue === "number" && Number.isFinite(clockValue) ? `${clockValue} MHz` : "—" return ( {bank} {capacityBytes > 0 ? formatBytes(capacityBytes) : "—"} {manufacturer} {partNumber} {clockLabel} ) })}
) : null} {windowsVideoControllers.length > 0 ? (

Adaptadores de vídeo

    {windowsVideoControllers.map((controller, idx) => { const record = toRecord(controller) ?? {} const normalized = normalizeGpuSource(record) const name = normalized?.name ?? readString(record, "Name", "name") ?? "—" const ram = normalized?.memoryBytes ?? parseBytesLike(record["AdapterRAM"]) ?? undefined const driver = normalized?.driver ?? readString(record, "DriverVersion", "driverVersion") const vendor = normalized?.vendor ?? readString(record, "AdapterCompatibility") return (
  • {name} {typeof ram === "number" && ram > 0 ? {formatBytes(ram)} : null} {vendor ? · {vendor} : null} {driver ? · Driver {driver} : null}
  • ) })}
) : null} {windowsDiskEntries.length > 0 ? (

Discos físicos

Modelo Tamanho Interface Tipo Serial {windowsDiskEntries.map((disk, idx) => { const record = toRecord(disk) ?? {} const model = readString(record, "Model", "model") const serial = readString(record, "SerialNumber", "serialNumber") const size = parseBytesLike(record["Size"]) const iface = readString(record, "InterfaceType", "interfaceType") const media = readString(record, "MediaType", "mediaType") return ( {model ?? serial ?? "—"} {typeof size === "number" && size > 0 ? formatBytes(size) : "—"} {iface ?? "—"} {media ?? "—"} {serial ?? "—"} ) })}
) : null}
) : null} {/* macOS */} {macosExt ? (
{Array.isArray(macosExt.packages) && macosExt.packages.length > 0 ? (

Pacotes

{macosExt.packages.slice(0, 8).join(", ")}

) : null} {macosExt.launchctl ? (

Launchctl

{macosExt.launchctl}
) : null}
) : null}
) : null} {/* Postura/Alertas */} {Array.isArray(machine?.postureAlerts) && machine?.postureAlerts?.length ? (

Alertas de postura

{machine?.postureAlerts?.map((a: { kind?: string; message?: string; severity?: string }, i: number) => (
{a?.message ?? formatPostureAlertKind(a?.kind)} {formatPostureAlertKind(a?.kind)}
))}

Última avaliação: {machine?.lastPostureAt ? formatRelativeTime(new Date(machine.lastPostureAt)) : "—"}

) : null}

Histórico de alertas

{machineAlertsHistory.length > 0 ? ( Últimos {machineAlertsHistory.length} {machineAlertsHistory.length === 1 ? "evento" : "eventos"} ) : null}
{machineAlertsHistory.length > 0 ? (
    {machineAlertsHistory.map((alert) => { const date = new Date(alert.createdAt) return (
  1. {formatPostureAlertKind(alert.kind)} {formatRelativeTime(date)}

    {alert.message ?? formatPostureAlertKind(alert.kind)}

    {format(date, "dd/MM/yyyy HH:mm:ss")}

  2. ) })}
) : (

Nenhum alerta registrado para esta máquina.

)}
{Array.isArray(software) && software.length > 0 ? ( ) : null} {Array.isArray(metadata?.services) && metadata.services.length > 0 ? ( ) : null}
{fleet ? (
ID Fleet {fleet.id ?? "—"}
Team ID {fleet.teamId ?? "—"}
Detalhes atualizados {fleet.detailUpdatedAt ? formatDate(new Date(String(fleet.detailUpdatedAt))) : "—"}
Versão osquery {fleet.osqueryVersion ?? "—"}
) : null} {software && software.length > 0 ? (

Softwares detectados

Nome Versão Fonte {software.slice(0, 6).map((item, index) => ( {item.name ?? "—"} {item.version ?? "—"} {item.source ?? "—"} ))}
{software.length > 6 ? (

+{software.length - 6} softwares adicionais sincronizados via Fleet.

) : null}
) : null} {machine ? (

Zona perigosa

Excluir a máquina revoga o token atual e remove os dados de inventário sincronizados.

) : null}
Inventário completo — {machine.hostname}
setDialogQuery(e.target.value)} className="sm:flex-1" />
')
                      .replaceAll("__END__", '')
                    }} />
                  
{ if (!open) setDeleting(false); setDeleteDialog(open) }}> Excluir máquina

Tem certeza que deseja excluir {machine?.hostname}? Esta ação não pode ser desfeita.

Os tokens ativos serão revogados e o inventário deixará de aparecer no painel.

)}
); } function MachinesGrid({ machines, companyNameBySlug }: { machines: MachinesQueryItem[]; companyNameBySlug: Map }) { if (!machines || machines.length === 0) return return (
{machines.map((m) => ( ))}
) } function MachineCard({ machine, companyName }: { machine: MachinesQueryItem; companyName?: string | null }) { const effectiveStatus = resolveMachineStatus(machine) const isActive = machine.isActive const lastHeartbeat = machine.lastHeartbeatAt ? new Date(machine.lastHeartbeatAt) : null type AgentMetrics = { memoryUsedBytes?: number memoryTotalBytes?: number memoryUsedPercent?: number cpuUsagePercent?: number } const mm = (machine.metrics ?? null) as unknown as AgentMetrics | null const memUsed = mm?.memoryUsedBytes ?? NaN const memTotal = mm?.memoryTotalBytes ?? NaN const memPct = mm?.memoryUsedPercent ?? (Number.isFinite(memUsed) && Number.isFinite(memTotal) ? (Number(memUsed) / Number(memTotal)) * 100 : NaN) const cpuPct = mm?.cpuUsagePercent ?? NaN const collaborator = (() => { if (machine.assignedUserEmail) { return { email: machine.assignedUserEmail ?? undefined, name: machine.assignedUserName ?? undefined, role: machine.persona ?? machine.assignedUserRole ?? undefined, } } const inv = machine.inventory as unknown if (!inv || typeof inv !== "object") return null const raw = (inv as Record).collaborator if (!raw || typeof raw !== "object") return null const obj = raw as Record const email = typeof obj.email === "string" ? obj.email : undefined if (!email) return null return { email, name: typeof obj.name === "string" ? obj.name : undefined, role: typeof obj.role === "string" ? (obj.role as string) : undefined, } })() const persona = collaborator?.role === "manager" ? "Gestor" : "Colaborador" const companyLabel = companyName ?? machine.companySlug ?? null return (
{effectiveStatus === "online" ? ( ) : null}
{machine.hostname} {machine.authEmail ?? "—"} {collaborator?.email ? (
Usuário vinculado: {collaborator.name ? `${collaborator.name} · ` : ""}{collaborator.email} Gerenciar usuários
) : null} {!isActive ? ( Desativada ) : null}
{(() => { const name = machine.osName ?? "SO" const ver = formatOsVersionDisplay(machine.osName, machine.osVersion) return [name, ver].filter(Boolean).join(" ").trim() })()} {machine.architecture ? ( {machine.architecture.toUpperCase()} ) : null} {companyLabel ? ( {companyLabel} ) : null}
{collaborator?.email ? (

{persona}: {collaborator.name ? `${collaborator.name} · ` : ""} {collaborator.email}

) : null}
{formatPercent(cpuPct)}
{Number.isFinite(memUsed) && Number.isFinite(memTotal) ? `${formatBytes(memUsed)} / ${formatBytes(memTotal)}` : formatPercent(memPct)}
{Array.isArray(machine.inventory?.disks) ? `${machine.inventory?.disks?.length ?? 0} discos` : "—"} {lastHeartbeat ? formatRelativeTime(lastHeartbeat) : "sem heartbeat"}
) } function DetailLine({ label, value, classNameValue, layout = "spread" }: DetailLineProps) { if (value === null || value === undefined) return null if (typeof value === "string" && (value.trim() === "" || value === "undefined" || value === "null")) { return null } return (
{label} {value}
) } function InfoChip({ label, value, icon, tone = "default" }: { label: string; value: string; icon?: ReactNode; tone?: "default" | "warning" | "muted" }) { const toneClasses = tone === "warning" ? "border-amber-200 bg-amber-50 text-amber-700" : tone === "muted" ? "border-slate-200 bg-slate-50 text-neutral-600" : "border-slate-200 bg-white text-neutral-800" return (
{icon ? {icon} : null}

{label}

{value}

) } function clampPercent(raw: number): number { if (!Number.isFinite(raw)) return 0 const normalized = raw > 1 && raw <= 100 ? raw : raw <= 1 ? raw * 100 : raw return Math.max(0, Math.min(100, normalized)) } function deriveUsageMetrics({ metrics, hardware, disks, }: { metrics: MachineMetrics hardware?: MachineInventory["hardware"] disks?: MachineInventory["disks"] }) { const data = (metrics ?? {}) as Record const cpuRaw = Number( data.cpuUsagePercent ?? data.cpuUsage ?? data.cpu_percent ?? data.cpu ?? NaN ) const cpuPercent = Number.isFinite(cpuRaw) ? clampPercent(cpuRaw) : null const totalCandidates = [ data.memoryTotalBytes, data.memory_total, data.memoryTotal, hardware?.memoryBytes, hardware?.memory, ] let memoryTotalBytes: number | null = null for (const candidate of totalCandidates) { const parsed = parseBytesLike(candidate) if (parsed && Number.isFinite(parsed) && parsed > 0) { memoryTotalBytes = parsed break } const numeric = Number(candidate) if (Number.isFinite(numeric) && numeric > 0) { memoryTotalBytes = numeric break } } const usedCandidates = [ data.memoryUsedBytes, data.memoryBytes, data.memory_used, data.memory, ] let memoryUsedBytes: number | null = null for (const candidate of usedCandidates) { const parsed = parseBytesLike(candidate) if (parsed !== undefined && Number.isFinite(parsed)) { memoryUsedBytes = parsed break } const numeric = Number(candidate) if (Number.isFinite(numeric)) { memoryUsedBytes = numeric break } } const memoryPercentRaw = Number(data.memoryUsedPercent ?? data.memory_percent ?? NaN) let memoryPercent = Number.isFinite(memoryPercentRaw) ? clampPercent(memoryPercentRaw) : null if (memoryTotalBytes && memoryUsedBytes === null && memoryPercent !== null) { memoryUsedBytes = (memoryPercent / 100) * memoryTotalBytes } else if (memoryTotalBytes && memoryUsedBytes !== null) { memoryPercent = clampPercent((memoryUsedBytes / memoryTotalBytes) * 100) } let diskTotalBytes: number | null = null let diskUsedBytes: number | null = null let diskPercent: number | null = null if (Array.isArray(disks) && disks.length > 0) { let total = 0 let available = 0 disks.forEach((disk) => { const totalParsed = parseBytesLike(disk?.totalBytes) if (typeof totalParsed === "number" && Number.isFinite(totalParsed) && totalParsed > 0) { total += totalParsed } const availableParsed = parseBytesLike(disk?.availableBytes) if (typeof availableParsed === "number" && Number.isFinite(availableParsed) && availableParsed >= 0) { available += availableParsed } }) if (total > 0) { diskTotalBytes = total const used = Math.max(0, total - available) diskUsedBytes = used diskPercent = clampPercent((used / total) * 100) } } if (diskPercent === null) { const diskMetric = Number( data.diskUsage ?? data.disk ?? data.diskUsedPercent ?? data.storageUsedPercent ?? NaN ) if (Number.isFinite(diskMetric)) { diskPercent = clampPercent(diskMetric) } } const gpuMetric = Number( data.gpuUsagePercent ?? data.gpuUsage ?? data.gpu_percent ?? data.gpu ?? NaN ) const gpuPercent = Number.isFinite(gpuMetric) ? clampPercent(gpuMetric) : null return { cpuPercent, memoryUsedBytes, memoryTotalBytes, memoryPercent, diskPercent, diskUsedBytes, diskTotalBytes, gpuPercent, } } function MetricsGrid({ metrics, hardware, disks }: { metrics: MachineMetrics; hardware?: MachineInventory["hardware"]; disks?: MachineInventory["disks"] }) { const derived = useMemo( () => deriveUsageMetrics({ metrics, hardware, disks }), [metrics, hardware, disks] ) const cards = [ { key: "cpu", label: "CPU", percent: derived.cpuPercent, primaryText: derived.cpuPercent !== null ? formatPercent(derived.cpuPercent) : "Sem dados", secondaryText: derived.cpuPercent !== null ? "Uso instantâneo" : "Sem leituras recentes", icon: , color: "var(--chart-1)", }, { key: "memory", label: "Memória", percent: derived.memoryPercent, primaryText: derived.memoryUsedBytes !== null && derived.memoryTotalBytes !== null ? `${formatBytes(derived.memoryUsedBytes)} / ${formatBytes(derived.memoryTotalBytes)}` : derived.memoryPercent !== null ? formatPercent(derived.memoryPercent) : "Sem dados", secondaryText: derived.memoryPercent !== null ? `${Math.round(derived.memoryPercent)}% em uso` : null, icon: , color: "var(--chart-2)", }, { key: "disk", label: "Disco", percent: derived.diskPercent, primaryText: derived.diskUsedBytes !== null && derived.diskTotalBytes !== null ? `${formatBytes(derived.diskUsedBytes)} / ${formatBytes(derived.diskTotalBytes)}` : derived.diskPercent !== null ? formatPercent(derived.diskPercent) : "Sem dados", secondaryText: derived.diskPercent !== null ? `${Math.round(derived.diskPercent)}% utilizado` : null, icon: , color: "var(--chart-3)", }, ] as Array<{ key: string; label: string; percent: number | null; primaryText: string; secondaryText?: string | null; icon: ReactNode; color: string }> if (derived.gpuPercent !== null) { cards.push({ key: "gpu", label: "GPU", percent: derived.gpuPercent, primaryText: formatPercent(derived.gpuPercent), secondaryText: null, icon: , color: "var(--chart-4)", }) } return (
{cards.map((card) => { const percentValue = Number.isFinite(card.percent ?? NaN) ? Math.max(0, Math.min(100, card.percent ?? 0)) : 0 const percentLabel = card.percent !== null ? `${Math.round(card.percent)}%` : "—" return (
{percentLabel}
{card.icon} {card.label}
{card.primaryText}
{card.secondaryText ? (
{card.secondaryText}
) : null}
) })}
) } function exportCsv(items: Array>, filename: string) { if (!Array.isArray(items) || items.length === 0) return const headersSet = new Set() items.forEach((it) => Object.keys(it ?? {}).forEach((k) => headersSet.add(k))) const headers = Array.from(headersSet) const csv = [headers.join(",")] for (const it of items) { const row = headers .map((h) => { const v = (it as Record)[h] if (v === undefined || v === null) return "" const s = typeof v === "string" ? v : JSON.stringify(v) return `"${s.replace(/"/g, '""')}"` }) .join(",") csv.push(row) } const blob = new Blob([csv.join("\n")], { type: "text/csv;charset=utf-8;" }) const url = URL.createObjectURL(blob) const a = document.createElement("a") a.href = url a.download = filename document.body.appendChild(a) a.click() a.remove() URL.revokeObjectURL(url) }