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
36 lines
1.3 KiB
TypeScript
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]))}
|
|
/>
|
|
);
|
|
}
|