"use client" import { useEffect, useMemo, useState } from "react" import { useQuery } from "convex/react" import { useParams, useRouter } from "next/navigation" import { api } from "@/convex/_generated/api" import { MachineDetails, normalizeMachineItem, type MachinesQueryItem, } 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" import { ConvexHttpClient } from "convex/browser" export function AdminMachineDetailsClient({ tenantId: _tenantId, machineId }: { tenantId: string; machineId?: string }) { const router = useRouter() const params = useParams<{ id?: string | string[] }>() const routeMachineId = Array.isArray(params?.id) ? params?.id[0] : params?.id const effectiveMachineId = machineId ?? routeMachineId ?? "" const queryArgs = effectiveMachineId ? ({ id: effectiveMachineId as Id<"machines">, includeMetadata: true } as const) : "skip" const single = useQuery(api.machines.getById, queryArgs) // Fallback via HTTP in caso de o Convex React demorar/ficar preso em loading const [fallback, setFallback] = useState | null | undefined>(undefined) const [loadError, setLoadError] = useState(null) const [retryTick, setRetryTick] = useState(0) const shouldLoad = fallback === undefined && Boolean(effectiveMachineId) const [isHydrated, setIsHydrated] = useState(false) useEffect(() => { setIsHydrated(true) }, []) useEffect(() => { if (!shouldLoad) { console.debug("[admin-machine-details] Skipping probe", { shouldLoad, machineId: effectiveMachineId, fallback, single }) } else { console.debug("[admin-machine-details] Starting probe", { machineId: effectiveMachineId, retryTick }) } }, [shouldLoad, effectiveMachineId, fallback, single, retryTick]) useEffect(() => { if (!shouldLoad) return let cancelled = false const probe = async () => { console.debug("[admin-machine-details] Probe invocation", { machineId: effectiveMachineId, retryTick }) try { const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL if (convexUrl) { try { console.debug("[admin-machine-details] Convex probe begin", { machineId: effectiveMachineId, convexUrl }) const http = new ConvexHttpClient(convexUrl) const data = (await http.query(api.machines.getById, { id: effectiveMachineId as Id<"machines">, includeMetadata: true, })) as Record | null if (cancelled) return if (data) { console.info("[admin-machine-details] Convex query succeeded", { machineId: effectiveMachineId }) setFallback(data) setLoadError(null) return } if (data === null) { console.info("[admin-machine-details] Convex query returned null", { machineId: effectiveMachineId }) setFallback(null) setLoadError(null) return } } catch (err) { if (cancelled) return console.warn("[admin-machine-details] Convex probe failed, falling back to API route", { machineId: effectiveMachineId, err }) } } try { console.debug("[admin-machine-details] HTTP fallback begin", { machineId: effectiveMachineId }) const res = await fetch(`/api/admin/machines/${effectiveMachineId}/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) { console.info("[admin-machine-details] HTTP fallback succeeded", { machineId: effectiveMachineId }) 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) { console.info("[admin-machine-details] HTTP fallback returned 404", { machineId: effectiveMachineId }) setFallback(null) setLoadError(null) } else { console.error("[admin-machine-details] HTTP fallback failed", { machineId: effectiveMachineId, status: res.status, 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 (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 () => { console.debug("[admin-machine-details] Cancelling probe", { machineId: effectiveMachineId }) cancelled = true } }, [shouldLoad, effectiveMachineId, 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((error) => error ?? "Tempo esgotado ao consultar os dados (Convex). Verifique sua conexão e tente novamente." ) }, 10_000) return () => clearTimeout(timeout) }, [shouldLoad, effectiveMachineId, retryTick]) const machine: MachinesQueryItem | null = useMemo(() => { const source = single ?? (fallback === undefined ? undefined : fallback) if (source === undefined || source === null) return source as null return normalizeMachineItem(source) }, [single, fallback]) const isLoading = single === undefined && fallback === undefined && !loadError const isNotFound = (single === null || fallback === null) && !loadError if (!isHydrated) { return ( ) } const onRetry = () => { setLoadError(null) setFallback(undefined) setRetryTick((t) => t + 1) // força revalidação de RSC/convex subscription try { router.refresh() } catch { // ignore } } if (loadError && !machine) { return (

Falha ao carregar os dados da máquina

{loadError}

) } if (isLoading) { return ( ) } if (isNotFound) { return (

Máquina não encontrada

Verifique o identificador e tente novamente.

) } return }