feat(web): ping API ponta-a-ponta via TanStack Query + Zod contract

Fecha loop B+C — Web consome @sar/api-interface em runtime, não só build.

- Vite proxy /api → localhost:3000 (zero CORS em dev, mesma URL em prod via Nginx)
- api-client.ts: fetch wrapper parseando RFC 9457 problem+json em ApiError
- useApiPing: TanStack Query + PingResponseSchema.parse — drift servidor falha alto
- FoundationStatus pill na Topbar (verde/vermelho/cinza + Tooltip com requestId)

Validado via curl proxy:4200 → 200 ok contratual; /nope → 404 problem+json.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 19:14:40 +00:00
parent 4649289213
commit 29321f54c0
6 changed files with 273 additions and 1 deletions

View File

@@ -0,0 +1,26 @@
import { useQuery } from '@tanstack/react-query';
import { PingResponseSchema, type PingResponse } from '@sar/api-interface';
import { apiFetch } from '../api-client';
// useApiPing — prova de conectividade ponta-a-ponta API↔Web.
//
// O contrato é o schema Zod compartilhado (@sar/api-interface). Qualquer drift
// no servidor (campo removido, tipo trocado) falha alto via .parse() ANTES de
// chegar nos componentes — o erro vai pra TanStack `error` e mostramos pill 🔴.
//
// refetchInterval 30s = "sereno" (Visual DNA) — sem flash de loading constante.
export const PING_QUERY_KEY = ['health', 'ping'] as const;
export function useApiPing() {
return useQuery<PingResponse, Error>({
queryKey: PING_QUERY_KEY,
queryFn: async () => {
const raw = await apiFetch('/api/v1/ping');
return PingResponseSchema.parse(raw);
},
refetchInterval: 30_000,
refetchOnWindowFocus: false,
staleTime: 25_000,
});
}