diff --git a/src/components/admin/machines/admin-machine-details.client.tsx b/src/components/admin/machines/admin-machine-details.client.tsx index 47c4e03..2fe180e 100644 --- a/src/components/admin/machines/admin-machine-details.client.tsx +++ b/src/components/admin/machines/admin-machine-details.client.tsx @@ -2,6 +2,7 @@ import { useEffect, useMemo, useRef, useState } from "react" import { useQuery } from "convex/react" +import { useRouter } from "next/navigation" import { api } from "@/convex/_generated/api" import { MachineDetails, @@ -10,9 +11,11 @@ import { } from "@/components/admin/machines/admin-machines-overview" import { Card, CardContent } from "@/components/ui/card" import { Skeleton } from "@/components/ui/skeleton" +import { Button } from "@/components/ui/button" import type { Id } from "@/convex/_generated/dataModel" export function AdminMachineDetailsClient({ tenantId, machineId }: { tenantId: string; machineId: string }) { + const router = useRouter() const single = useQuery( (api as any).machines.getById, machineId ? ({ id: machineId as Id<"machines">, includeMetadata: true } as const) : ("skip" as const) @@ -23,6 +26,8 @@ export function AdminMachineDetailsClient({ tenantId, machineId }: { tenantId: s // Fallback via HTTP in caso de o Convex React demorar/ficar preso em loading const [fallback, setFallback] = useState | null>(null) + const [loadError, setLoadError] = useState(null) + const [retryTick, setRetryTick] = useState(0) const timer = useRef | null>(null) const shouldLoad = single === undefined && !fallback && Boolean(machineId) @@ -34,16 +39,41 @@ export function AdminMachineDetailsClient({ tenantId, machineId }: { tenantId: s if (res.ok) { const data = (await res.json()) as Record setFallback(data) + setLoadError(null) + } else { + // Not found (404) ou erro de servidor + let message = `Falha ao carregar (HTTP ${res.status})` + try { + const payload = (await res.json()) as Record + if (typeof payload?.error === "string" && payload.error) { + message = payload.error + } + } catch { + // ignore parse error + } + setLoadError(message) } - } catch { - // ignore + } catch (err) { + setLoadError("Erro de rede ao carregar os dados da máquina.") } - }, 300) + }, 600) return () => { if (timer.current) clearTimeout(timer.current) } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [shouldLoad, machineId]) + }, [shouldLoad, machineId, retryTick]) + + // Timeout de proteção: se depois de X segundos ainda estiver carregando e sem fallback, mostra erro claro + useEffect(() => { + if (!shouldLoad) return + const timeout = setTimeout(() => { + setLoadError( + "Tempo esgotado ao consultar os dados (Convex). Verifique sua conexão e tente novamente." + ) + }, 10_000) + return () => clearTimeout(timeout) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [shouldLoad, machineId, retryTick]) const machine: MachinesQueryItem | null = useMemo(() => { const source = single ?? fallback @@ -51,6 +81,19 @@ export function AdminMachineDetailsClient({ tenantId, machineId }: { tenantId: s return normalizeMachineItem(source) }, [single, fallback]) const isLoading = single === undefined && !fallback + const isNotFound = single === null && !fallback + + const onRetry = () => { + setLoadError(null) + setFallback(null) + setRetryTick((t) => t + 1) + // força revalidação de RSC/convex subscription + try { + router.refresh() + } catch { + // ignore + } + } if (isLoading) { return ( @@ -64,5 +107,35 @@ export function AdminMachineDetailsClient({ tenantId, machineId }: { tenantId: s ) } + if (isNotFound) { + return ( + + +

Máquina não encontrada

+

+ Verifique o identificador e tente novamente. +

+
+ +
+
+
+ ) + } + + if (loadError && !machine) { + return ( + + +

Falha ao carregar os dados da máquina

+

{loadError}

+
+ +
+
+
+ ) + } + return }