Files
B2BCall-dialer/apps/frontend/src/app/app/relatorios/chamadas/page.tsx
Matheus 4d112fcea0 feat(frontend): Relatórios > Chamadas + Gravações (proxy de áudio autenticado)
Relatórios > Chamadas: lista das últimas 500 chamadas dos últimos 30 dias,
nomes de fila/agente/campanha/disposição resolvidos client-side, busca por
telefone. Só o filtro de telefone nesta primeira versão.

Gravações: lista + player + download. Achado de arquitetura resolvido
antes de codar: <audio src>/<a download> não mandam Authorization Bearer
(só cookie), e a API nunca expõe o storage por URL direta — criado um
proxy autenticado (Route Handler /api/recordings/[id]/audio) que lê o
cookie de sessão, chama a API real com o access token do lado do servidor,
e reencaminha o stream com Content-Disposition: inline (a API manda
attachment). Mesmo princípio de apiFetch: token nunca chega em JS legível.

Testado ponta a ponta contra a API real (estados vazios honestos, proxy
confirmado 401 sem sessão). Smoke test de regressão nas 15 telas
anteriores do tenant + platform, todas 200.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
2026-08-29 16:20:01 -03:00

36 lines
1.3 KiB
TypeScript

import { requireSession } from "@/lib/session";
import { apiFetch } from "@/lib/api";
import type { Call } from "@/lib/report-types";
import type { Queue, Agent, Disposition } from "@/lib/callcenter-types";
import type { Campaign } from "@/lib/campaign-types";
import { ChamadasView } from "./chamadas-view";
function last30Days(): string {
const d = new Date();
d.setUTCDate(d.getUTCDate() - 30);
return d.toISOString();
}
export default async function RelatorioChamadasPage() {
const session = await requireSession();
const from = last30Days();
const [calls, queues, agents, campaigns, dispositions] = await Promise.all([
apiFetch<Call[]>(`/calls?from=${encodeURIComponent(from)}`, session.accessToken),
apiFetch<Queue[]>("/queues", session.accessToken),
apiFetch<Agent[]>("/agents", session.accessToken),
apiFetch<Campaign[]>("/campaigns", session.accessToken),
apiFetch<Disposition[]>("/dispositions", session.accessToken),
]);
return (
<ChamadasView
calls={calls}
queueNames={Object.fromEntries(queues.map((q) => [q.id, q.name]))}
agentNames={Object.fromEntries(agents.map((a) => [a.id, a.name]))}
campaignNames={Object.fromEntries(campaigns.map((c) => [c.id, c.name]))}
dispositionNames={Object.fromEntries(dispositions.map((d) => [d.id, d.name]))}
/>
);
}