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

@ -0,0 +1,98 @@
"use client"
import { useCallback, useMemo } from "react"
import { useQuery } from "convex/react"
import { toast } from "sonner"
import { api } from "@/convex/_generated/api"
import type { Id } from "@/convex/_generated/dataModel"
import { useAuth } from "@/lib/auth-client"
import type { TicketWithDetails } from "@/lib/schemas/ticket"
import {
buildRustDeskUri,
isRustDeskAccess,
normalizeDeviceRemoteAccessList,
type DeviceRemoteAccessEntry,
} from "@/components/admin/devices/admin-devices-overview"
import { DEVICE_STATUS_LABELS, resolveDeviceStatus } from "@/lib/device-status"
type RemoteAccessResult = {
canShowRemoteAccess: boolean
primaryRemoteAccess: DeviceRemoteAccessEntry | null
statusKey: string
statusLabel: string
hostname: string | null
connect: () => void
}
export function useTicketRemoteAccess(ticket: TicketWithDetails): RemoteAccessResult {
const { isStaff } = useAuth()
const machineId = ticket.machine?.id ?? null
const canQueryDevice = isStaff && Boolean(machineId)
const deviceRecord = useQuery(
api.devices.getById,
canQueryDevice
? ({
id: machineId as Id<"machines">,
includeMetadata: true,
} as const)
: "skip"
) as Record<string, unknown> | null | undefined
const remoteAccessEntries = useMemo(() => {
if (!deviceRecord) return []
const source = (deviceRecord as { remoteAccess?: unknown }).remoteAccess
return normalizeDeviceRemoteAccessList(source)
}, [deviceRecord])
const primaryRemoteAccess = useMemo(
() => remoteAccessEntries.find((entry) => isRustDeskAccess(entry)) ?? null,
[remoteAccessEntries]
)
const hostname =
ticket.machine?.hostname ?? ((deviceRecord as { hostname?: string | null })?.hostname ?? null)
const statusKey = useMemo(() => {
if (deviceRecord && typeof deviceRecord === "object") {
return resolveDeviceStatus(
deviceRecord as { status?: string | null; lastHeartbeatAt?: number | null; isActive?: boolean | null }
)
}
const fallback = ticket.machine?.status
return typeof fallback === "string" && fallback.length > 0 ? fallback.toLowerCase() : "unknown"
}, [deviceRecord, ticket.machine?.status])
const handleConnect = useCallback(() => {
if (!primaryRemoteAccess) {
toast.error("Nenhum acesso remoto RustDesk cadastrado para esta máquina.")
return
}
const link = buildRustDeskUri(primaryRemoteAccess)
if (!link) {
toast.error("Não foi possível montar o link do RustDesk (ID ou senha ausentes).")
return
}
if (typeof window === "undefined") {
toast.error("A conexão direta só funciona no navegador.")
return
}
try {
window.location.href = link
toast.success("Abrindo o RustDesk...")
} catch (error) {
console.error(error)
toast.error("Não foi possível acionar o RustDesk neste dispositivo.")
}
}, [primaryRemoteAccess])
return {
canShowRemoteAccess: Boolean(canQueryDevice),
primaryRemoteAccess,
statusKey,
statusLabel: DEVICE_STATUS_LABELS[statusKey] ?? statusKey,
hostname,
connect: handleConnect,
}
}