feat: improve quick actions and remote access
This commit is contained in:
parent
aeb6d50377
commit
4f8dad2255
10 changed files with 906 additions and 154 deletions
|
|
@ -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]",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue