Ajusta placeholders, formulários e widgets

This commit is contained in:
Esdras Renan 2025-11-06 23:13:41 -03:00
parent 343f0c8c64
commit b94cea2f9a
33 changed files with 2122 additions and 462 deletions

View file

@ -2441,6 +2441,9 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
const effectiveStatus = device ? resolveDeviceStatus(device) : "unknown"
const [isActiveLocal, setIsActiveLocal] = useState<boolean>(device?.isActive ?? true)
const isDeactivated = !isActiveLocal || effectiveStatus === "deactivated"
const isManualMobile =
(device?.managementMode ?? "").toLowerCase() === "manual" &&
(device?.deviceType ?? "").toLowerCase() === "mobile"
const alertsHistory = useQuery(
api.devices.listAlerts,
device ? { machineId: device.id as Id<"machines">, limit: 50 } : "skip"
@ -3578,6 +3581,11 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
<h1 className="break-words text-2xl font-semibold text-neutral-900">
{device.displayName ?? device.hostname ?? "Dispositivo"}
</h1>
{isManualMobile ? (
<span className="rounded-full border border-slate-200 bg-slate-50 px-2 py-0.5 text-xs font-semibold uppercase tracking-wide text-neutral-600">
Identificação interna
</span>
) : null}
<Button
size="icon"
variant="ghost"
@ -3723,29 +3731,33 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
<ShieldCheck className="size-4" />
Ajustar acesso
</Button>
<Button
size="sm"
variant="outline"
className="gap-2 border-dashed border-amber-300 text-amber-700 hover:border-amber-400 hover:text-amber-800"
onClick={handleResetAgent}
disabled={isResettingAgent}
>
<RefreshCcw className={cn("size-4", isResettingAgent && "animate-spin")} />
{isResettingAgent ? "Resetando agente..." : "Resetar agente"}
</Button>
<Button
size="sm"
variant={isActiveLocal ? "outline" : "default"}
className={cn(
"gap-2 border-dashed",
!isActiveLocal && "bg-emerald-600 text-white hover:bg-emerald-600/90"
)}
onClick={handleToggleActive}
disabled={togglingActive}
>
{isActiveLocal ? <Power className="size-4" /> : <PlayCircle className="size-4" />}
{isActiveLocal ? (togglingActive ? "Desativando..." : "Desativar") : togglingActive ? "Reativando..." : "Reativar"}
</Button>
{!isManualMobile ? (
<>
<Button
size="sm"
variant="outline"
className="gap-2 border-dashed border-amber-300 text-amber-700 hover:border-amber-400 hover:text-amber-800"
onClick={handleResetAgent}
disabled={isResettingAgent}
>
<RefreshCcw className={cn("size-4", isResettingAgent && "animate-spin")} />
{isResettingAgent ? "Resetando agente..." : "Resetar agente"}
</Button>
<Button
size="sm"
variant={isActiveLocal ? "outline" : "default"}
className={cn(
"gap-2 border-dashed",
!isActiveLocal && "bg-emerald-600 text-white hover:bg-emerald-600/90"
)}
onClick={handleToggleActive}
disabled={togglingActive}
>
{isActiveLocal ? <Power className="size-4" /> : <PlayCircle className="size-4" />}
{isActiveLocal ? (togglingActive ? "Desativando..." : "Desativar") : togglingActive ? "Reativando..." : "Reativar"}
</Button>
</>
) : null}
{device.registeredBy ? (
<span
className={cn(
@ -4157,55 +4169,59 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
</DialogContent>
</Dialog>
<section className="space-y-2">
<h4 className="text-sm font-semibold">Sincronização</h4>
<div className="grid gap-2 text-sm text-muted-foreground">
<div className="flex justify-between gap-4">
<span>Último heartbeat</span>
<span className="text-right font-medium text-foreground">
{formatRelativeTime(lastHeartbeatDate)}
</span>
{!isManualMobile ? (
<section className="space-y-2">
<h4 className="text-sm font-semibold">Sincronização</h4>
<div className="grid gap-2 text-sm text-muted-foreground">
<div className="flex justify-between gap-4">
<span>Último heartbeat</span>
<span className="text-right font-medium text-foreground">
{formatRelativeTime(lastHeartbeatDate)}
</span>
</div>
<div className="flex justify-between gap-4">
<span>Criada em</span>
<span className="text-right font-medium text-foreground">{formatDate(new Date(device.createdAt))}</span>
</div>
<div className="flex justify-between gap-4">
<span>Atualizada em</span>
<span className="text-right font-medium text-foreground">{formatDate(new Date(device.updatedAt))}</span>
</div>
<div className="flex justify-between gap-4">
<span>Token expira</span>
<span className="text-right font-medium text-foreground">
{tokenExpiry ? formatRelativeTime(tokenExpiry) : "—"}
</span>
</div>
<div className="flex justify-between gap-4">
<span>Token usado por último</span>
<span className="text-right font-medium text-foreground">
{tokenLastUsed ? formatRelativeTime(tokenLastUsed) : "—"}
</span>
</div>
<div className="flex justify-between gap-4">
<span>Uso do token</span>
<span className="text-right font-medium text-foreground">{device.token?.usageCount ?? 0} trocas</span>
</div>
</div>
<div className="flex justify-between gap-4">
<span>Criada em</span>
<span className="text-right font-medium text-foreground">{formatDate(new Date(device.createdAt))}</span>
</div>
<div className="flex justify-between gap-4">
<span>Atualizada em</span>
<span className="text-right font-medium text-foreground">{formatDate(new Date(device.updatedAt))}</span>
</div>
<div className="flex justify-between gap-4">
<span>Token expira</span>
<span className="text-right font-medium text-foreground">
{tokenExpiry ? formatRelativeTime(tokenExpiry) : "—"}
</span>
</div>
<div className="flex justify-between gap-4">
<span>Token usado por último</span>
<span className="text-right font-medium text-foreground">
{tokenLastUsed ? formatRelativeTime(tokenLastUsed) : "—"}
</span>
</div>
<div className="flex justify-between gap-4">
<span>Uso do token</span>
<span className="text-right font-medium text-foreground">{device.token?.usageCount ?? 0} trocas</span>
</div>
</div>
</section>
</section>
) : null}
<section className="space-y-2">
<div className="flex flex-wrap items-center gap-2">
<h4 className="text-sm font-semibold">Métricas recentes</h4>
{lastUpdateRelative ? (
<span className="text-xs font-medium text-muted-foreground">
Última atualização {lastUpdateRelative}
</span>
) : null}
</div>
<MetricsGrid metrics={metrics} hardware={hardware} disks={disks} />
</section>
{!isManualMobile ? (
<section className="space-y-2">
<div className="flex flex-wrap items-center gap-2">
<h4 className="text-sm font-semibold">Métricas recentes</h4>
{lastUpdateRelative ? (
<span className="text-xs font-medium text-muted-foreground">
Última atualização {lastUpdateRelative}
</span>
) : null}
</div>
<MetricsGrid metrics={metrics} hardware={hardware} disks={disks} />
</section>
) : null}
{hardware || network || (labels && labels.length > 0) ? (
{!isManualMobile && (hardware || network || (labels && labels.length > 0)) ? (
<section className="space-y-3">
<div>
<h4 className="text-sm font-semibold">Inventário</h4>
@ -5145,39 +5161,41 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
</section>
) : null}
<section className="space-y-2">
<div className="flex items-center justify-between">
<h4 className="text-sm font-semibold">Histórico de alertas</h4>
{deviceAlertsHistory.length > 0 ? (
<span className="text-xs text-muted-foreground">
Últimos {deviceAlertsHistory.length} {deviceAlertsHistory.length === 1 ? "evento" : "eventos"}
</span>
) : null}
</div>
{deviceAlertsHistory.length > 0 ? (
<div className="relative max-h-64 overflow-y-auto pr-2">
<div className="absolute left-3 top-3 bottom-3 w-px bg-slate-200" />
<ol className="space-y-3 pl-6">
{deviceAlertsHistory.map((alert) => {
const date = new Date(alert.createdAt)
return (
<li key={alert.id} className="relative rounded-md border border-slate-200/80 bg-white px-3 py-2 text-xs shadow-sm">
<span className="absolute -left-5 top-3 inline-flex size-3 items-center justify-center rounded-full border border-white bg-slate-200 ring-2 ring-white" />
<div className={cn("flex items-center justify-between", postureSeverityClass(alert.severity))}>
<span className="text-xs font-medium uppercase tracking-wide text-slate-600">{formatPostureAlertKind(alert.kind)}</span>
<span className="text-xs text-slate-500">{formatRelativeTime(date)}</span>
</div>
<p className="mt-1 text-sm text-foreground">{alert.message ?? formatPostureAlertKind(alert.kind)}</p>
<p className="mt-1 text-[11px] text-muted-foreground">{format(date, "dd/MM/yyyy HH:mm:ss")}</p>
</li>
)
})}
</ol>
{!isManualMobile ? (
<section className="space-y-2">
<div className="flex items-center justify-between">
<h4 className="text-sm font-semibold">Histórico de alertas</h4>
{deviceAlertsHistory.length > 0 ? (
<span className="text-xs text-muted-foreground">
Últimos {deviceAlertsHistory.length} {deviceAlertsHistory.length === 1 ? "evento" : "eventos"}
</span>
) : null}
</div>
) : (
<p className="text-xs text-muted-foreground">Nenhum alerta registrado para este dispositivo.</p>
)}
</section>
{deviceAlertsHistory.length > 0 ? (
<div className="relative max-h-64 overflow-y-auto pr-2">
<div className="absolute left-3 top-3 bottom-3 w-px bg-slate-200" />
<ol className="space-y-3 pl-6">
{deviceAlertsHistory.map((alert) => {
const date = new Date(alert.createdAt)
return (
<li key={alert.id} className="relative rounded-md border border-slate-200/80 bg-white px-3 py-2 text-xs shadow-sm">
<span className="absolute -left-5 top-3 inline-flex size-3 items-center justify-center rounded-full border border-white bg-slate-200 ring-2 ring-white" />
<div className={cn("flex items-center justify-between", postureSeverityClass(alert.severity))}>
<span className="text-xs font-medium uppercase tracking-wide text-slate-600">{formatPostureAlertKind(alert.kind)}</span>
<span className="text-xs text-slate-500">{formatRelativeTime(date)}</span>
</div>
<p className="mt-1 text-sm text-foreground">{alert.message ?? formatPostureAlertKind(alert.kind)}</p>
<p className="mt-1 text-[11px] text-muted-foreground">{format(date, "dd/MM/yyyy HH:mm:ss")}</p>
</li>
)
})}
</ol>
</div>
) : (
<p className="text-xs text-muted-foreground">Nenhum alerta registrado para este dispositivo.</p>
)}
</section>
) : null}
<div className="flex flex-wrap gap-2 pt-2">
{Array.isArray(software) && software.length > 0 ? (