feat: improve quick actions and remote access

This commit is contained in:
Esdras Renan 2025-11-18 21:16:00 -03:00
parent aeb6d50377
commit 4f8dad2255
10 changed files with 906 additions and 154 deletions

View file

@ -64,6 +64,7 @@ import { Separator } from "@/components/ui/separator"
import { ChartContainer } from "@/components/ui/chart"
import { cn } from "@/lib/utils"
import { DEVICE_INVENTORY_COLUMN_METADATA, type DeviceInventoryColumnConfig } from "@/lib/device-inventory-columns"
import { DEVICE_STATUS_LABELS, getDeviceStatusIndicator, resolveDeviceStatus } from "@/lib/device-status"
import { RadialBarChart, RadialBar, PolarAngleAxis } from "recharts"
import Link from "next/link"
import { useRouter } from "next/navigation"
@ -914,29 +915,6 @@ function useDevicesQuery(tenantId: string): { devices: DevicesQueryItem[]; isLoa
}
}
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<string, string> = {
online: "Online",
offline: "Offline",
stale: "Sem sinal",
maintenance: "Em manutenção",
blocked: "Bloqueado",
deactivated: "Desativado",
unknown: "Desconhecida",
}
const DEVICE_TYPE_LABELS: Record<string, string> = {
desktop: "Desktop",
mobile: "Celular",
@ -1236,30 +1214,14 @@ function describeScheduledDay(value?: number | null): string | null {
}
function getStatusVariant(status?: string | null) {
if (!status) return { label: statusLabels.unknown, className: statusClasses.unknown }
if (!status) return { label: DEVICE_STATUS_LABELS.unknown, className: statusClasses.unknown }
const normalized = status.toLowerCase()
return {
label: statusLabels[normalized] ?? status,
label: DEVICE_STATUS_LABELS[normalized] ?? status,
className: statusClasses[normalized] ?? statusClasses.unknown,
}
}
function resolveDeviceStatus(device: { status?: string | null; lastHeartbeatAt?: number | null; isActive?: boolean | null }): string {
if (device.isActive === false) return "deactivated"
const manualStatus = (device.status ?? "").toLowerCase()
if (["maintenance", "blocked"].includes(manualStatus)) {
return manualStatus
}
const heartbeat = device.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 device.status ?? "unknown"
}
function OsIcon({ osName }: { osName?: string | null }) {
const name = (osName ?? "").toLowerCase()
if (name.includes("mac") || name.includes("darwin") || name.includes("macos")) return <Apple className="size-4 text-black" />
@ -1977,7 +1939,7 @@ export function AdminDevicesOverview({
<ul className="divide-y divide-slate-100">
{filteredDevices.map((device) => {
const statusKey = resolveDeviceStatus(device)
const statusLabel = statusLabels[statusKey] ?? statusKey
const statusLabel = DEVICE_STATUS_LABELS[statusKey] ?? statusKey
const isChecked = exportSelection.includes(device.id)
const osParts = [device.osName ?? "", device.osVersion ?? ""].filter(Boolean)
const osLabel = osParts.join(" ")
@ -2360,37 +2322,7 @@ export function AdminDevicesOverview({
function DeviceStatusBadge({ 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 === "stale"
? "bg-amber-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 === "stale"
? "bg-amber-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"
const { dotClass, ringClass, isPinging } = getDeviceStatusIndicator(status)
return (
<Badge
variant="outline"
@ -2400,8 +2332,8 @@ function DeviceStatusBadge({ status }: { status?: string | null }) {
)}
>
<span className="relative inline-flex size-4 items-center justify-center">
<span className={cn("size-2.5 rounded-full", colorClass)} />
{isOnline ? (
<span className={cn("size-2.5 rounded-full", dotClass)} />
{isPinging ? (
<span
className={cn(
"absolute left-1/2 top-1/2 size-5 -translate-x-1/2 -translate-y-1/2 rounded-full animate-ping [animation-duration:2s]",