feat: add agent reset flow and document machine handover
This commit is contained in:
parent
28796bf105
commit
25d2a9b062
6 changed files with 196 additions and 8 deletions
|
|
@ -2280,6 +2280,7 @@ export function MachineDetails({ machine }: MachineDetailsProps) {
|
|||
[editingRemoteAccessClientId, remoteAccessEntries]
|
||||
)
|
||||
const [togglingActive, setTogglingActive] = useState(false)
|
||||
const [isResettingAgent, setIsResettingAgent] = useState(false)
|
||||
const [showAllWindowsSoftware, setShowAllWindowsSoftware] = useState(false)
|
||||
const jsonText = useMemo(() => {
|
||||
const payload = {
|
||||
|
|
@ -2569,6 +2570,34 @@ export function MachineDetails({ machine }: MachineDetailsProps) {
|
|||
}
|
||||
}
|
||||
|
||||
const handleResetAgent = useCallback(async () => {
|
||||
if (!machine) return
|
||||
toast.dismiss("machine-reset")
|
||||
toast.loading("Resetando agente...", { id: "machine-reset" })
|
||||
setIsResettingAgent(true)
|
||||
try {
|
||||
const response = await fetch("/api/admin/machines/reset-agent", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
credentials: "include",
|
||||
body: JSON.stringify({ machineId: machine.id }),
|
||||
})
|
||||
const payload = (await response.json().catch(() => null)) as { error?: string; revoked?: number } | null
|
||||
if (!response.ok) {
|
||||
const message = payload?.error ?? "Falha ao resetar agente."
|
||||
throw new Error(message)
|
||||
}
|
||||
const revokedLabel = typeof payload?.revoked === "number" && payload.revoked > 0 ? ` (${payload.revoked} token(s) revogados)` : ""
|
||||
toast.success(`Agente resetado${revokedLabel}. Reprovisione o agente na máquina.`, { id: "machine-reset" })
|
||||
router.refresh()
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "Falha ao resetar agente."
|
||||
toast.error(message, { id: "machine-reset" })
|
||||
} finally {
|
||||
setIsResettingAgent(false)
|
||||
}
|
||||
}, [machine, router])
|
||||
|
||||
const handleCopyRemoteIdentifier = useCallback(async (identifier: string | null | undefined) => {
|
||||
if (!identifier) return
|
||||
try {
|
||||
|
|
@ -2702,6 +2731,16 @@ export function MachineDetails({ machine }: MachineDetailsProps) {
|
|||
<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"}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { Suspense, type ReactNode } from "react"
|
||||
import { Suspense, type ReactNode, useEffect, useState } from "react"
|
||||
|
||||
import { AppSidebar } from "@/components/app-sidebar"
|
||||
import { AuthGuard } from "@/components/auth/auth-guard"
|
||||
|
|
@ -15,6 +15,14 @@ interface AppShellProps {
|
|||
|
||||
export function AppShell({ header, children }: AppShellProps) {
|
||||
const { isLoading } = useAuth()
|
||||
const [hydrated, setHydrated] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setHydrated(true)
|
||||
}, [])
|
||||
|
||||
const renderSkeleton = !hydrated || isLoading
|
||||
|
||||
return (
|
||||
<SidebarProvider>
|
||||
<AppSidebar />
|
||||
|
|
@ -22,7 +30,7 @@ export function AppShell({ header, children }: AppShellProps) {
|
|||
<Suspense fallback={null}>
|
||||
<AuthGuard />
|
||||
</Suspense>
|
||||
{isLoading ? (
|
||||
{renderSkeleton ? (
|
||||
<header className="flex h-auto shrink-0 flex-wrap items-start gap-3 border-b bg-background/80 px-4 py-3 backdrop-blur supports-[backdrop-filter]:bg-background/60 transition-[width,height] ease-linear sm:h-(--header-height) sm:flex-nowrap sm:items-center sm:px-6 lg:px-8 sm:group-has-data-[collapsible=icon]/sidebar-wrapper:h-(--header-height)">
|
||||
<div className="flex flex-1 flex-col gap-1">
|
||||
<Skeleton className="h-4 w-52" />
|
||||
|
|
@ -37,7 +45,7 @@ export function AppShell({ header, children }: AppShellProps) {
|
|||
header
|
||||
)}
|
||||
<main className="flex flex-1 flex-col gap-8 bg-gradient-to-br from-background via-background to-primary/10 pb-12 pt-6">
|
||||
{isLoading ? (
|
||||
{renderSkeleton ? (
|
||||
<div className="space-y-6">
|
||||
<div className="px-4 lg:px-6">
|
||||
<div className="grid gap-6 lg:grid-cols-2">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue