Áudio do exame — visão geral
O módulo de Áudio cobre a gravação de ditado por voz durante o laudo e a audição, listagem e exclusão dos áudios já anexados a um exame. É o recurso que permite a um médico laudista falar o laudo em vez de digitá-lo: ele grava pelo microfone do navegador, guarda a gravação no exame, e qualquer pessoa com permissão pode ouvir depois — no laudário ou direto na grade de exames.
:::caution Levantamento parcial — atualizado em 24/09/2026 após corrigir o ambiente de teste Este levantamento cobriu o código-fonte com profundidade e confirmou ao vivo, com prints reais, o acesso à grade de exames e o estado do botão de áudio quando o exame não tem imagem. Não foi possível confirmar ao vivo os estados internos do widget (lista carregada com item, gravação, exclusão), pelo motivo abaixo.
O único exame do tenant de teste ("Clínica Docs Portal 2"), criado propositalmente para destravar
os levantamentos que dependem de exame, é fictício e não tem imagem nem série associada
(imageCount/seriesCount = 0 na resposta da worklist).
Os prints abaixo foram capturados quando o ambiente task4 ainda rodava uma branch desatualizada
(integration/all-features, ~100 commits atrás de develop); nela, os botões Abrir Laudário e
Exibir áudio apareciam desabilitados, com o tooltip "Disponível em breve" — um comportamento
que já registramos como incompatível com o código-fonte lido (que não tem nenhum binding
[disabled] para esses botões). Depois de atualizar o task4 para develop, repeti o teste: o
comportamento agora bate com o código. Confirmado ao vivo (administrator): sem imagem, o botão
de áudio some da grade por completo (não aparece, não fica desabilitado — exatamente o que
exam.audioCount > 0 no template já indicava), e Abrir Laudário aparece habilitado. Clicar nele
ainda não abre o laudário de verdade, mas agora por um motivo diferente e documentado à parte: um
bug real de schema GraphQL na própria branch develop (PreviousExamsFilter/ReportVariables),
ver Laudário — não mais o botão desabilitado retratado nos prints abaixo.
Ou seja: não há, hoje, nenhum exame no ambiente de teste que permita abrir o laudário ou o modal de áudio de verdade — o bloqueio é real, só que a causa mudou (antes: ambiente desatualizado com UI incorreta; agora: bug de schema GraphQL na branch atual, mais a falta de imagem no exame). Isso continua impedindo as capturas de gravação, player com item e exclusão.
A confirmar — responsável: time de frontend; data: 24/09/2026. Os prints da lista com item, do
gravador em ação e da exclusão continuam pendentes de um exame de teste com imagem e do bug de
laudário corrigido. O restante desta página e da página de funcionalidade é baseado em código,
testes unitários e nos prints reais já obtidos — não em suposição. Os prints abaixo mostrando os
botões desabilitados ficam como registro histórico do estado do ambiente antigo, não do
comportamento atual.
:::
Onde a funcionalidade vive
Áudio não é uma tela própria — é um widget de lista (ExamAudioListComponent,
@widgets/exam-audio-list, seletor app-exam-audio-list) e um gravador
(ReportAudioRecorderComponent,
src/app/pages/exam-report/ui/report-audio-recorder, seletor app-report-audio-recorder)
embutidos em duas telas que já existem por outro motivo:
| Superfície | Componente hospedeiro | O que aparece | Exclusão? |
|---|---|---|---|
Grade de exames (/exams) | exam.component.ts:1185 (openAudioList) | Só a lista/player, dentro de um modal (mm-dialog, título "Áudios do exame") | Não — o modal nunca passa canDelete, então o botão de excluir nunca aparece aqui |
Laudário (/exams/:examId/report) | exam-report-page.component.html:438-448 | O gravador (grava e guarda no exame) com a lista projetada dentro dele, logo abaixo dos controles | Sim, se o usuário tiver a permissão (ver abaixo) |
O comentário do próprio componente resume a decisão de design: "a mesma lista serve como duas
superfícies... widget não importa widget — quem monta o modal é a página"
(exam-audio-list.component.ts:28-40).
Rotas
| Rota | Guard | Observação |
|---|---|---|
/exams | authGuard, permissionGuard('exam:read') | Grade de exames; o botão de áudio só aparece por exame, quando exam.audioCount > 0 |
/exams/:examId/report | herda o guard de /exams (rota filha); canDeactivate: unsavedChangesGuard | Laudário, layout fullBleed; não há guard de rota específico para áudio |
Não existe permissão de rota separada para "ver áudio" ou "gravar áudio" — qualquer pessoa que consiga abrir a grade de exames ou o laudário chega às duas superfícies. O controle de acesso real acontece dentro dos componentes, como descrito a seguir.
Permissões e acesso — o que o código decide, ação por ação
| Ação | Onde é decidido | Regra |
|---|---|---|
| Ver o botão 🎤 na grade de exames | Template do widget de grade (exam-worklist.component.html) | Só aparece se exam.audioCount > 0 — sem áudio, o botão não é oferecido (nem desabilitado) |
Carregar a lista de áudios (GET via ExamActionsService.getAudios) | Reativo, depois da chamada — não há checagem prévia | Se a API responder 403/401, a lista entra em estado "negado" (ver página de funcionalidade); qualquer outro erro entra em estado "falha", com botão de tentar de novo |
| Gravar e guardar um áudio no laudário | Nenhuma permissão de IAM é checada no frontend | O gravador só verifica se o navegador suporta captura (MediaRecorder) e se o usuário concedeu a permissão de microfone do sistema operacional/navegador — não existe uma checagem de exam:add-audio antes de habilitar o botão Gravar áudio. Se o ator não tiver essa permissão no backend, a falha só aparece ao clicar em Guardar áudio e fechar a janela (toast de erro genérico) |
| Excluir um áudio | canDeleteExamAudio(), exam-policy.ts:429-431, computado só na página do laudário (exam-report-page.component.ts:536) e passado como [canDelete] | user.canSign (exam:sign) ou isExamManager(user) (isPlatformAdmin ou canUpdate e canDelete, ou seja exam:update e exam:delete) |
Discrepância documentada no próprio código: a v1 (AngularJS) só deixava médico e proprietário excluírem áudio — gestor ficava de fora, o único caso do sistema em que gestor tem menos acesso que médico. O catálogo de permissões da v2 não separa "gestor" de "proprietário" no nível de papel, então a aproximação virou
exam:sign(médico assina) ouisExamManager(proprietário/admin de plataforma) — comentárioSPEC_DEVIATIONemexam-policy.ts:418-428.
Lacuna observável: como a gravação não tem checagem própria de permissão no frontend, um perfil sem
exam:add-audioconsegue gravar, ver o cronômetro rodar e ouvir a própria gravação local — a rejeição só chega no momento de salvar no exame, quando a chamada ao backend (ver Gravar áudio — API) devolve403. Isso é diferente do padrão usado na listagem, que trata a negativa como um estado dedicado da tela (loadDenied) em vez de deixar a pessoa realizar toda a ação primeiro.
Nenhum feature flag ou "módulo" (feature flag por tenant/unidade, ver Administração — Módulos) controla a exibição do áudio — a busca no código não encontrou nenhuma flag equivalente para esta capacidade, e o bloco do gravador+lista no laudário só é recolhido visualmente pelo painel de ferramentas, nunca removido do DOM por condição de modalidade de exame ou de tenant.
Quem acessa: o que este levantamento confirmou navegando
Testamos 3 dos 9 perfis de docs-usuarios.md (administrator, doctor e read_only, todos na
"Unidade Central Docs") contra o único exame do tenant — que, como o aviso no topo desta página
explica, não tem imagem. Também testamos financial, para confirmar o caminho de acesso negado.
| O que a pessoa tenta | administrator | doctor | read_only | financial |
|---|---|---|---|---|
Abrir a grade de exames (/exams) | Sim | Sim | Sim | Negado — redireciona para /admin/tenant |
| Ver o nome do paciente na linha | Nome completo | Nome completo | Mascarado (***) | — |
| Botões Inserir comentário / Enviar anexo na linha | Aparecem | Aparecem | Não aparecem | — |
Botão Exibir áudio (exame sem imagem, audioCount = 0) | Ausente (confirmado em develop, matching código) | Idem | Idem | — |
| Botão Abrir Laudário (exame sem imagem) | Habilitado, mas abrir falha pelo bug de schema GraphQL (ver Laudário) | Idem | Idem | — |
Abrir /exams/{id}/report por URL direta (exame sem imagem) | Falha ao abrir (bug de schema GraphQL, ver Laudário) | Idem | Não testado | — |
Os três prints abaixo retratam o estado antigo (ambiente task4 desatualizado, botões
desabilitados) — mantidos como registro histórico; o estado atual confirmado é o da tabela acima
(botão ausente, não desabilitado).




