feat(admin): Windows hardware cards (CPU/RAM/GPU/Disks) with Lucide icons; feat(desktop): inventory summary cards; feat(agent/windows): extended hardware collectors (CPU/board/BIOS/memory/video/disks); fix(agent): memory units in bytes
This commit is contained in:
parent
fcd45ff034
commit
c70691bce8
6 changed files with 6200 additions and 126 deletions
125
apps/desktop/src-tauri/Cargo.lock
generated
125
apps/desktop/src-tauri/Cargo.lock
generated
|
|
@ -71,7 +71,6 @@ dependencies = [
|
|||
"sysinfo",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-keyring",
|
||||
"tauri-plugin-opener",
|
||||
"tauri-plugin-store",
|
||||
"thiserror 1.0.69",
|
||||
|
|
@ -545,16 +544,6 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.10.1"
|
||||
|
|
@ -578,7 +567,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"core-foundation 0.10.1",
|
||||
"core-foundation",
|
||||
"core-graphics-types",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
|
|
@ -591,7 +580,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"core-foundation 0.10.1",
|
||||
"core-foundation",
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
|
@ -729,27 +718,6 @@ dependencies = [
|
|||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dbus"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "190b6255e8ab55a7b568df5a883e9497edc3e4821c06396612048b430e5ad1e9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"libdbus-sys",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dbus-secret-service"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6"
|
||||
dependencies = [
|
||||
"dbus",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.4"
|
||||
|
|
@ -1987,21 +1955,6 @@ dependencies = [
|
|||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keyring"
|
||||
version = "3.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"dbus-secret-service",
|
||||
"log",
|
||||
"security-framework 2.11.1",
|
||||
"security-framework 3.5.1",
|
||||
"windows-sys 0.60.2",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kuchikiki"
|
||||
version = "0.8.8-speedreader"
|
||||
|
|
@ -2050,15 +2003,6 @@ version = "0.2.176"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
|
||||
|
||||
[[package]]
|
||||
name = "libdbus-sys"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cbe856efeb50e4681f010e9aaa2bf0a644e10139e54cde10fc83a307c23bd9f"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.4"
|
||||
|
|
@ -3473,42 +3417,6 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"core-foundation 0.9.4",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "3.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"core-foundation 0.10.1",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "selectors"
|
||||
version = "0.24.0"
|
||||
|
|
@ -3958,7 +3866,7 @@ checksum = "959469667dbcea91e5485fc48ba7dd6023face91bb0f1a14681a70f99847c3f7"
|
|||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"block2 0.6.2",
|
||||
"core-foundation 0.10.1",
|
||||
"core-foundation",
|
||||
"core-graphics",
|
||||
"crossbeam-channel",
|
||||
"dispatch",
|
||||
|
|
@ -4139,19 +4047,6 @@ dependencies = [
|
|||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-keyring"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a52455d6472f3c0f9cac1b1f017cfce85e177db32ccd5f4b50e2e31deeaf25e"
|
||||
dependencies = [
|
||||
"keyring",
|
||||
"serde",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-opener"
|
||||
version = "2.5.0"
|
||||
|
|
@ -5796,20 +5691,6 @@ name = "zeroize"
|
|||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
|
|
|
|||
|
|
@ -193,7 +193,8 @@ fn build_inventory_metadata(system: &System) -> serde_json::Value {
|
|||
.first()
|
||||
.map(|cpu| cpu.brand().to_string())
|
||||
.unwrap_or_default();
|
||||
let mem_total_bytes = system.total_memory().saturating_mul(1024);
|
||||
// sysinfo 0.31 já retorna bytes em total_memory/used_memory
|
||||
let mem_total_bytes = system.total_memory();
|
||||
let network = collect_network_addrs();
|
||||
let disks = collect_disks(system);
|
||||
let mut inventory = json!({
|
||||
|
|
@ -489,12 +490,26 @@ fn collect_windows_extended() -> serde_json::Value {
|
|||
let defender = ps("Get-MpComputerStatus | Select-Object AMRunningMode,AntivirusEnabled,RealTimeProtectionEnabled,AntispywareEnabled").unwrap_or_else(|| json!({}));
|
||||
let hotfix = ps("Get-HotFix | Select-Object HotFixID,InstalledOn").unwrap_or_else(|| json!([]));
|
||||
|
||||
// Hardware detalhado (CPU/Board/BIOS/Memória/Vídeo/Discos)
|
||||
let cpu = ps("Get-CimInstance Win32_Processor | Select-Object Name,Manufacturer,SocketDesignation,NumberOfCores,NumberOfLogicalProcessors,L2CacheSize,L3CacheSize,MaxClockSpeed").unwrap_or_else(|| json!({}));
|
||||
let baseboard = ps("Get-CimInstance Win32_BaseBoard | Select-Object Product,Manufacturer,SerialNumber,Version").unwrap_or_else(|| json!({}));
|
||||
let bios = ps("Get-CimInstance Win32_BIOS | Select-Object Manufacturer,SMBIOSBIOSVersion,ReleaseDate,Version").unwrap_or_else(|| json!({}));
|
||||
let memory = ps("Get-CimInstance Win32_PhysicalMemory | Select-Object BankLabel,Capacity,Manufacturer,PartNumber,SerialNumber,ConfiguredClockSpeed,Speed,ConfiguredVoltage").unwrap_or_else(|| json!([]));
|
||||
let video = ps("Get-CimInstance Win32_VideoController | Select-Object Name,AdapterRAM,DriverVersion,PNPDeviceID").unwrap_or_else(|| json!([]));
|
||||
let disks = ps("Get-CimInstance Win32_DiskDrive | Select-Object Model,SerialNumber,Size,InterfaceType,MediaType").unwrap_or_else(|| json!([]));
|
||||
|
||||
json!({
|
||||
"windows": {
|
||||
"software": software,
|
||||
"services": services,
|
||||
"defender": defender,
|
||||
"hotfix": hotfix,
|
||||
"cpu": cpu,
|
||||
"baseboard": baseboard,
|
||||
"bios": bios,
|
||||
"memoryModules": memory,
|
||||
"videoControllers": video,
|
||||
"disks": disks,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -544,8 +559,9 @@ fn collect_metrics(system: &System) -> MachineMetrics {
|
|||
let collected_at = Utc::now();
|
||||
let total_memory = system.total_memory();
|
||||
let used_memory = system.used_memory();
|
||||
let memory_total_bytes = total_memory.saturating_mul(1024);
|
||||
let memory_used_bytes = used_memory.saturating_mul(1024);
|
||||
// sysinfo 0.31: valores já em bytes
|
||||
let memory_total_bytes = total_memory;
|
||||
let memory_used_bytes = used_memory;
|
||||
let memory_used_percent = if total_memory > 0 {
|
||||
(used_memory as f32 / total_memory as f32) * 100.0
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -233,6 +233,9 @@ function renderInventoryPanel(inv: Record<string, unknown>, profile: MachineProf
|
|||
if (!panel) return
|
||||
const disks = Array.isArray((inv as any).disks) ? ((inv as any).disks as any[]) : []
|
||||
const network = (inv as any).network
|
||||
const mem = profile.metrics
|
||||
const totalDisk = disks.reduce((acc, d) => acc + Number(d?.totalBytes ?? 0), 0)
|
||||
const disksCount = disks.length
|
||||
|
||||
let networkHtml = ""
|
||||
if (Array.isArray(network)) {
|
||||
|
|
@ -277,6 +280,12 @@ function renderInventoryPanel(inv: Record<string, unknown>, profile: MachineProf
|
|||
: ""
|
||||
|
||||
panel.innerHTML = `
|
||||
<div class="cards-grid">
|
||||
<div class="card-item"><span class="card-icon">🧠</span><div><div class="text-xs">CPU</div><div class="text-sm"><strong>${formatPercent(mem.cpuUsagePercent)}</strong></div></div></div>
|
||||
<div class="card-item"><span class="card-icon">🧩</span><div><div class="text-xs">Memória</div><div class="text-sm"><strong>${formatBytes(mem.memoryUsedBytes)}</strong> / ${formatBytes(mem.memoryTotalBytes)}</div></div></div>
|
||||
<div class="card-item"><span class="card-icon">🖥️</span><div><div class="text-xs">Sistema</div><div class="text-sm"><strong>${profile.os.name}</strong></div></div></div>
|
||||
<div class="card-item"><span class="card-icon">💾</span><div><div class="text-xs">Discos</div><div class="text-sm"><strong>${disksCount}</strong> · ${formatBytes(totalDisk)}</div></div></div>
|
||||
</div>
|
||||
${networkHtml}
|
||||
${disksHtml}
|
||||
${softwareHtml}
|
||||
|
|
|
|||
|
|
@ -193,6 +193,26 @@ button:hover:not(:disabled) {
|
|||
display: block;
|
||||
}
|
||||
|
||||
/* Summary cards */
|
||||
.cards-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||||
gap: 12px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.card-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.5);
|
||||
background-color: rgba(248, 250, 252, 0.85);
|
||||
padding: 10px 12px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.card-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { useQuery } from "convex/react"
|
|||
import { format, formatDistanceToNowStrict } from "date-fns"
|
||||
import { ptBR } from "date-fns/locale"
|
||||
import { toast } from "sonner"
|
||||
import { ClipboardCopy, ServerCog } from "lucide-react"
|
||||
import { ClipboardCopy, ServerCog, Cpu, MemoryStick, Monitor, HardDrive } from "lucide-react"
|
||||
|
||||
import { api } from "@/convex/_generated/api"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
|
|
@ -53,6 +53,21 @@ type WindowsExtended = {
|
|||
services?: Array<Record<string, unknown>>
|
||||
defender?: Record<string, unknown>
|
||||
hotfix?: Array<Record<string, unknown>>
|
||||
cpu?: Record<string, unknown> | Array<Record<string, unknown>>
|
||||
baseboard?: Record<string, unknown> | Array<Record<string, unknown>>
|
||||
bios?: Record<string, unknown> | Array<Record<string, unknown>>
|
||||
memoryModules?: Array<{
|
||||
BankLabel?: string
|
||||
Capacity?: number
|
||||
Manufacturer?: string
|
||||
PartNumber?: string
|
||||
SerialNumber?: string
|
||||
ConfiguredClockSpeed?: number
|
||||
Speed?: number
|
||||
ConfiguredVoltage?: number
|
||||
}>
|
||||
videoControllers?: Array<{ Name?: string; AdapterRAM?: number; DriverVersion?: string; PNPDeviceID?: string }>
|
||||
disks?: Array<{ Model?: string; SerialNumber?: string; Size?: number; InterfaceType?: string; MediaType?: string }>
|
||||
}
|
||||
|
||||
type MacExtended = {
|
||||
|
|
@ -409,6 +424,22 @@ function MachineDetails({ machine }: MachineDetailsProps) {
|
|||
const windowsExt = extended?.windows ?? null
|
||||
const macosExt = extended?.macos ?? null
|
||||
|
||||
const winCpu = Array.isArray(windowsExt?.cpu)
|
||||
? (windowsExt?.cpu as Array<Record<string, unknown>>)[0] ?? null
|
||||
: (windowsExt?.cpu as Record<string, unknown> | null)
|
||||
const winMemTotal = Array.isArray(windowsExt?.memoryModules)
|
||||
? (windowsExt?.memoryModules as Array<{ Capacity?: number }>).reduce((acc, m) => acc + Number(m?.Capacity ?? 0), 0)
|
||||
: 0
|
||||
const winGpu = Array.isArray(windowsExt?.videoControllers)
|
||||
? (windowsExt?.videoControllers as Array<Record<string, unknown>>)[0] ?? null
|
||||
: null
|
||||
const winDiskStats = Array.isArray(windowsExt?.disks)
|
||||
? {
|
||||
count: (windowsExt?.disks as Array<Record<string, unknown>>).length,
|
||||
total: (windowsExt?.disks as Array<{ Size?: number }>).reduce((acc, d) => acc + Number(d?.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
|
||||
|
|
@ -746,6 +777,83 @@ function MachineDetails({ machine }: MachineDetailsProps) {
|
|||
{/* Windows */}
|
||||
{windowsExt ? (
|
||||
<div className="space-y-3">
|
||||
{/* Cards resumidos: CPU / RAM / GPU / Discos */}
|
||||
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
||||
<Card className="border-slate-200">
|
||||
<CardContent className="flex items-center gap-3 py-3">
|
||||
<Cpu className="size-5 text-slate-500" />
|
||||
<div className="min-w-0">
|
||||
<p className="truncate text-xs text-muted-foreground">CPU</p>
|
||||
<p className="truncate text-sm font-semibold text-foreground">{String((winCpu as any)?.Name ?? "—")}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="border-slate-200">
|
||||
<CardContent className="flex items-center gap-3 py-3">
|
||||
<MemoryStick className="size-5 text-slate-500" />
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">Memória total</p>
|
||||
<p className="text-sm font-semibold text-foreground">{formatBytes(winMemTotal)}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="border-slate-200">
|
||||
<CardContent className="flex items-center gap-3 py-3">
|
||||
<Monitor className="size-5 text-slate-500" />
|
||||
<div className="min-w-0">
|
||||
<p className="truncate text-xs text-muted-foreground">GPU</p>
|
||||
<p className="truncate text-sm font-semibold text-foreground">{String((winGpu as any)?.Name ?? "—")}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="border-slate-200">
|
||||
<CardContent className="flex items-center gap-3 py-3">
|
||||
<HardDrive className="size-5 text-slate-500" />
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">Discos</p>
|
||||
<p className="text-sm font-semibold text-foreground">{winDiskStats.count} · {formatBytes(winDiskStats.total)}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
{windowsExt.cpu ? (
|
||||
<div className="rounded-md border border-slate-200 bg-slate-50/60 p-3">
|
||||
<p className="text-xs font-semibold uppercase text-slate-500">CPU</p>
|
||||
{Array.isArray(windowsExt.cpu) ? (
|
||||
(windowsExt.cpu as Array<Record<string, unknown>>).slice(0,1).map((c, i) => (
|
||||
<div key={`cpu-${i}`} className="mt-2 grid gap-1 text-sm text-muted-foreground">
|
||||
<DetailLine label="Modelo" value={String(c?.["Name"] ?? "—")} />
|
||||
<DetailLine label="Fabricante" value={String(c?.["Manufacturer"] ?? "—")} />
|
||||
<DetailLine label="Socket" value={String(c?.["SocketDesignation"] ?? "—")} />
|
||||
<DetailLine label="Núcleos" value={String(c?.["NumberOfCores"] ?? "—")} />
|
||||
<DetailLine label="Threads" value={String(c?.["NumberOfLogicalProcessors"] ?? "—")} />
|
||||
<DetailLine label="L2" value={c?.["L2CacheSize"] ? `${c["L2CacheSize"]} KB` : "—"} />
|
||||
<DetailLine label="L3" value={c?.["L3CacheSize"] ? `${c["L3CacheSize"]} KB` : "—"} />
|
||||
<DetailLine label="Clock máx" value={c?.["MaxClockSpeed"] ? `${c["MaxClockSpeed"]} MHz` : "—"} />
|
||||
</div>
|
||||
))
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{windowsExt.baseboard || windowsExt.bios ? (
|
||||
<div className="rounded-md border border-slate-200 bg-slate-50/60 p-3">
|
||||
<p className="text-xs font-semibold uppercase text-slate-500">Placa-mãe / BIOS</p>
|
||||
<div className="mt-2 grid gap-1 text-sm text-muted-foreground">
|
||||
{(() => {
|
||||
const b = Array.isArray(windowsExt.baseboard) ? windowsExt.baseboard[0] : windowsExt.baseboard
|
||||
const bios = Array.isArray(windowsExt.bios) ? windowsExt.bios[0] : windowsExt.bios
|
||||
return (
|
||||
<>
|
||||
<DetailLine label="Board" value={b ? `${String(b?.["Manufacturer"] ?? "")} ${String(b?.["Product"] ?? "")}`.trim() : "—"} />
|
||||
<DetailLine label="Board SN" value={b ? String(b?.["SerialNumber"] ?? "—") : "—"} />
|
||||
<DetailLine label="BIOS" value={bios ? `${String(bios?.["Manufacturer"] ?? "")} ${String(bios?.["SMBIOSBIOSVersion"] ?? "")}`.trim() : "—"} />
|
||||
</>
|
||||
)
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{Array.isArray(windowsExt.services) ? (
|
||||
<div className="rounded-md border border-slate-200 bg-slate-50/60 p-3">
|
||||
<p className="text-xs font-semibold uppercase text-slate-500">Serviços</p>
|
||||
|
|
@ -787,6 +895,81 @@ function MachineDetails({ machine }: MachineDetailsProps) {
|
|||
</div>
|
||||
) : null}
|
||||
|
||||
{Array.isArray(windowsExt.memoryModules) && windowsExt.memoryModules.length > 0 ? (
|
||||
<div className="rounded-md border border-slate-200 bg-slate-50/60 p-3">
|
||||
<p className="text-xs font-semibold uppercase text-slate-500">Módulos de memória</p>
|
||||
<div className="mt-2 overflow-hidden rounded-md border border-slate-200">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="border-slate-200 bg-slate-100/80">
|
||||
<TableHead className="text-xs text-slate-500">Banco</TableHead>
|
||||
<TableHead className="text-xs text-slate-500">Capacidade</TableHead>
|
||||
<TableHead className="text-xs text-slate-500">Fabricante</TableHead>
|
||||
<TableHead className="text-xs text-slate-500">PartNumber</TableHead>
|
||||
<TableHead className="text-xs text-slate-500">Clock</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{(windowsExt.memoryModules as Array<any>).map((m, idx) => (
|
||||
<TableRow key={`mem-${idx}`} className="border-slate-100">
|
||||
<TableCell className="text-sm">{m?.BankLabel ?? "—"}</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">{formatBytes(Number(m?.Capacity ?? 0))}</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">{m?.Manufacturer ?? "—"}</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">{m?.PartNumber ?? "—"}</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">{m?.ConfiguredClockSpeed ?? m?.Speed ? `${m?.ConfiguredClockSpeed ?? m?.Speed} MHz` : "—"}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{Array.isArray(windowsExt.videoControllers) && windowsExt.videoControllers.length > 0 ? (
|
||||
<div className="rounded-md border border-slate-200 bg-slate-50/60 p-3">
|
||||
<p className="text-xs font-semibold uppercase text-slate-500">Adaptadores de vídeo</p>
|
||||
<ul className="mt-2 grid gap-1 text-xs text-muted-foreground">
|
||||
{(windowsExt.videoControllers as Array<any>).map((v, idx) => (
|
||||
<li key={`vid-${idx}`}>
|
||||
<span className="font-medium text-foreground">{v?.Name ?? "—"}</span>
|
||||
{v?.AdapterRAM ? <span className="ml-1">{formatBytes(Number(v.AdapterRAM))}</span> : null}
|
||||
{v?.DriverVersion ? <span className="ml-1">· Driver {v.DriverVersion}</span> : null}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{Array.isArray(windowsExt.disks) && windowsExt.disks.length > 0 ? (
|
||||
<div className="rounded-md border border-slate-200 bg-slate-50/60 p-3">
|
||||
<p className="text-xs font-semibold uppercase text-slate-500">Discos físicos</p>
|
||||
<div className="mt-2 overflow-hidden rounded-md border border-slate-200">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="border-slate-200 bg-slate-100/80">
|
||||
<TableHead className="text-xs text-slate-500">Modelo</TableHead>
|
||||
<TableHead className="text-xs text-slate-500">Tamanho</TableHead>
|
||||
<TableHead className="text-xs text-slate-500">Interface</TableHead>
|
||||
<TableHead className="text-xs text-slate-500">Tipo</TableHead>
|
||||
<TableHead className="text-xs text-slate-500">Serial</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{(windowsExt.disks as Array<any>).map((d, idx) => (
|
||||
<TableRow key={`diskp-${idx}`} className="border-slate-100">
|
||||
<TableCell className="text-sm">{d?.Model ?? "—"}</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">{formatBytes(Number(d?.Size ?? 0))}</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">{d?.InterfaceType ?? "—"}</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">{d?.MediaType ?? "—"}</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">{d?.SerialNumber ?? "—"}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{windowsExt.defender ? (
|
||||
<div className="rounded-md border border-slate-200 bg-slate-50/60 p-3">
|
||||
<p className="text-xs font-semibold uppercase text-slate-500">Defender</p>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue