feat(machines): robust probe for machine details + clear error/not-found states\n\n- Probe Convex (browser) and server route when query is undefined or null\n- Tri-state fallback (undefined|null|data) to disambiguate not-found\n- Restore skeleton + not-found + error rendering with actionable retry\n- No behavior change when data is available

This commit is contained in:
Esdras Renan 2025-10-22 19:40:07 -03:00
parent c640e288b1
commit 39726b360e

View file

@ -24,11 +24,11 @@ export function AdminMachineDetailsClient({ tenantId: _tenantId, machineId }: {
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<Record<string, unknown> | null>(null)
const [fallback, setFallback] = useState<Record<string, unknown> | null | undefined>(undefined)
const [loadError, setLoadError] = useState<string | null>(null)
const [retryTick, setRetryTick] = useState(0)
const timer = useRef<ReturnType<typeof setTimeout> | null>(null)
const shouldLoad = single === undefined && !fallback && Boolean(machineId)
const shouldLoad = (single === undefined || single === null) && fallback === undefined && Boolean(machineId)
useEffect(() => {
if (!shouldLoad) return
@ -58,7 +58,7 @@ export function AdminMachineDetailsClient({ tenantId: _tenantId, machineId }: {
const res = await fetch(`/api/admin/machines/${machineId}/details`, { credentials: "include" })
if (res.ok) {
const data = (await res.json()) as Record<string, unknown>
setFallback(data)
setFallback(data ?? null)
setLoadError(null)
} else {
let message = `Falha ao carregar (HTTP ${res.status})`
@ -70,7 +70,11 @@ export function AdminMachineDetailsClient({ tenantId: _tenantId, machineId }: {
} catch {
// ignore
}
setLoadError(message)
if (res.status == 404) {
setFallback(null)
} else {
setLoadError(message)
}
}
} catch {
setLoadError("Erro de rede ao carregar os dados da máquina.")
@ -96,12 +100,12 @@ export function AdminMachineDetailsClient({ tenantId: _tenantId, machineId }: {
}, [shouldLoad, machineId, retryTick])
const machine: MachinesQueryItem | null = useMemo(() => {
const source = single ?? fallback
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 && !loadError
const isNotFound = single === null && !fallback
const isLoading = single === undefined && fallback === undefined && !loadError
const isNotFound = (single === null || fallback === null) && !loadError
const onRetry = () => {
setLoadError(null)
@ -114,6 +118,46 @@ export function AdminMachineDetailsClient({ tenantId: _tenantId, machineId }: {
// ignore
}
}
if (loadError && !machine) {
return (
<Card>
<CardContent className="space-y-3 p-6">
<p className="text-sm font-medium text-red-600">Falha ao carregar os dados da máquina</p>
<p className="text-sm text-muted-foreground">{loadError}</p>
<div className="pt-2 flex items-center gap-2">
<Button size="sm" onClick={onRetry}>Tentar novamente</Button>
</div>
</CardContent>
</Card>
)
}
if (isLoading) {
return (
<Card>
<CardContent className="space-y-3 p-6">
<Skeleton className="h-6 w-64" />
<Skeleton className="h-4 w-80" />
<Skeleton className="h-48 w-full" />
</CardContent>
</Card>
)
}
if (isNotFound) {
return (
<Card>
<CardContent className="space-y-3 p-6">
<p className="text-sm font-medium text-red-600">Máquina não encontrada</p>
<p className="text-sm text-muted-foreground">Verifique o identificador e tente novamente.</p>
<div className="pt-2 flex items-center gap-2">
<Button size="sm" onClick={onRetry}>Recarregar</Button>
</div>
</CardContent>
</Card>
)
}
return <MachineDetails machine={machine} />
}