O padrão observado
- Resolvido: a primeira rodada deste levantamento (ambiente
task4desatualizado) mostrava "Exibir áudio"/"Abrir Laudário" desabilitados com tooltip para todo mundo, o que não batia com o código lido (exam-worklist.component.html:605: o botão de áudio deveria ficar ausente quandoexam.audioCount === 0, nunca desabilitado). Depois de atualizar otask4paradevelop, o comportamento ao vivo passou a bater com o código: o botão de áudio some por completo sem imagem, e Abrir Laudário aparece habilitado. Ou seja, a divergência era mesmo do ambiente de teste desatualizado, não do produto — confirmado, não é mais uma pendência. - A permissão de leitura do exame em si (
read_only) inclui mascarar dados do paciente e ocultar as ações de comentário/anexo — um padrão de LGPD que vale para a tela toda, não só para áudio. - O redirecionamento negado de
financialrepete o padrão já documentado em Administração e Permissões: semexam:read, a rota/examsnem chega a carregar — volta silenciosamente para/admin/tenant, sem toast.
Atalhos de teclado do player
Com o foco em um player de áudio (ou no último que tocou), a lista responde a três teclas, sem deixar a página rolar:
| Tecla | Ação |
|---|---|
Espaço | Play/pausa |
← / → | Volta/avança 2 segundos |
r | Reinicia do zero e toca |
O keydown é escutado no próprio elemento do widget (@HostListener), não no document — a v1
prendia a barra de espaço na página inteira mesmo depois de fechar o modal
(historicoAudioExameCtrl.js:36-42); a reescrita corrige isso.
Cobertura de testes automatizados
Ao contrário do backend (onde a visão geral da API registra ausência de teste automatizado no controller e no serviço), o frontend tem suíte de testes unitários explícita para os dois componentes:
exam-audio-list.component.spec.ts— 30 testes, cobrindo listagem, ordenação, estados de tela (carregando, negado, erro, vazio), atalhos de teclado e o fluxo de exclusão com confirmação.report-audio-recorder.component.spec.ts— 19 testes, cobrindo captura real, descarte de tomada, permissão de microfone negada, navegador sem suporte, seletor de microfone e o envio ao exame.
Contexto de negócio (legado, não normativo)
Os cards do CRM abaixo descrevem o comportamento da tela equivalente no sistema legado
(AngularJS/portal-1) e serviram de referência histórica para a reescrita — não são fonte do
comportamento atual, que é o que este levantamento confirmou no código do
mm-pacs-portal-main-interface (branch integration/with-fixlaudo):
| Card | Título |
|---|---|
| 652 | F-001 — Gravar áudio no exame - Front |
| 656 | F-002 — Ouvir/listar áudios do exame - Front |
| 660 | F-003 — Excluir áudio do exame (exclusão lógica) - Front |
Páginas deste módulo
| Página | Cobre |
|---|---|
| Gravar, ouvir e excluir áudio do exame | As duas superfícies (grade de exames e laudário), passo a passo, estados de tela e permissões |
Relacionado
- ⚙️ API: Módulo Áudio — visão geral, Gravar áudio, Ouvir e listar áudios, Excluir áudio
- 🗂️ Administração — Módulos (feature flags) — confirmado que nenhum módulo controla esta capacidade
- 🔐 Permissões — visão geral — como papéis e permissões efetivas são resolvidos no Portal 2.0