feat(devices): exibe bateria, sensores termicos, rede, monitores e chassis
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
Quality Checks / Lint, Test and Build (push) Has been cancelled

- Adiciona tipos TypeScript para novos dados do Windows
- Exibe informacoes de bateria com status traduzido
- Exibe sensores termicos em tabela
- Exibe adaptadores de rede com velocidade e status
- Exibe monitores conectados com fabricante e serial
- Exibe info do chassis/gabinete com tipo traduzido

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rever-tecnologia 2025-12-18 08:13:26 -03:00
parent f0c2bdc283
commit 3e63589055

View file

@ -271,6 +271,64 @@ type WindowsDiskEntry = {
MediaType?: string
}
type WindowsBattery = {
Name?: string
DeviceID?: string
Status?: string
BatteryStatus?: number
BatteryStatusText?: string
EstimatedChargeRemaining?: number
EstimatedRunTime?: number
DesignCapacity?: number
FullChargeCapacity?: number
DesignVoltage?: number
Chemistry?: number
BatteryRechargeTime?: number
}
type WindowsBatteryInfo = {
Present: boolean
Batteries: WindowsBattery[]
}
type WindowsThermalSensor = {
Source?: string
Name?: string
TemperatureCelsius?: number
CriticalTripPoint?: number
Parent?: string
}
type WindowsNetworkAdapter = {
Name?: string
Description?: string
MACAddress?: string
Speed?: number
NetConnectionStatus?: number
StatusText?: string
AdapterType?: string
Manufacturer?: string
NetConnectionID?: string
PNPDeviceID?: string
}
type WindowsMonitor = {
ManufacturerName?: string
ProductCodeID?: string
SerialNumberID?: string
UserFriendlyName?: string
YearOfManufacture?: number
WeekOfManufacture?: number
}
type WindowsChassis = {
ChassisTypes?: number[]
ChassisTypesText?: string[]
Manufacturer?: string
SerialNumber?: string
SMBIOSAssetTag?: string
}
type WindowsExtended = {
software?: DeviceSoftware[]
services?: Array<{ name?: string; status?: string; displayName?: string }>
@ -292,6 +350,11 @@ type WindowsExtended = {
windowsUpdate?: Record<string, unknown>
computerSystem?: Record<string, unknown>
azureAdStatus?: Record<string, unknown>
battery?: WindowsBatteryInfo
thermal?: WindowsThermalSensor[]
networkAdapters?: WindowsNetworkAdapter[]
monitors?: WindowsMonitor[]
chassis?: WindowsChassis
}
type MacExtended = {
@ -3015,6 +3078,21 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
return a.id.localeCompare(b.id)
})
}, [windowsExt?.hotfix])
// Novos dados do agente: bateria, sensores termicos, adaptadores de rede, monitores, chassis
const windowsBatteryInfo = windowsExt?.battery ?? null
const windowsThermalSensors = useMemo(() => {
if (Array.isArray(windowsExt?.thermal)) return windowsExt.thermal
return []
}, [windowsExt?.thermal])
const windowsNetworkAdapters = useMemo(() => {
if (Array.isArray(windowsExt?.networkAdapters)) return windowsExt.networkAdapters
return []
}, [windowsExt?.networkAdapters])
const windowsMonitors = useMemo(() => {
if (Array.isArray(windowsExt?.monitors)) return windowsExt.monitors
return []
}, [windowsExt?.monitors])
const windowsChassisInfo = windowsExt?.chassis ?? null
const osNameDisplay = useMemo(() => {
const base = device?.osName?.trim()
const edition = windowsEditionLabel?.trim()
@ -5506,6 +5584,143 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
</div>
) : null}
{/* Bateria */}
{windowsBatteryInfo?.Present && windowsBatteryInfo.Batteries.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">Bateria</p>
<div className="mt-2 space-y-2">
{windowsBatteryInfo.Batteries.map((battery, idx) => (
<div key={`bat-${idx}`} className="grid gap-1 text-sm text-muted-foreground">
<DetailLine label="Nome" value={battery.Name ?? "Bateria"} />
<DetailLine
label="Status"
value={battery.BatteryStatusText ?? (battery.BatteryStatus != null ? `Código ${battery.BatteryStatus}` : "—")}
/>
{battery.EstimatedChargeRemaining != null && (
<DetailLine label="Carga" value={`${battery.EstimatedChargeRemaining}%`} />
)}
{battery.EstimatedRunTime != null && battery.EstimatedRunTime > 0 && battery.EstimatedRunTime < 71582788 && (
<DetailLine label="Tempo restante" value={`${Math.floor(battery.EstimatedRunTime / 60)}h ${battery.EstimatedRunTime % 60}min`} />
)}
{battery.DesignCapacity != null && battery.DesignCapacity > 0 && (
<DetailLine label="Capacidade de projeto" value={`${battery.DesignCapacity} mWh`} />
)}
{battery.FullChargeCapacity != null && battery.FullChargeCapacity > 0 && (
<DetailLine label="Capacidade atual" value={`${battery.FullChargeCapacity} mWh`} />
)}
</div>
))}
</div>
</div>
) : null}
{/* Sensores térmicos */}
{windowsThermalSensors.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">Sensores térmicos</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">Sensor</TableHead>
<TableHead className="text-xs text-slate-500">Temperatura</TableHead>
<TableHead className="text-xs text-slate-500">Crítico</TableHead>
<TableHead className="text-xs text-slate-500">Fonte</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{windowsThermalSensors.map((sensor, idx) => (
<TableRow key={`thermal-${idx}`} className="border-slate-100">
<TableCell className="text-sm">{sensor.Name ?? sensor.Parent ?? "Sensor"}</TableCell>
<TableCell className="text-sm text-muted-foreground">
{sensor.TemperatureCelsius != null ? `${sensor.TemperatureCelsius}°C` : "—"}
</TableCell>
<TableCell className="text-sm text-muted-foreground">
{sensor.CriticalTripPoint != null ? `${sensor.CriticalTripPoint}°C` : "—"}
</TableCell>
<TableCell className="text-sm text-muted-foreground">{sensor.Source ?? "—"}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
) : null}
{/* Adaptadores de rede */}
{windowsNetworkAdapters.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 rede</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">Nome</TableHead>
<TableHead className="text-xs text-slate-500">MAC</TableHead>
<TableHead className="text-xs text-slate-500">Velocidade</TableHead>
<TableHead className="text-xs text-slate-500">Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{windowsNetworkAdapters.slice(0, 10).map((adapter, idx) => (
<TableRow key={`netadapter-${idx}`} className="border-slate-100">
<TableCell className="text-sm">{adapter.NetConnectionID ?? adapter.Name ?? "—"}</TableCell>
<TableCell className="text-sm text-muted-foreground">{adapter.MACAddress ?? "—"}</TableCell>
<TableCell className="text-sm text-muted-foreground">
{adapter.Speed != null && adapter.Speed > 0
? adapter.Speed >= 1000000000
? `${(adapter.Speed / 1000000000).toFixed(1)} Gbps`
: `${(adapter.Speed / 1000000).toFixed(0)} Mbps`
: "—"}
</TableCell>
<TableCell className="text-sm text-muted-foreground">{adapter.StatusText ?? "—"}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
) : null}
{/* Monitores */}
{windowsMonitors.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">Monitores</p>
<ul className="mt-2 grid gap-2 text-sm text-muted-foreground">
{windowsMonitors.map((monitor, idx) => (
<li key={`monitor-${idx}`} className="rounded border border-slate-200 bg-white px-3 py-2">
<span className="font-medium text-foreground">
{monitor.UserFriendlyName ?? monitor.ManufacturerName ?? "Monitor"}
</span>
<div className="mt-1 flex flex-wrap gap-2 text-xs">
{monitor.ManufacturerName && <span>Fabricante: {monitor.ManufacturerName}</span>}
{monitor.SerialNumberID && <span>· Serial: {monitor.SerialNumberID}</span>}
{monitor.YearOfManufacture && <span>· Ano: {monitor.YearOfManufacture}</span>}
</div>
</li>
))}
</ul>
</div>
) : null}
{/* Chassis / Gabinete */}
{windowsChassisInfo ? (
<div className="rounded-md border border-slate-200 bg-slate-50/60 p-3">
<p className="text-xs font-semibold uppercase text-slate-500">Gabinete / Chassis</p>
<div className="mt-2 grid gap-1 text-sm text-muted-foreground">
<DetailLine
label="Tipo"
value={windowsChassisInfo.ChassisTypesText?.join(", ") ?? "—"}
/>
<DetailLine label="Fabricante" value={windowsChassisInfo.Manufacturer ?? "—"} />
<DetailLine label="Serial" value={windowsChassisInfo.SerialNumber ?? "—"} />
{windowsChassisInfo.SMBIOSAssetTag && windowsChassisInfo.SMBIOSAssetTag !== "Default string" && (
<DetailLine label="Asset Tag" value={windowsChassisInfo.SMBIOSAssetTag} />
)}
</div>
</div>
) : null}
</div>
) : null}