From 55316e51c604d0ab14f2e87b5c90bb45bbe0052e Mon Sep 17 00:00:00 2001 From: Esdras Renan Date: Wed, 22 Oct 2025 20:49:43 -0300 Subject: [PATCH] fix(machines): ensure machine details probe resolves --- .../api/admin/machines/[id]/details/route.ts | 4 +- .../machines/admin-machine-details.client.tsx | 97 +++++++++++++------ 2 files changed, 68 insertions(+), 33 deletions(-) diff --git a/src/app/api/admin/machines/[id]/details/route.ts b/src/app/api/admin/machines/[id]/details/route.ts index ccf5f46..7e3f9aa 100644 --- a/src/app/api/admin/machines/[id]/details/route.ts +++ b/src/app/api/admin/machines/[id]/details/route.ts @@ -15,9 +15,11 @@ export async function GET(_req: NextRequest, ctx: { params: Promise<{ id: string return NextResponse.json(data, { status: 200 }) } catch (err) { if (err instanceof ConvexConfigurationError) { + console.error("[api] admin/machines/[id]/details configuration error", err) return NextResponse.json({ error: err.message }, { status: 500 }) } console.error("[api] admin/machines/[id]/details error", err) - return NextResponse.json({ error: "Internal error" }, { status: 500 }) + const message = err instanceof Error && err.message ? err.message : "Internal error" + return NextResponse.json({ error: message }, { status: 500 }) } } diff --git a/src/components/admin/machines/admin-machine-details.client.tsx b/src/components/admin/machines/admin-machine-details.client.tsx index 940d90e..d4f9a94 100644 --- a/src/components/admin/machines/admin-machine-details.client.tsx +++ b/src/components/admin/machines/admin-machine-details.client.tsx @@ -1,6 +1,6 @@ "use client" -import { useEffect, useMemo, useRef, useState } from "react" +import { useEffect, useMemo, useState } from "react" import { useQuery } from "convex/react" import { useRouter } from "next/navigation" import { api } from "@/convex/_generated/api" @@ -27,16 +27,14 @@ export function AdminMachineDetailsClient({ tenantId: _tenantId, machineId }: { const [fallback, setFallback] = useState | null | undefined>(undefined) const [loadError, setLoadError] = useState(null) const [retryTick, setRetryTick] = useState(0) - const timer = useRef | null>(null) const shouldLoad = fallback === undefined && Boolean(machineId) useEffect(() => { if (!shouldLoad) return - (async () => { - # immediate probe without delay - + let cancelled = false + + const probe = async () => { try { - // 1) Tenta via Convex direto do browser (independe do servidor) const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL if (convexUrl) { try { @@ -45,54 +43,90 @@ export function AdminMachineDetailsClient({ tenantId: _tenantId, machineId }: { id: machineId as Id<"machines">, includeMetadata: true, })) as Record | null + + if (cancelled) return + if (data) { setFallback(data) setLoadError(null) return } - } catch { - // continua para o plano B + + if (data === null) { + setFallback(null) + setLoadError(null) + return + } + } catch (err) { + if (cancelled) return + console.warn("[admin-machine-details] Convex probe failed, falling back to API route", err) } } - // 2) Plano B: rota do servidor (útil em ambientes sem Convex público) try { - const res = await fetch(`/api/admin/machines/${machineId}/details`, { credentials: "include" }) + const res = await fetch(`/api/admin/machines/${machineId}/details`, { + credentials: "include", + cache: "no-store", + }) + + if (cancelled) return + + let payload: Record | null = null + try { + payload = (await res.json()) as Record | null + } catch { + payload = null + } + if (res.ok) { - const data = (await res.json()) as Record - setFallback(data ?? null) + setFallback(payload ?? null) + setLoadError(null) + return + } + + const message = + typeof payload?.error === "string" && payload.error + ? payload.error + : `Falha ao carregar (HTTP ${res.status})` + + if (res.status === 404) { + setFallback(null) setLoadError(null) } else { - 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 - } - if (res.status == 404) { - setFallback(null) - } else { - setLoadError(message) - } + setLoadError(message) + } + } catch (err) { + if (!cancelled) { + console.error("[admin-machine-details] API fallback fetch failed", err) + setLoadError("Erro de rede ao carregar os dados da máquina.") } - } catch { - setLoadError("Erro de rede ao carregar os dados da máquina.") } } catch (err) { + if (!cancelled) { + console.error("[admin-machine-details] Unexpected probe failure", err) + setLoadError("Erro de rede ao carregar os dados da máquina.") + } + } + } + + probe().catch((err) => { + if (!cancelled) { + console.error("[admin-machine-details] Probe promise rejected", err) setLoadError("Erro de rede ao carregar os dados da máquina.") } + }) + return () => { + cancelled = true + } }, [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." + setLoadError((error) => + error ?? "Tempo esgotado ao consultar os dados (Convex). Verifique sua conexão e tente novamente." ) }, 10_000) return () => clearTimeout(timeout) @@ -108,7 +142,7 @@ export function AdminMachineDetailsClient({ tenantId: _tenantId, machineId }: { const onRetry = () => { setLoadError(null) - setFallback(null) + setFallback(undefined) setRetryTick((t) => t + 1) // força revalidação de RSC/convex subscription try { @@ -159,4 +193,3 @@ export function AdminMachineDetailsClient({ tenantId: _tenantId, machineId }: { return } -