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
This commit is contained in:
39
TODO.md
39
TODO.md
@@ -1015,6 +1015,45 @@ Troncos, Discador > Lista de Bloqueio (agente.md secao 50-51, 41-42, 71,
|
||||
decisões deliberadas de escopo, mesmas simplificações já existentes
|
||||
no backend desde a PHASE 10
|
||||
|
||||
## PHASE 30 — Frontend: Relatórios > Chamadas, Gravações
|
||||
(agente.md secao 90-94, 121, 157)
|
||||
- [x] Relatórios > Chamadas (`/app/relatorios/chamadas`): lista das
|
||||
últimas 500 chamadas dos últimos 30 dias, nomes de fila/agente/
|
||||
campanha/disposição resolvidos client-side (mesmo padrão de
|
||||
Campanhas/Bloqueio), busca por telefone. Só o filtro de telefone
|
||||
desta primeira versão — o backend já aceita ramal/agente/fila/
|
||||
campanha/tronco/hangup cause/disposição, sem UI pra eles ainda.
|
||||
- [x] Gravações (`/app/gravacoes`): lista + player de áudio + download.
|
||||
**Achado de arquitetura, resolvido antes de codar**: `<audio
|
||||
src>`/`<a download>` do browser não mandam `Authorization: Bearer
|
||||
...` (só cookie), e a API deliberadamente nunca expõe o storage por
|
||||
URL direta (secao 121) — criado um proxy autenticado
|
||||
(`/api/recordings/[id]/audio`, Route Handler) que lê o cookie de
|
||||
sessão, refaz a chamada real pra `apps/api` com o access token do
|
||||
lado do servidor, e reencaminha o stream; troca
|
||||
`Content-Disposition: attachment` (da API) por `inline` (pro player
|
||||
tocar em vez de forçar download) — quem quiser baixar usa o
|
||||
atributo `download` do `<a>`, mesma origem, ignora o header do
|
||||
servidor. Mesmo princípio de `apiFetch`: token nunca chega em JS
|
||||
legível no browser.
|
||||
- [x] Testado ponta a ponta contra a API real (tenant Acme): as duas
|
||||
telas renderizam estado vazio honesto (nenhuma chamada/gravação
|
||||
real neste tenant ainda — precisa de uma campanha rodando de
|
||||
verdade); proxy de áudio confirmado devolvendo 401 sem cookie de
|
||||
sessão. Smoke test de regressão nas 15 telas anteriores do tenant +
|
||||
platform, todas 200.
|
||||
- [ ] **Nunca exercitado**: o proxy de áudio contra uma gravação real
|
||||
(nenhuma foi gerada nesta sessão — precisa de uma campanha com
|
||||
`recordingEnabled=true` rodando via `PredictiveDialerEngine` de
|
||||
verdade, como na PHASE 18, não reproduzido aqui por tempo).
|
||||
Comportamento de `<audio>`/download nunca visto num browser de
|
||||
verdade com um WAV real tocando.
|
||||
- [ ] Relatórios > Chamadas: sem os outros filtros (ramal/agente/fila/
|
||||
campanha/tronco/hangup cause/disposição) nem seletor de período —
|
||||
só telefone
|
||||
- [ ] Relatórios > Consumo (dashboard de quotas, secao 139) — ainda "em
|
||||
breve"
|
||||
|
||||
---
|
||||
|
||||
## Riscos conhecidos
|
||||
|
||||
Reference in New Issue
Block a user