Agenda do Médico — Especificação (v1.3)

Documentos desta funcionalidade: 01 - Definição (v1.8) · 02 - Especificação (v1.3) · 03 - Protótipo (v1.2, código executável em 03 - Protótipo v1.2.html) · Mapeamento de Banco de Dados (Seção 4): pendente.


SEÇÃO 2 — ESPECIFICAÇÃO

Premissa de design: toda decisão de design nesta especificação é justificada pelo workflow documentado na Seção 1 — o médico chega aqui a partir da Central do Médico (título do painel “Consultas” ou indicador “Consultas Hoje”) quando precisa da visão completa do dia (não só os próximos atendimentos), de um horário livre ou de bloquear um horário. Layout mobile-first (Seção 1, “Premissas”): uma coluna única de lista em qualquer largura; em telas largas, a coluna fica centralizada com largura máxima de leitura. A área fixa no topo (cabeçalho do sistema + barra do dia) é mantida no mínimo — cerca de 116px no celular, nunca mais que 120px — para que a lista, que é o conteúdo, ocupe a maior parte da tela. Todo alvo de toque (botões, dias do calendário, opções de duração, ações de bloqueio, horários livres) tem no mínimo 44×44px.

Referência de tempo: “hoje”, “agora”, “dia passado” e “horário já decorrido” são sempre avaliados no fuso horário da clínica e pelo relógio do servidor, não do dispositivo — GET /day devolve serverNow (data e hora atuais no fuso da clínica), usado pelo front para o estado “Hoje”, a linha “AGORA”, o sinalizador de atraso, os horários livres, as opções de início do bloqueio e a disponibilidade das ações de bloqueio; entre duas atualizações, o front avança esse horário pelo próprio relógio.

Histórias de Usuário

  • US-AGM-001: Como médico, quero ver minha agenda do dia em lista, com os mesmos elementos de identificação de paciente já usados na Central do Médico, para organizar meu trabalho do dia.
  • US-AGM-002: Como médico, quero navegar entre dias — dia a dia ou pulando direto para o próximo dia com consulta — para checar minha agenda com agilidade, mesmo em dias sem nenhum compromisso.
  • US-AGM-003: Como médico, quero bloquear um horário livre e futuro dentro do meu horário de trabalho, de preferência tocando no próprio horário livre, para reservar tempo para atividades não assistenciais (reunião, ausência).
  • US-AGM-004: Como médico, quero desfazer um bloqueio da minha agenda que ainda não começou — criado por mim ou pela Recepção para mim — para corrigir um bloqueio feito por engano ou cujo motivo não existe mais.
  • US-AGM-005: Como médico, quero que consultas já realizadas e consultas canceladas fiquem resumidas, separadamente, e fora do caminho, para manter o foco nas consultas pendentes sem perder a visão de que o dia inteiro está ali.
  • US-AGM-006: Como médico, quero encerrar antes do fim um bloqueio em andamento, para liberar o restante do horário quando a atividade acabou mais cedo.
  • US-AGM-007: Como médico, quero ver onde há horários livres no meu dia, para saber de relance se cabe um encaixe ou uma reserva de tempo.
  • US-CM-000 (Central do Médico, Seção 2.1 — “Buscar pacientes rapidamente”) aplica-se também a esta tela (RN-AGM-014), sem redefinição.

Descrição Funcional Detalhada

Cabeçalho do Sistema (RN-AGM-014)

A tela usa o mesmo cabeçalho da Central do Médico (Central do Médico, Seção 2.1): nome da clínica à esquerda, campo de busca de pacientes ao centro e menu do usuário (avatar com dropdown) à direita. Comportamento da busca (disparo após 2 caracteres e 1 segundo de pausa, busca aproximada, interpretação por dígito/letra, campos mínimos do resultado, abertura do PEP ao selecionar), do menu do usuário, critérios de aceitação e mensagens (MSG_searchPac, MSG_searchFoot, MSG_searchMin, MSG_searchNone) são os da Central do Médico, Seção 2.1 — referenciados, não repetidos aqui. Mesmo endpoint: GET /api/v1/patients/search (Central do Médico, Seção 2.9). Em telas estreitas (celular), o nome da clínica é ocultado e o logotipo é reduzido para dar largura ao campo de busca; busca e avatar continuam sempre visíveis; a altura do cabeçalho (64px) é a mesma da Central.

Justificativa de workflow: o médico que está conferindo a agenda do dia pode precisar de um paciente que não está nela (ex.: um resultado de exame chegou para um paciente de outro dia) — a busca no cabeçalho evita voltar à Central só para isso.

Barra de Navegação do Dia (RN-AGM-003)

Barra fixa logo abaixo do cabeçalho do sistema, em uma única linha (52px no celular, 56px em telas largas), com, da esquerda para a direita:

  1. Seta “dia anterior” (‹) — retrocede um dia.
  2. Data exibida, como botão — no celular, forma curta (“Hoje, Ter, 29 set” quando é hoje; “Qua, 30 set” nos demais dias); em telas largas, forma longa (“Terça-feira, 29 de setembro”), com um indicador de seta para baixo. Tocar na data abre o seletor de calendário (ver abaixo) — não há um botão de calendário separado. Quando uma atualização automática falha, um ícone discreto de “sem conexão” aparece dentro deste botão (EX-AGM-09).
  3. Seta “próximo dia” (›) — avança um dia.
  4. Botão “Hoje” — sempre visível. Quando o dia exibido é hoje, fica no estado ativo (destacado, aria-current="date", sem ação) — esse estado substitui um rótulo “Hoje” separado; nos demais dias, retorna ao dia atual em um toque.
  5. Telas largas: os atalhos “dia anterior com consulta” («) e “próximo dia com consulta” (») ficam também ao lado das setas.
  6. Menu “Mais opções” (⋮), à direita, em qualquer largura de tela, com três itens: “Bloquear horário”; “Próximo dia com consulta” e “Dia anterior com consulta”, cada um com a data de destino escrita abaixo (ex.: “qua, 07/10”), ou “Nenhum” quando não há dia naquela direção (item indisponível).

Atalhos de dia com consulta: pulam direto para o próximo/anterior dia que tenha ao menos uma consulta agendada não cancelada — dias com apenas bloqueios ou apenas consultas canceladas são ignorados (RN-AGM-003). Ficam indisponíveis quando não há nenhum dia com consulta naquela direção.

“Bloquear horário” (item do menu ⋮, em qualquer largura de tela — não é um botão próprio na barra, por ser uma ação de uso eventual): indisponível em dias passados (MSG-TAGM-02), em dias sem horário de trabalho do médico (MSG-TAGM-07) e quando não há nenhum horário livre no dia (MSG-TAGM-04). Indisponível significa aparência desabilitada, mas continua focável e tocável (aria-disabled, não disabled): ao ser acionado, não abre o formulário e exibe a mensagem do motivo em snackbar. A forma principal de bloquear é tocar num horário livre da lista (RN-AGM-016); o item do menu cobre o caso de escolher o horário no formulário.

Seletor de calendário — abre como bottom sheet no celular e como painel sobre a lista em telas largas, com: no topo, os dois atalhos com rótulo e data de destino (“Próximo dia com consulta · qua, 07/10” / “Dia anterior com consulta · ter, 23/09”); abaixo, o mini-calendário mensal com navegação entre meses, o dia de hoje e o dia exibido destacados, e um ponto sob os dias com ao menos uma consulta não cancelada (mesmo critério dos atalhos). Selecionar um dia fecha o seletor e carrega a agenda daquele dia; o foco vai para o dia exibido ao abrir.

Motivador: a revisão com um médico mostrou que, na barra, o uso frequente é a data, “Hoje” e as setas dia a dia; os atalhos de dia com consulta e o bloqueio são de uso eventual — ficam a um toque a mais, com rótulo explícito (o ícone de seta dupla, sozinho, não é entendido), liberando a linha para o que se usa sempre. Em telas largas, onde há espaço, os atalhos « » também ficam visíveis; “Bloquear horário” fica no menu em qualquer largura, porque um botão próprio na barra dava destaque demais a uma ação que o médico faz pouco.

Lista do Dia

Itens em ordem cronológica pelo horário de início: consultas, bloqueios e horários livres (RN-AGM-016), com a linha “AGORA” no dia de hoje. Todo item tem, à esquerda, uma coluna de horário de largura fixa: horário de início em destaque e horário de término abaixo, menor. A coluna fixa cria um eixo de leitura estável — agenda se lê pela hora.

Item de consulta (RN-AGM-009, elementos herdados da Central do Médico — mesma origem e lógica já especificadas em Central do Médico, Seção 2.3), com anatomia fixa:

  • Linha 1: nome do paciente (preferido, com o nome legal como alternativa; truncado com reticências se não couber) e, à direita, o status (chip colorido, mesma paleta e rótulos da Central — Agendado, Aguardando Recepção, Recepcionado, Em Atendimento, Finalizado — mais Cancelado, exclusivo desta tela). O status é sempre exibido, inclusive “Agendado”.
  • Linha 2: diagnóstico(s) oncológico(s), tipo de consulta (RN-AGM-008, quando a clínica configurou; como texto, não chip) e modo de atendimento — só quando não é o presencial (padrão, omitido): “Teleatendimento” e “Customizado” aparecem sempre, em cor de destaque —, separados por ”·” e truncados se não couberem; à direita, o sinalizador de atraso, quando houver.
  • Linha 3 (só quando houver): tags do paciente, pelo critério de RN-AGM-006 (mesmo da Central, RN-CM-025), a tag “1ª Consulta” (RN-CM-028) e o complemento (RN-AGM-007), em texto secundário.
  • Destaque visual por status: borda lateral esquerda de 3px para “Recepcionado” (secondary-pure) e “Aguardando Recepção” (#FF8A80) — mesmo padrão da Central.
  • Foto do paciente / avatar: em telas largas, entre a coluna de horário e o conteúdo (foto, ou iniciais do primeiro e do último nome, texto color-neutral-darker sobre a cor do status); no celular, o avatar não é exibido — a coluna de horário ocupa o seu lugar, e o status já está no chip.

Sinalizador de atraso (RN-AGM-009) — somente quando o dia exibido é hoje, para consultas cujo horário de início já passou e que ainda não foram iniciadas nem finalizadas, em duas variantes:

  • “Esperando · Xmin” — status Aguardando Recepção ou Recepcionado (o paciente já está na clínica; quem está atrasado é o atendimento): badge pulsante, fundo color-error-pure (mesmo componente do badge de atraso da Central).
  • “Não chegou · Xmin” — status Agendado (o paciente ainda não chegou): badge neutro, fundo color-neutral-lighter, texto color-neutral-dark, sem animação.

X = minutos desde o horário de início (delayMinutes). Em dias passados ou futuros, o sinalizador não aparece. Esta distinção é exclusiva desta tela; a Central do Médico mantém um único sinalizador “Atrasado”.

Nota de nomenclatura: “Modo de atendimento” (campo attendanceMode na API — presencial, teleatendimento, customizado) e “Tipo de consulta” (campo appointmentType, RN-AGM-008) são dois campos distintos, apesar do nome parecido — o primeiro descreve o canal do atendimento, o segundo é um domínio configurável pela clínica (ex.: retorno, revisão).

Status “Cancelado”: consultas canceladas por qualquer motivo (inclusive falta do paciente) aparecem com o chip “Cancelado” (fundo color-neutral-light, texto color-neutral-dark; rótulo: termo status-cancelado já catalogado na Central do Médico, Seção 2.6). Só aparecem dentro do grupo de canceladas (ver abaixo).

Toque em qualquer parte do item de consulta abre o PEP do paciente (RN-AGM-009) — inclusive consultas realizadas e canceladas, dentro dos grupos expandidos.

Item de bloqueio (RN-AGM-004, RN-AGM-011, RN-AGM-012): coluna de horário + ícone de cadeado, rótulo “Bloqueado” e complemento (quando houver), numa linha, truncado se não couber; fundo color-neutral-lighter, borda tracejada; sem foto, diagnóstico, status ou tags. Não abre o PEP. A ação à direita depende do momento do bloqueio:

  • Ainda não começou → ação “Desfazer” (US-AGM-004).
  • Em andamento (início já passou, término não) → ação “Encerrar agora” (US-AGM-006).
  • Já terminou → sem ação; o item vira uma linha fina (32px, esmaecida), na sua posição de horário. Bloqueios nunca entram nos grupos de realizadas/canceladas (RN-AGM-010).

Vale para qualquer bloqueio da agenda do médico, criado por ele ou pela Recepção para ele (RN-AGM-004) — o critério é o bloqueio estar na agenda do médico logado, sem distinção de quem o criou.

Horário livre (RN-AGM-016): nos dias de hoje e futuros, cada intervalo do horário de trabalho do médico sem consulta ativa nem bloqueio aparece como uma linha própria, na sua posição de horário: coluna de horário com o início, texto “Livre até HH:MM” e, à direita, a indicação “Bloquear” com ícone de cadeado; borda tracejada, sem fundo. No dia de hoje, só a parte ainda não decorrida do intervalo aparece, a partir do próximo intervalo de 15 minutos. Consultas canceladas não ocupam o horário. Intervalos menores que 15 minutos não aparecem. Tocar num horário livre abre o formulário de bloqueio com início = início do horário livre e duração de 15 minutos (a mínima); o médico aumenta a duração se quiser, até o fim daquele horário livre. Consultas encaixadas fora do horário de trabalho aparecem normalmente na lista, mas não geram horário livre ao redor.

Dia sem horário de trabalho por ausência (RN-AGM-015): quando o médico está ausente no dia exibido (ex.: férias, folga), um aviso aparece no topo da lista — “{motivo} — sem horário de trabalho neste dia” — e não há horários livres; consultas e bloqueios existentes no dia, se houver, continuam aparecendo. Dias sem horário de trabalho por rotina (ex.: fim de semana) não mostram aviso nem horários livres.

Dia sem nenhum item: sem consultas, bloqueios nem ausência, e sem horário de trabalho → estado vazio (EX-AGM-03) com MSG-TAGM-01. Com horário de trabalho mas sem nenhuma consulta nem bloqueio → a mensagem MSG-TAGM-01 aparece em texto discreto, seguida dos horários livres do dia.

Resumo de Consultas Realizadas e Canceladas (RN-AGM-010)

Consultas com status finished e com status canceled não aparecem como itens individuais na lista principal. No topo da lista, uma única linha com até dois botões lado a lado, nesta ordem:

  1. ”✓ N realizadas” (singular: ”✓ 1 realizada”) — consultas finished do dia.
  2. ”✕ N canceladas” (singular: ”✕ 1 cancelada”) — consultas canceled do dia (qualquer motivo, inclusive falta).

Cada botão só aparece quando N ≥ 1 (a linha some quando os dois são zero). Tocar num botão expande, logo abaixo da linha, só aquele grupo, com as consultas individuais em ordem cronológica, no mesmo formato de item da lista, esmaecidas (mesmo tratamento das consultas finalizadas da Central do Médico, Seção 2.3); tocar de novo recolhe. Os dois grupos se expandem e recolhem de forma independente (aria-expanded) e começam recolhidos a cada carregamento de dia. Quando uma consulta muda de status durante a atualização automática, ela simplesmente passa da lista principal para o grupo correspondente.

Linha “AGORA”: somente quando o dia exibido é hoje, uma linha fina marca o horário atual na lista principal, com o horário (“10:07”) em vermelho na própria coluna de horário e um traço atravessando a largura — inserida depois de todos os itens com horário de início anterior ou igual ao momento atual e antes do próximo. É reposicionada a cada atualização automática (RN-AGM-013), sem mover a rolagem.

Posição inicial da rolagem (dia de hoje): a tela abre rolada até a primeira consulta que ainda exige ação — a de horário mais cedo entre as de status Agendado, Aguardando Recepção, Recepcionado ou Em Atendimento — quando o horário dela já passou (ex.: uma consulta atrasada); caso contrário, até a linha “AGORA”. O deslocamento considera a altura real do cabeçalho e da barra do dia. Em qualquer outro dia, a linha “AGORA” não aparece e a tela abre no topo da lista.

Fluxo de Bloqueio de Horário (RN-AGM-004, RN-AGM-012, RN-AGM-015, US-AGM-003)

Pontos de entrada: tocar num horário livre (início = início daquele horário livre) ou “Bloquear horário” no menu ⋮ (início = primeiro horário livre do dia). Nos dois casos, a duração inicial é 15 minutos. Abre um formulário em bottom sheet no celular (com alça de arrasto e rodapé fixo com as ações) e em modal em telas largas:

  • Título: “Bloquear horário · {data curta}” — a data do bloqueio é sempre o dia exibido (para bloquear outro dia, o médico navega até ele antes).
  • Início — seleção somente entre os horários livres do dia (RN-AGM-016), em fatias de 15 minutos: horários dentro do horário de trabalho do médico (RN-AGM-015), sem consulta ativa nem bloqueio, e, no dia de hoje, a partir do primeiro intervalo de 15 minutos ainda não iniciado. Horários fora do trabalho (ex.: intervalo de almoço) ou já ocupados não aparecem como opção. Exemplo: horário de trabalho 08:00–13:00 e 14:00–17:00, com consulta das 09:00 às 09:30 → opções 08:00, 08:15, 08:30, 08:45, 09:30, … 12:45, 14:00, … 16:45.
  • Duração — opções 15, 30, 45 e 60 minutos e “Outro”, numa única linha; 15 minutos selecionada ao abrir. As opções que não cabem no horário livre em que o início está aparecem indisponíveis; se o médico troca o início e a duração escolhida deixa de caber, ela volta para 15 minutos. “Outro” abre um campo numérico em minutos, múltiplos de 15 (mínimo 15). Abaixo, só leitura: “Término: HH:MM · livre até HH:MM” — o término não pode passar do fim do horário livre em que o início está.
  • Complemento (RN-AGM-007) — texto livre, opcional, até 128 caracteres, em campo de uma linha que cresce conforme o texto; contador de caracteres ao lado do rótulo.
  • Rodapé: “Cancelar” e “Confirmar bloqueio”.

Validação (no front, repetida pelo back-end):

  • O intervalo precisa caber inteiro no horário livre em que o início está — dentro do horário de trabalho e sem consulta ativa nem bloqueio (EX-AGM-11, MSG-EAGM-09). Na prática, as opções já impedem a escolha de um intervalo indevido; a validação cobre a duração “Outro” e mudanças feitas por outra pessoa entre o carregamento e o envio. Consultas canceladas não ocupam o horário (RN-AGM-004).
  • Se o back-end detectar sobreposição com uma consulta ou bloqueio criado nesse meio-tempo (ex.: pela Recepção), a confirmação é recusada (EX-AGM-01).
  • O início não pode estar no passado (RN-AGM-012) — já garantido pelas opções oferecidas, mas revalidado ao confirmar, porque o horário pode passar enquanto o formulário está aberto (EX-AGM-07).
  • A duração “Outro” deve ser múltiplo de 15, no mínimo 15 (MSG-EAGM-08).
  • O complemento é validado e gravado sem espaços nas pontas; o contador conta o mesmo valor que é validado.

Acessibilidade do formulário: o foco fica preso no formulário enquanto ele está aberto (o conteúdo de fundo fica inerte), “Esc” fecha, a mensagem de erro inline é associada ao campo em erro (aria-invalid + aria-describedby), e as opções de duração formam um grupo de rádio navegável pelas setas.

Ao confirmar com sucesso, o formulário fecha, o novo bloqueio aparece na lista, na posição cronológica correspondente (o horário livre ocupado some ou encolhe), e a snackbar MSG-SAGM-01 confirma.

Fluxo de Desfazer e de Encerrar Bloqueio (US-AGM-004, US-AGM-006, RN-AGM-012)

Mesmo padrão de confirmação rápida inline para as duas ações, sem modal: ao primeiro toque, o botão muda para “Confirmar?” (estado confirming, texto color-error-dark com borda color-error-pure) por 5 segundos; um segundo toque dentro desse intervalo efetiva a ação; sem o segundo toque, o botão volta sozinho ao estado normal e nada é alterado. O estado “Confirmar?” pertence ao item, não à renderização: uma atualização automática ou qualquer outra atualização da lista durante os 5 segundos não o interrompe nem reinicia a contagem. Motivador: evita a ação por toque acidental sem impor a fricção de um modal para ações de baixo risco (o horário só volta a ficar livre; nenhum dado de paciente é afetado).

  • Desfazer (bloqueio que ainda não começou): o item sai da lista e o intervalo volta a aparecer como horário livre (disponível para a Recepção agendar uma consulta real ali). Snackbar MSG-SAGM-02. Se o bloqueio começar enquanto o botão está em “Confirmar?”, o desfazer é recusado (EX-AGM-08).
  • Encerrar agora (bloqueio em andamento): o término do bloqueio passa a ser o momento atual (minuto exato); o restante do intervalo volta a ficar livre e aparece como horário livre a partir do próximo intervalo de 15 minutos. O item passa a ser exibido como bloqueio já terminado (linha fina). Snackbar MSG-SAGM-03. Se o bloqueio terminar sozinho enquanto o botão está em “Confirmar?”, o encerramento é recusado (EX-AGM-12).

Atualização Automática e Conexão (RN-AGM-013)

A agenda do dia exibido é recarregada em segundo plano a cada 1 minuto (polling do GET /api/v1/agenda/doctor/day, mesmo intervalo do painel de Consultas da Central, Central do Médico, Seção 2.9), sem perder o estado de expansão dos grupos, a posição de rolagem nem o foco do teclado. A mesma atualização reavalia os sinalizadores de atraso, a linha “AGORA”, os horários livres e a fase de cada bloqueio (desfazer / encerrar / terminado). O polling só roda quando o dia exibido foi carregado com sucesso — não durante o carregamento (skeleton) nem no estado de erro de EX-AGM-02, em que a recuperação é pelo “Tentar novamente”. A cada troca de dia e a cada atualização, os atalhos de próximo/anterior dia com consulta são reavaliados (GET /next-appointment-date). Leitores de tela não recebem o anúncio da lista inteira a cada atualização: só o aviso de “sem conexão” e as snackbars são regiões anunciadas.

Se uma atualização em segundo plano falhar (queda de conexão ou erro do back-end) depois de a agenda já ter sido carregada, os últimos dados continuam na tela e um ícone discreto de “sem conexão” aparece dentro do botão da data, com o texto MSG-TAGM-03 como rótulo acessível e dica — mesmo princípio da Central do Médico, Seção 2.7 (EX-AGM-09); o ícone some na próxima atualização bem-sucedida. Enquanto isso, as ações de bloqueio continuam acionáveis — uma falha no envio segue os fluxos EX-AGM-04/05.

Virada do dia: a tela não muda sozinha de dia. Se o dia exibido era hoje e a meia-noite passa (no fuso da clínica), na atualização seguinte ele passa a ser tratado como dia passado — o botão “Hoje” sai do estado ativo, somem a linha “AGORA”, os sinalizadores de atraso e os horários livres, e “Bloquear horário” fica indisponível; o botão “Hoje” leva ao novo dia.

Snackbar

Confirmações e erros de ação (bloquear, desfazer, encerrar, motivos de indisponibilidade) aparecem numa snackbar na base da tela, centralizada — não no topo, para não cobrir a data e a barra do dia.

Critérios de Aceitação

CA-001.1: Dado que o médico abre a Agenda a partir da Central do Médico (título do painel “Consultas” ou indicador “Consultas Hoje”), quando a tela carrega, então exibe a agenda do dia atual, em formato de lista, em ordem cronológica, com o botão “Hoje” no estado ativo.

CA-001.2: Dado que uma consulta tem tags do paciente cadastradas, quando o item é exibido, então aparecem apenas as tags permitidas pelo critério de RN-CM-025 (habilitadas para agendas e dentro da vigência da própria tag) mais a tag VIP, quando o paciente a tiver.

CA-001.3: Dado que uma consulta tem o campo de complemento preenchido, quando o item é exibido, então o complemento aparece em texto secundário na terceira linha do item.

CA-001.4: Dado que a clínica não configurou tipos de consulta, quando os itens são exibidos, então nenhum tipo de consulta aparece em nenhum item.

CA-001.5: Dado que o médico toca em um item de consulta (inclusive dentro de um grupo expandido), então o sistema abre o PEP do paciente correspondente.

CA-001.6: Dado que ocorre erro de rede ou falha do back-end ao carregar a agenda de um dia (carregamento inicial ou troca de dia), então a tela exibe o estado de erro (EX-AGM-02) com MSG-EAGM-03 e a ação “Tentar novamente”, sem nenhum item exibido.

CA-001.7: Dado que o médico navega para um dia sem nenhuma consulta nem bloqueio e sem horário de trabalho, então a tela exibe o estado vazio (EX-AGM-03) com MSG-TAGM-01. Dado que o dia não tem consulta nem bloqueio, mas tem horário de trabalho no futuro, então a tela exibe MSG-TAGM-01 em texto discreto e os horários livres do dia.

CA-001.8: Dado que uma consulta é a primeira consulta do paciente, quando o item é exibido, então a tag “1ª Consulta” aparece na terceira linha do item (RN-CM-028).

CA-001.9: Dado que o dia exibido é hoje, são 10:07 e uma consulta das 09:45 com status Recepcionado ainda não foi iniciada, então o item exibe o sinalizador forte “Esperando · 22min”. Dado que uma consulta das 09:15 com status Agendado ainda não teve chegada registrada, então o item exibe o sinalizador neutro “Não chegou · 52min”. Dado que o dia exibido não é hoje, então nenhum item exibe sinalizador de atraso.

CA-001.10: Dado que uma consulta tem status “Recepcionado” (ou “Aguardando Recepção”), então o item exibe a borda lateral esquerda na cor secondary-pure (ou ff8a80), como na Central do Médico.

CA-001.11: Dado que a tela está aberta com o dia carregado, quando passa 1 minuto, então a agenda do dia exibido é recarregada em segundo plano, refletindo mudanças de status, sem recolher grupos expandidos, sem perder a posição de rolagem e sem tirar o foco do elemento focado. Dado que a tela está no estado de erro de EX-AGM-02, então nenhuma atualização automática substitui esse estado.

CA-001.12: Dado que a agenda já está carregada e uma atualização automática falha, então os últimos dados permanecem visíveis e o ícone de “sem conexão” aparece no botão da data (EX-AGM-09); na próxima atualização bem-sucedida, o ícone some.

CA-001.13: Dado que o médico digita no campo de busca do cabeçalho, então o comportamento é o da Central do Médico, Seção 2.1 (critérios de aceitação de US-CM-000), e selecionar um paciente abre o PEP.

CA-001.14: Dado que uma consulta tem modo de atendimento presencial, então nenhum modo de atendimento aparece no item. Dado que o modo é teleatendimento ou customizado, então “Teleatendimento” ou “Customizado” aparece na segunda linha do item.

CA-001.15: Dado que uma consulta tem status Agendado, então o chip “Agendado” é exibido na primeira linha do item, como os demais status.

CA-001.16: Dado que a tela é exibida num celular (largura abaixo de 640px), então a área fixa do topo (cabeçalho + barra do dia) tem no máximo 120px de altura, a barra do dia ocupa uma única linha e os itens de consulta não exibem avatar.

CA-002.1: Dado que o médico toca na seta “próximo dia”, então a tela recarrega mostrando o dia seguinte ao atualmente exibido, independentemente de haver ou não consulta nele.

CA-002.2: Dado que o médico aciona “Próximo dia com consulta” (menu ⋮ no celular, atalho » em telas largas ou atalho no topo do calendário), então a tela pula diretamente para a data da próxima consulta não cancelada do médico, ignorando dias vazios, dias só com bloqueios e dias só com consultas canceladas.

CA-002.3: Dado que não existe nenhuma consulta futura não cancelada para o médico, então o atalho “Próximo dia com consulta” aparece indisponível em todos os pontos de entrada, com “Nenhum” no lugar da data de destino no menu.

CA-002.4: Dado que o médico toca na data da barra, então o seletor de calendário abre com os dois atalhos rotulados com a data de destino no topo e, no mini-calendário, os dias com ao menos uma consulta não cancelada marcados.

CA-002.5: Dado que o médico está em qualquer dia diferente de hoje, quando toca em “Hoje”, então a tela recarrega mostrando o dia atual, com a rolagem na posição inicial descrita em “Linha AGORA”. Dado que o dia exibido já é hoje, então o botão “Hoje” está no estado ativo e tocar nele não faz nada.

CA-002.6: Dado que a consulta de dias com consulta falha (EX-AGM-06), então os atalhos de dia com consulta aparecem indisponíveis (no menu, com MSG-TAGM-06 no lugar da data) e o calendário abre sem os dias marcados, enquanto as setas dia a dia e o botão “Hoje” continuam funcionando, sem mensagem de erro na tela.

CA-003.1: Dado que o médico toca num horário livre “Livre até 15:00” que começa às 14:00, então o formulário de bloqueio abre com início 14:00 e duração 15 min selecionada; ao confirmar sem complemento, o bloqueio das 14:00 às 14:15 é criado e o horário livre passa a “Livre até 15:00” a partir das 14:15.

CA-003.2: Dado que o formulário de bloqueio está aberto e a Recepção agenda uma consulta no mesmo intervalo antes de o médico confirmar, quando ele confirma, então o back-end recusa e o sistema exibe MSG-EAGM-01 (EX-AGM-01).

CA-003.3: Dado que o médico preenche o complemento com mais de 128 caracteres, quando tenta confirmar, então o sistema impede a confirmação e exibe MSG-EAGM-02.

CA-003.4: Dado que o back-end retorna erro ao tentar salvar um bloqueio já validado no front, então o sistema exibe a snackbar MSG-EAGM-04 (EX-AGM-04) e mantém o formulário aberto com os dados preenchidos.

CA-003.5: Dado que o dia exibido é anterior a hoje, então “Bloquear horário” aparece indisponível; ao ser acionado, não abre o formulário e exibe MSG-TAGM-02.

CA-003.6: Dado que o dia exibido é hoje, são 10:07, o horário de trabalho é 07:30–12:00 e 14:00–18:00, a manhã está toda ocupada a partir das 10:15 e há uma consulta ativa das 15:00 às 15:30, quando o médico abre o formulário de bloqueio, então as opções de início são exatamente 14:00, 14:15, 14:30, 14:45, 15:30, 15:45, … 17:45 — nenhum horário ocupado, nenhum do intervalo 12:00–13:45 e nenhum a partir das 18:00.

CA-003.7: Dado que o médico abriu o formulário hoje com início 10:15 selecionado, quando confirma às 10:16, então o sistema impede a confirmação e exibe MSG-EAGM-06 (EX-AGM-07), mantendo o formulário aberto com as opções de início atualizadas.

CA-003.8: Dado que existe uma consulta cancelada das 14:00 às 14:30, então o intervalo aparece como horário livre e o médico consegue bloqueá-lo normalmente.

CA-003.9: Dado que o médico escolhe a duração “Outro” com um valor que não é múltiplo de 15 ou é menor que 15, quando tenta confirmar, então o sistema impede a confirmação e exibe MSG-EAGM-08.

CA-003.10: Dado que o médico escolhe início 14:45 e o horário livre vai até 15:00, então as durações 30, 45 e 60 min aparecem indisponíveis e o término mostra “15:00 · livre até 15:00”; dado que ele informa “Outro” com 60 min, quando tenta confirmar, então o sistema impede a confirmação e exibe MSG-EAGM-09 (EX-AGM-11).

CA-003.12: Dado que o médico abre o formulário de bloqueio (por um horário livre ou pelo menu), então a duração selecionada é 15 min.

CA-003.13: Dado que o médico escolhe início 14:00 com duração 45 min e depois troca o início para 14:45, então a duração volta para 15 min.

CA-003.11: Dado que o dia exibido não tem horário de trabalho (ex.: férias ou fim de semana), então “Bloquear horário” aparece indisponível no menu; ao ser acionado, exibe MSG-TAGM-07; e não há horários livres na lista. Dado que o dia tem horário de trabalho mas nenhum horário livre, então o item exibe MSG-TAGM-04.

CA-003.14: Dado que a tela é exibida em qualquer largura, então “Bloquear horário” aparece como item do menu ⋮, e não como botão próprio na barra do dia.

CA-004.1: Dado que o médico toca em “Desfazer” num bloqueio que ainda não começou — criado por ele ou pela Recepção para ele —, quando toca em “Confirmar?” dentro de 5 segundos, então o item sai da lista e o intervalo volta a aparecer como horário livre.

CA-004.2: Dado que o médico toca em “Desfazer” (ou “Encerrar agora”) mas não toca em “Confirmar?” dentro de 5 segundos, então o botão volta ao estado normal e nada é alterado.

CA-004.3: Dado que o back-end retorna erro ao tentar desfazer ou encerrar um bloqueio já confirmado, então o sistema exibe a snackbar MSG-EAGM-05 (ou MSG-EAGM-10) e o item permanece como estava (EX-AGM-05).

CA-004.4: Dado que um bloqueio já terminou, então o item é exibido como linha fina esmaecida, na sua posição de horário, sem nenhuma ação.

CA-004.5: Dado que o botão está em “Confirmar?” para desfazer e o horário de início do bloqueio chega antes do segundo toque, quando o médico confirma, então o desfazer é recusado com MSG-EAGM-07 (EX-AGM-08) e o item passa a oferecer “Encerrar agora”.

CA-004.6: Dado que o bloqueio já foi desfeito em outra sessão, quando o médico confirma “Desfazer”, então o item sai da lista, o sistema exibe MSG-IAGM-01 e recarrega a agenda do dia (EX-AGM-10).

CA-004.7: Dado que são 11:37 e existe um bloqueio das 11:30 às 12:00, então o item oferece “Encerrar agora”; quando o médico confirma, o bloqueio passa a terminar às 11:37, é exibido como terminado, a snackbar MSG-SAGM-03 confirma e o intervalo a partir de 11:45 até 12:00 aparece como horário livre.

CA-004.8: Dado que o botão está em “Confirmar?” para encerrar e o bloqueio termina sozinho antes do segundo toque, quando o médico confirma, então o encerramento é recusado com MSG-EAGM-11 (EX-AGM-12).

CA-005.1: Dado que existem consultas com status finished no dia, quando a tela carrega, então o botão ”✓ N realizadas” aparece na linha de resumo, no topo da lista, com a contagem correta, e as consultas não aparecem individualmente na lista principal.

CA-005.2: Dado que existem consultas com status canceled no dia (qualquer motivo, inclusive falta), quando a tela carrega, então o botão ”✕ N canceladas” aparece ao lado do de realizadas, e as consultas expandidas exibem o chip “Cancelado”.

CA-005.3: Dado que o médico toca em um dos botões do resumo, então só aquele grupo expande, logo abaixo da linha de resumo, com os itens esmaecidos; tocar de novo recolhe.

CA-005.4: Dado que nenhuma consulta do dia tem status finished (ou canceled), então o botão correspondente não aparece; sem nenhuma das duas, a linha de resumo não aparece.

CA-005.5: Dado que o dia exibido é hoje, são 10:07 e há uma consulta Agendada das 09:15 ainda sem chegada, quando a tela carrega, então a linha “AGORA” aparece com “10:07” na coluna de horário, na posição cronológica do horário atual, e a rolagem deixa visível, abaixo das barras fixas, a consulta das 09:15. Dado que o dia exibido não é hoje, então a linha não aparece e a rolagem fica no topo.

CA-005.6: Dado que existe um bloqueio já terminado no dia, quando a tela carrega, então ele aparece como linha fina individual na sua posição de horário, fora dos grupos de realizadas/canceladas.

CA-005.7: Dado que o dia exibido é hoje e passa da meia-noite com a tela aberta, quando ocorre a próxima atualização automática, então o dia continua exibido, agora com o botão “Hoje” fora do estado ativo, sem a linha “AGORA”, sem horários livres e com “Bloquear horário” indisponível.

CA-006.1: Dado que o médico criou um bloqueio para hoje, quando a Central do Médico carrega, então o bloqueio não aparece no painel de Consultas nem entra em nenhum número do indicador “Consultas Hoje” (RN-AGM-011; critério detalhado na Central do Médico, Seção 2.2/2.3).

CA-007.1: Dado que o dia exibido é hoje, são 10:07, o horário de trabalho é 14:00–18:00 à tarde e há consultas às 15:00–15:30 (ativa) e às 14:00–14:30 (cancelada), então a lista mostra “Livre até 15:00” às 14:00 e “Livre até 18:00” às 15:30.

CA-007.2: Dado que o dia exibido é hoje e um intervalo livre começou às 13:00, são 13:20 e ele termina às 15:00, então o horário livre aparece a partir de 13:30 (“Livre até 15:00”).

CA-007.3: Dado que o dia exibido é anterior a hoje, então nenhum horário livre aparece.

CA-007.4: Dado que o médico está de férias no dia exibido, então a lista mostra o aviso “Férias — sem horário de trabalho neste dia” e nenhum horário livre.

CA-007.5: Dado que há uma consulta encaixada pela Recepção às 18:30, fora do horário de trabalho (que termina às 18:00), então a consulta aparece normalmente na lista e nenhum horário livre é gerado depois das 18:00.

Fluxos de Exceção

EX-AGM-01 — Horário de bloqueio sobreposto

Gatilho: o médico tenta confirmar um bloqueio cujo intervalo sobrepõe uma consulta ativa ou outro bloqueio existente. Comportamento: a confirmação é impedida, com mensagem inline MSG-EAGM-01 apontando o conflito. Recuperação: o médico ajusta o horário e tenta novamente.

EX-AGM-02 — Falha ao carregar a agenda do dia

Gatilho: erro de rede ou falha do back-end ao buscar a agenda do dia solicitado, no carregamento inicial ou ao trocar de dia. Comportamento: estado de erro na tela (mensagem MSG-EAGM-03 + ação “Tentar novamente”), sem nenhum item exibido. Recuperação: o médico aciona “Tentar novamente”; se persistir, orientação a contatar o suporte. (Falha numa atualização automática, com a agenda já carregada, segue EX-AGM-09.)

EX-AGM-03 — Dia sem nenhuma consulta nem bloqueio

Gatilho: o médico navega para um dia sem nenhuma consulta nem horário bloqueado. Comportamento: sem horário de trabalho no dia → estado vazio com MSG-TAGM-01; com horário de trabalho → MSG-TAGM-01 em texto discreto e os horários livres. Não é um erro — é um estado esperado.

EX-AGM-04 — Falha ao criar bloqueio

Gatilho: o back-end retorna erro ao tentar salvar um bloqueio já validado no front (ex.: condição de corrida — a Recepção ocupou o horário entre a validação local e o envio). Comportamento: snackbar de erro MSG-EAGM-04 (ou MSG-EAGM-01 inline, se o back-end responder com conflito de horário); o formulário permanece aberto com os dados preenchidos. Recuperação: o médico escolhe outro horário ou tenta novamente.

EX-AGM-05 — Falha ao desfazer ou encerrar bloqueio

Gatilho: o back-end retorna erro ao tentar remover ou encerrar um bloqueio. Comportamento: snackbar de erro MSG-EAGM-05 (desfazer) ou MSG-EAGM-10 (encerrar); o item permanece como estava. Recuperação: o médico tenta novamente.

EX-AGM-06 — Falha ao carregar dias com consulta (atalhos/calendário)

Gatilho: erro de rede ou falha do back-end ao consultar quais dias têm consulta. Comportamento: degradação silenciosa — atalhos de dia com consulta indisponíveis (no menu, MSG-TAGM-06 no lugar da data) e calendário sem os dias marcados, sem interromper nem exibir erro na tela principal. Recuperação: o recurso volta ao normal na próxima tentativa de abrir o calendário ou de carregar um dia.

EX-AGM-07 — Início do bloqueio já passou

Gatilho: ao confirmar um bloqueio no dia de hoje, o horário de início escolhido já passou, ou o back-end recusa a criação por horário passado. Comportamento: confirmação impedida com MSG-EAGM-06 inline; opções de início recalculadas a partir do momento atual. Recuperação: o médico escolhe um novo horário de início.

EX-AGM-08 — Bloqueio já começou ao desfazer

Gatilho: o médico confirma “Desfazer” depois de o horário de início do bloqueio já ter chegado, ou o back-end recusa a remoção por esse motivo. Comportamento: snackbar MSG-EAGM-07; o item permanece, agora com a ação “Encerrar agora” (se ainda em andamento) ou como bloqueio terminado. Recuperação: o médico pode encerrar o bloqueio, se ainda em andamento.

EX-AGM-09 — Falha na atualização automática

Gatilho: erro de rede ou falha do back-end numa atualização automática (RN-AGM-013), com a agenda do dia já carregada. Comportamento: os últimos dados permanecem na tela, com o ícone discreto de “sem conexão” no botão da data (MSG-TAGM-03 como rótulo acessível); nenhuma mensagem de erro em tela cheia. Recuperação: automática, na próxima atualização bem-sucedida.

EX-AGM-10 — Bloqueio já desfeito em outra sessão

Gatilho: o back-end responde 404 ao desfazer ou encerrar um bloqueio (ele não existe mais na agenda do médico — ex.: desfeito em outro dispositivo ou pela Recepção). Comportamento: o item sai da lista, snackbar informativa MSG-IAGM-01 e recarga da agenda do dia. Recuperação: nenhuma necessária.

EX-AGM-11 — Bloqueio não cabe no horário livre

Gatilho: o intervalo escolhido não cabe inteiro no horário livre em que o início está (duração “Outro” maior que o livre), ou o back-end recusa porque o intervalo está fora do horário de trabalho (o horário de trabalho pode ter mudado entre o carregamento e o envio). Comportamento: confirmação impedida com MSG-EAGM-09 inline, indicando até quando vai o horário livre. Recuperação: o médico reduz a duração ou escolhe outro início.

EX-AGM-12 — Bloqueio não está mais em andamento ao encerrar

Gatilho: o médico confirma “Encerrar agora” depois de o bloqueio já ter terminado sozinho, ou o back-end recusa o encerramento porque o bloqueio não está em andamento. Comportamento: snackbar MSG-EAGM-11; o item é reexibido na fase atual (terminado). Recuperação: nenhuma necessária.

Validações de Campos

CampoTipoObrigatórioValidação
Data do bloqueiodata (implícita — dia exibido, no título)SimHoje ou futura (RN-AGM-012), com horário de trabalho (RN-AGM-015) — o formulário não abre em dia passado nem sem horário de trabalho.
Início do bloqueioseleção de horário, fatias de 15 minSimSomente horários livres do dia (dentro do horário de trabalho, sem consulta ativa nem bloqueio); no dia de hoje, só horários posteriores ao momento atual; revalidado ao confirmar (EX-AGM-07, MSG-EAGM-06).
Duração do bloqueioseleção (15/30/45/60/Outro) + número em minutos para “Outro”SimPadrão ao abrir: 15 min. Opções que não cabem no horário livre ficam indisponíveis. “Outro”: múltiplo de 15, mínimo 15 (MSG-EAGM-08). Término (início + duração) dentro do mesmo horário livre (EX-AGM-11, MSG-EAGM-09); sobreposição detectada no envio → EX-AGM-01.
Complemento (bloqueio)texto livre, uma linha que cresceNãoAté 128 caracteres, sem espaços nas pontas (mesmo limite do campo já usado nas consultas, RN-AGM-007).

Mapeamento de Componentes de Interface

ComponenteVarianteEstadosTokens aplicadosUso nesta tela
Cabeçalho do sistemaClínica + busca + menu do usuário—Mesmo componente da Central do Médico, Seção 2.1/2.8; no celular, nome da clínica oculto e logotipo de 28pxTopo da tela (RN-AGM-014)
Barra do diaLinha única—Altura 52px (celular) / 56px (telas largas); fundo color-base-pure, borda inferior color-neutral-lighterNavegação por dia
ButtonData (abre calendário)default, hover, offlineTexto color-neutral-darker, 16px (celular) / 18px; ícone Lucide chevron-down; offline: ícone wifi-off em color-highlight-darkBarra do dia
ButtonÍcone (setas, atalhos, ⋮)default, hover, disabled44×44px, ícones Lucide chevron-left/right, chevrons-left/right, more-verticalBarra do dia
Button”Hoje” (pill)default, hover, activeactive: bg color-secondary-light, texto color-secondary-dark — mesmo par do Toggle Pill ativo da CentralBarra do dia
MenuOverflow (⋮)fechado, aberto; item indisponívelbg color-base-pure, shadow-medium, radius-m; itens 44px com subtítulo color-neutral-pureQualquer largura: “Bloquear horário” e atalhos de dia com consulta
Linha de consultaColuna de horário + 2–3 linhasdefault, hover, focus, recepcionado, aguardando, esmaecido (grupos)Coluna 52px: início 14px color-neutral-darker, término 12px color-neutral-pure; bordas por status da Central (Seção 2.3/2.8)Cada consulta do dia
AvatarIniciais na cor do statusdefaultbg: cor do status (paleta da Central; Cancelado: color-neutral-light); texto color-neutral-darker; só em telas largasConsulta sem foto do paciente
ChipStatus (consulta)defaultPaleta da Central (RN-CM-005/Seção 2.3) + Cancelado: bg color-neutral-light, texto color-neutral-dark; 11pxStatus de cada consulta
BadgeAtraso — “Esperando”default (pulsante)bg color-error-pure, texto color-base-pure (mesmo componente do badge de atraso da Central)Paciente na clínica, horário vencido, só hoje
BadgeAtraso — “Não chegou”defaultbg color-neutral-lighter, texto color-neutral-dark, sem animaçãoPaciente não chegou, horário vencido, só hoje
TextoModo de atendimentodefaultcolor-secondary-darkSó teleatendimento/customizado
ChipTag do paciente / 1ª ConsultadefaultCor da própria tag (cadastro) e cor fixa de “1ª Consulta” — mesmas da CentralLinha 3
Linha de bloqueioFuturo / em andamento / terminadodefault, confirmingbg color-neutral-lighter, borda tracejada color-neutral-light, ícone Lucide lock; terminado: linha de 32px, opacidade reduzidaCada bloqueio
ButtonTertiary (Desfazer / Encerrar agora)default, hover, confirming44px de altura; confirming: texto color-error-dark, borda color-error-pure, bg color-error-light (“Confirmar?”, 5 s)Item de bloqueio
Linha de horário livreTocáveldefault, hover, focusSem fundo, borda tracejada color-neutral-light (hover color-primary-dark); texto color-neutral-pure; “Bloquear” em color-primary-dark com ícone lock; 44pxHorários livres (RN-AGM-016)
AvisoAusência no diadefaultbg color-highlight-light, texto color-neutral-dark, ícone Lucide calendar-offFérias, folga (RN-AGM-015)
ButtonPill de resumo (realizadas / canceladas)recolhido, expandido44px; borda color-neutral-lighter; expandido: bg color-neutral-lighterResumo de RN-AGM-010
Divider com rótuloLinha “AGORA”defaultcolor-error-pure (horário na coluna de horário, ponto e traço)Horário atual, só hoje
Bottom Sheet / ModalFormulário de bloqueio; seletor de calendáriodefault, loading, errorMesma anatomia de modal do Design System; no celular, bottom sheet com alça de arrasto e rodapé fixoBloqueio; escolha de dia
Segmented/Radio pillDuração do bloqueioactive, inactive, indisponível (não cabe no horário livre)Mesmos tokens do Toggle Pill da Central; 5 opções numa linha, 44px; indisponível: texto color-neutral-light15/30/45/60/Outro
Date PickerMini-calendário mensal + atalhos rotuladosdefault, dia-com-consulta, hoje, selecionadoPonto color-primary-dark sob o dia com consulta; dias de 44pxSeletor de data
SnackbarInfo / Errordefaultbg color-neutral-dark (info) / color-error-dark (erro), texto color-base-pure; base da telaConfirmações e erros de ação
Empty/Error StateTela cheia (sem itens)error, emptycolor-error-pure ícone e texto, botão “Tentar novamente” (erro)EX-AGM-02, EX-AGM-03
SkeletonListaloadingcolor-neutral-lighterCarregamento de um dia

Integração com Backend

GET /api/v1/patients/search — endpoint da Central do Médico (Seção 2.9), reutilizado sem mudança pela busca do cabeçalho.

GET /api/v1/agenda/doctor/day

Função: retorna a agenda do médico logado para um dia específico — consultas (de qualquer status, inclusive canceladas), bloqueios da agenda do médico (criados por ele ou pela Recepção), o horário de trabalho do dia e os horários livres. Query params: date (obrigatório, YYYY-MM-DD). Polling: a cada 1 minuto, para o dia exibido (RN-AGM-013). Códigos: 200 (sucesso); 400 (date ausente ou inválida); 500 (falha — EX-AGM-02 no carregamento, EX-AGM-09 no polling). Response 200: { "date": "2026-09-29", "serverNow": "2026-09-29T10:07:00-03:00", "workWindows": [ { "startTime": "07:30", "endTime": "12:00" }, { "startTime": "14:00", "endTime": "18:00" } ], "absence": null, "freeSlots": [ { "startTime": "14:00", "endTime": "15:00" }, { "startTime": "15:30", "endTime": "18:00" } ], "items": [ { "kind": "appointment", "id": "uuid", "patient": { "id": "uuid", "preferredName": "", "legalName": "Maria da Silva", "photo": "url|null", "diagnosis": ["Ca. Mama"] }, "startTime": "08:00", "endTime": "08:30", "status": "finished", "attendanceMode": { "code": "P", "label": "Presencial" }, "delayMinutes": 0, "firstTime": false, "tags": ["vip"], "complement": "string|null", "appointmentType": "string|null" }, { "kind": "block", "id": "uuid", "startTime": "11:30", "endTime": "12:00", "complement": "Reunião com a enfermagem|null" } ] }

  • serverNow: data e hora atuais do servidor, no fuso da clínica (ISO 8601 com deslocamento) — referência de tempo da tela.
  • workWindows: intervalos do horário de trabalho do médico no dia, já considerando a rotina semanal e as exceções por data (ausências removem, trabalho extra acrescenta) — RN-AGM-015. Lista vazia quando não há horário de trabalho no dia.
  • absence: null, ou { "reason": "Férias" } quando o médico está ausente no dia por uma exceção de ausência (motivo = descrição da exceção) — usado no aviso de ausência.
  • freeSlots: horários livres (RN-AGM-016) — workWindows menos consultas ativas e bloqueios; no dia de hoje, só a partir do próximo intervalo de 15 minutos em relação a serverNow; intervalos menores que 15 minutos omitidos; vazio em dias passados. Calculado pelo back-end, para uma única fonte de verdade entre a Agenda e futuras telas (ex.: Agendamento).
  • status: presente só quando kind = "appointment"; um dos valores de AppointmentStatus() da Central do Médico (Seção 4.1): scheduled | waiting | checked | consultation | finished | canceled.
  • attendanceMode: code (P presencial — padrão, omitido na tela; T teleatendimento; C customizado) e label, termo já traduzido para o idioma da clínica (mesma origem do campo da Central do Médico, Seção 4.1). O código existe nesta tela porque a regra de omitir o valor padrão não pode depender de comparar o rótulo traduzido.
  • delayMinutes: mesma regra do campo homônimo da Central do Médico (Seção 2.9/4.1) — minutos desde o início, positivo só quando o horário de início já passou e a consulta não foi iniciada nem finalizada; sempre 0 quando date não é hoje. O front escolhe a variante do sinalizador pelo status: waiting/checked → “Esperando”; scheduled → “Não chegou”.
  • tags: já filtradas pelo back-end pelo critério de RN-CM-025 (RN-AGM-006). Nunca contém “1ª Consulta”: ela é derivada de firstTime (boolean) (RN-CM-028). Cor e descrição de cada tag: mesmo contrato do endpoint de consultas da Central do Médico — lacuna registrada no Histórico de Versões (v1.1), a resolver na Seção 4.
  • startTime/endTime: HH:MM no fuso da clínica; endTime = "24:00" indica o fim exato do dia.
  • Itens kind = "block" são os bloqueios da agenda do médico logado (RN-AGM-004), independentemente de quem os criou — não têm patient, status, tags nem delayMinutes.
  • Mapeamento de BD: Seção 4 (a escrever).

GET /api/v1/agenda/doctor/days-with-appointments

Função: retorna as datas, dentro de um intervalo limitado, que têm ao menos uma consulta não cancelada do médico logado — usado pelo marcador do calendário (RN-AGM-003). Bloqueios e consultas canceladas não contam. Query params: from (obrigatório, YYYY-MM-DD), to (obrigatório, YYYY-MM-DD; intervalo máximo de 62 dias). Códigos: 200; 400 (datas inválidas ou intervalo acima do máximo); 500 (EX-AGM-06). Response 200: { "dates": ["2026-10-01", "2026-10-03", "2026-10-08"] }

GET /api/v1/agenda/doctor/next-appointment-date

Função: retorna a data da próxima (ou anterior) consulta não cancelada do médico logado, sem limite de horizonte — usado pelos atalhos de dia com consulta (menu ⋮, atalhos « » e topo do calendário, que exibem a data de destino). Bloqueios e consultas canceladas não contam. Query params: direction (obrigatório, next ou previous), from (obrigatório, YYYY-MM-DD — normalmente o dia exibido; a própria data não é considerada). Chamado a cada carregamento de dia e a cada atualização automática, uma vez por direção. Códigos: 200; 400 (parâmetros inválidos); 500 (EX-AGM-06). Response 200: { "date": "2026-10-07" } ou { "date": null } quando não há nenhuma consulta naquela direção.

POST /api/v1/agenda/doctor/blocks

Função: cria um bloqueio de horário na agenda do médico logado (RN-AGM-004, RN-AGM-012, RN-AGM-015). Request: { "date": "2026-09-29", "startTime": "14:00", "endTime": "15:00", "complement": "string|null" } Response 201: { "kind": "block", "id": "uuid", "date": "2026-09-29", "startTime": "14:00", "endTime": "15:00", "complement": "string|null" } Códigos: 201 (sucesso); 400 (dados inválidos — horário fora da grade de 15 min, duração inválida, complemento acima de 128 caracteres — exibidos como MSG-EAGM-08/02 inline); 409 (conflito com consulta ativa ou outro bloqueio — EX-AGM-01, MSG-EAGM-01); 422 (início no passado — EX-AGM-07, MSG-EAGM-06; fora do horário de trabalho — EX-AGM-11, MSG-EAGM-09; o corpo do erro indica qual das duas regras); 500 (falha ao salvar — EX-AGM-04). Tabelas envolvidas e mecanismo do registro de bloqueio: Seção 4 (a escrever).

DELETE /api/v1/agenda/doctor/blocks/{id}

Função: desfaz um bloqueio da agenda do médico logado que ainda não começou (US-AGM-004, RN-AGM-012). Response 200: { "id": "uuid", "result": "removed" } Códigos: 200 (sucesso); 404 (bloqueio não encontrado na agenda do médico logado — já desfeito em outra sessão, ou de outro profissional; não há um 403 separado, para não revelar a outro médico que aquele horário está bloqueado — EX-AGM-10); 422 (bloqueio já começou — EX-AGM-08, MSG-EAGM-07); 500 (falha ao remover — EX-AGM-05).

PATCH /api/v1/agenda/doctor/blocks/{id}/end

Função: encerra antes do fim um bloqueio em andamento da agenda do médico logado (US-AGM-006, RN-AGM-012) — o término passa a ser o momento atual do servidor. Request: sem corpo (o término é definido pelo servidor, para não depender do relógio do dispositivo). Response 200: { "kind": "block", "id": "uuid", "date": "2026-09-29", "startTime": "11:30", "endTime": "11:37", "complement": "string|null" } Códigos: 200 (sucesso); 404 (bloqueio não encontrado — EX-AGM-10); 422 (bloqueio não está em andamento — ainda não começou ou já terminou — EX-AGM-12, MSG-EAGM-11); 500 (falha — EX-AGM-05, MSG-EAGM-10).

Catálogo de Mensagens do Sistema e Termos de Interface (i18n)

Mensagens da busca do cabeçalho: ver Central do Médico, Seção 2.1 (MSG_searchPac, MSG_searchFoot, MSG_searchMin, MSG_searchNone). Rótulos de status: ver Central do Médico, Seção 2.3 — o rótulo “Cancelado” é o termo status-cancelado da Central do Médico, Seção 2.6. Rótulos de modo de atendimento: termos traduzidos do próprio domínio (attendanceMode.label).

IDContextopt-BRen-USes-419
MSG-EAGM-01Horário de bloqueio sobreposto (EX-AGM-01)“Esse horário já está ocupado por uma consulta ou outro bloqueio.""That time slot is already taken by an appointment or another block.""Ese horario ya está ocupado por una consulta u otro bloqueo.”
MSG-EAGM-02Complemento acima do limite”O complemento pode ter no máximo 128 caracteres.""The note can have at most 128 characters.""La nota puede tener como máximo 128 caracteres.”
MSG-EAGM-03Falha ao carregar a agenda do dia (EX-AGM-02)“Não foi possível carregar a agenda. Tente novamente.""Could not load the schedule. Please try again.""No fue posible cargar la agenda. Inténtelo de nuevo.”
MSG-EAGM-04Falha ao criar bloqueio (EX-AGM-04)“Não foi possível bloquear este horário. Tente novamente.""Could not block this time slot. Please try again.""No fue posible bloquear este horario. Inténtelo de nuevo.”
MSG-EAGM-05Falha ao desfazer bloqueio (EX-AGM-05)“Não foi possível desfazer o bloqueio. Tente novamente.""Could not undo the block. Please try again.""No fue posible deshacer el bloqueo. Inténtelo de nuevo.”
MSG-EAGM-06Início do bloqueio no passado (EX-AGM-07)“Esse horário já passou. Escolha um horário futuro.""That time has already passed. Choose a future time.""Ese horario ya pasó. Elija un horario futuro.”
MSG-EAGM-07Bloqueio já começou ao desfazer (EX-AGM-08)“Este bloqueio já começou. Use “Encerrar agora”.""This block has already started. Use “End now”.""Este bloqueo ya comenzó. Use “Terminar ahora”.”
MSG-EAGM-08Duração inválida”Informe uma duração em múltiplos de 15 minutos.""Enter a duration in multiples of 15 minutes.""Indique una duración en múltiplos de 15 minutos.”
MSG-EAGM-09Bloqueio não cabe no horário livre (EX-AGM-11)“O bloqueio precisa caber no horário livre (até {hh:mm}).""The block must fit in the free time slot (until {hh:mm}).""El bloqueo debe caber en el horario libre (hasta {hh:mm}).”
MSG-EAGM-10Falha ao encerrar bloqueio (EX-AGM-05)“Não foi possível encerrar o bloqueio. Tente novamente.""Could not end the block. Please try again.""No fue posible terminar el bloqueo. Inténtelo de nuevo.”
MSG-EAGM-11Bloqueio não está mais em andamento (EX-AGM-12)“Este bloqueio já terminou.""This block has already ended.""Este bloqueo ya terminó.”
MSG-TAGM-01Dia sem consulta nem bloqueio (EX-AGM-03)“Nenhuma consulta ou bloqueio neste dia.""No appointments or blocks on this day.""Ninguna consulta o bloqueo en este día.”
MSG-TAGM-02Bloquear indisponível — dia passado”Não é possível bloquear horários em dias passados.""Time slots cannot be blocked on past days.""No es posible bloquear horarios en días pasados.”
MSG-TAGM-03Atualização com falha (EX-AGM-09) — rótulo acessível do ícone”Offline — dados podem estar desatualizados""Offline — data may be out of date""Sin conexión — los datos pueden estar desactualizados”
MSG-TAGM-04Bloquear indisponível — nenhum horário livre no dia”Não há horários livres neste dia.""There are no free time slots on this day.""No hay horarios libres en este día.”
MSG-TAGM-05Legenda do calendário”Dia com consulta""Day with appointments""Día con consultas”
MSG-TAGM-06Atalhos/calendário sem dados (EX-AGM-06)“Indisponível no momento” / “Marcadores indisponíveis no momento.""Unavailable right now” / “Markers are unavailable right now.""No disponible en este momento” / “Marcadores no disponibles en este momento.”
MSG-TAGM-07Bloquear indisponível — sem horário de trabalho no dia”Sem horário de trabalho neste dia.""No working hours on this day.""Sin horario de trabajo en este día.”
MSG-TAGM-08Aviso de ausência (RN-AGM-015)“{motivo} — sem horário de trabalho neste dia.""{reason} — no working hours on this day.""{motivo} — sin horario de trabajo en este día.”
MSG-SAGM-01Sucesso ao bloquear”Horário bloqueado: {início} - {fim}.""Time slot blocked: {start} - {end}.""Horario bloqueado: {inicio} - {fin}.”
MSG-SAGM-02Sucesso ao desfazer”Bloqueio desfeito. O horário voltou a ficar livre.""Block undone. The time slot is free again.""Bloqueo deshecho. El horario volvió a quedar libre.”
MSG-SAGM-03Sucesso ao encerrar”Bloqueio encerrado às {hh:mm}. O restante do horário voltou a ficar livre.""Block ended at {hh:mm}. The rest of the time slot is free again.""Bloqueo terminado a las {hh:mm}. El resto del horario volvió a quedar libre.”
MSG-IAGM-01Bloqueio já desfeito em outra sessão (EX-AGM-10)“Este bloqueio já tinha sido desfeito.""This block had already been undone.""Este bloqueo ya había sido deshecho.”
MSG-BAGM-01Ação de bloqueio de horário”Bloquear horário""Block time slot""Bloquear horario”
MSG-BAGM-02Ação de desfazer bloqueio”Desfazer""Undo""Deshacer”
MSG-BAGM-03Botão de voltar para hoje / prefixo da data de hoje”Hoje""Today""Hoy”
MSG-BAGM-04Rótulo do item bloqueado”Bloqueado""Blocked""Bloqueado”
MSG-BAGM-05Estado de confirmação rápida”Confirmar?""Confirm?""¿Confirmar?”
MSG-BAGM-06Botão do formulário de bloqueio”Confirmar bloqueio""Confirm block""Confirmar bloqueo”
MSG-BAGM-07Resumo de realizadas (singular / plural)”✓ 1 realizada” / ”✓ {n} realizadas""✓ 1 completed” / ”✓ {n} completed""✓ 1 realizada” / ”✓ {n} realizadas”
MSG-BAGM-08Resumo de canceladas (singular / plural)”✕ 1 cancelada” / ”✕ {n} canceladas""✕ 1 canceled” / ”✕ {n} canceled""✕ 1 cancelada” / ”✕ {n} canceladas”
MSG-BAGM-09Linha do horário atual (rótulo acessível)“Agora, {hh:mm}""Now, {hh:mm}""Ahora, {hh:mm}“
MSG-BAGM-11Rótulos do formulário de bloqueio”Bloquear horário · {data}” / “Início” / “Duração” / “Outro” / “minutos (múltiplos de 15)” / “Término: {hh:mm} · livre até {hh:mm}” / “Complemento (opcional)""Block time slot · {date}” / “Start” / “Duration” / “Other” / “minutes (multiples of 15)” / “End: {hh:mm} · free until {hh:mm}” / “Note (optional)""Bloquear horario · {fecha}” / “Inicio” / “Duración” / “Otro” / “minutos (múltiplos de 15)” / “Fin: {hh:mm} · libre hasta {hh:mm}” / “Nota (opcional)“
MSG-BAGM-12Botões genéricos da tela”Tentar novamente” / “Cancelar” / “Salvando…""Try again” / “Cancel” / “Saving…""Intentar de nuevo” / “Cancelar” / “Guardando…”
MSG-BAGM-13Ação de encerrar bloqueio em andamento”Encerrar agora""End now""Terminar ahora”
MSG-BAGM-14Sinalizadores de atraso”Esperando · {n}min” / “Não chegou · {n}min""Waiting · {n}min” / “Not arrived · {n}min""Esperando · {n}min” / “No llegó · {n}min”
MSG-BAGM-15Horário livre”Livre até {hh:mm}” / “Bloquear""Free until {hh:mm}” / “Block""Libre hasta {hh:mm}” / “Bloquear”
MSG-BAGM-16Menu e atalhos de dia com consulta”Mais opções” / “Próximo dia com consulta” / “Dia anterior com consulta” / “Nenhum” / “Escolher dia""More options” / “Next day with appointments” / “Previous day with appointments” / “None” / “Choose day""Más opciones” / “Próximo día con consultas” / “Día anterior con consultas” / “Ninguno” / “Elegir día”

Conformidade SBIS (detalhamento)

ECF.05.02 — Bloqueios na agenda (estágio 2, recomendado) — atendimento parcial (ver Definição, tabela SBIS): o requisito pede parametrização de bloqueios da clínica em dias específicos (fins de semana, feriados), que pertence à parametrização de agendas e horários (Recepção/Administração); esta tela cobre o bloqueio pontual da agenda do médico e respeita o horário de trabalho e as ausências cadastradas (RN-AGM-015). O fluxo de bloqueio (US-AGM-003/004/006, POST/DELETE/PATCH .../end em /api/v1/agenda/doctor/blocks) implementa a parte que cabe a esta tela, com as restrições de tempo de RN-AGM-012; o mecanismo de persistência (paciente/convênio reservados) é detalhado na Seção 4, quando escrita, junto da subseção “Seeds da Funcionalidade”.

ECF.05.04 — Especificação do tipo de consulta (estágio 2, recomendado) — atendido pela exibição do tipo de consulta em cada item da lista, quando configurado pela clínica (RN-AGM-008).

ECF.03.11 / ECF.03.14 — Busca simples de pacientes / Dados da lista de pacientes para seleção de prontuários (estágio 1, obrigatórios) — atendidos pela busca do cabeçalho, reutilizada da Central do Médico (RN-AGM-014); detalhamento na Central do Médico, Seção 2.10.

NGS1.07.03 / NGS1.07.04 — Eventos registrados na trilha de auditoria (estágio 1 / 2) — a leitura da agenda de um dia (GET /day no carregamento de um dia — a lista expõe nome e diagnóstico de pacientes; o polling não gera novo evento para o mesmo dia), a abertura do PEP a partir dela e a criação, a remoção e o encerramento de um bloqueio (POST/DELETE/PATCH .../end acima) devem gerar evento de auditoria, mesmo padrão já usado em outras funcionalidades assistenciais deste projeto (RN-AGM-004).

ECF.17.19 — Mensagens do sistema (estágio 1, obrigatório) — todas as mensagens exibidas ao médico estão catalogadas acima (ou na Central do Médico, para os elementos reutilizados), em linguagem não técnica, em português do Brasil, com suporte multilíngue para en-US e es-419.


Histórico de Versões

  • v1.0 (31/08/2026) — Arquivo criado. Especificação completa a partir das RNs fechadas na Seção 1 (v1.5): cabeçalho de navegação (RN-AGM-003, com os dois mecanismos de atalho confirmados pelo solicitante — setas de atalho e seletor de calendário), lista de consultas com os elementos herdados e os dois campos novos (RN-AGM-006/007/008/009), bloco de consultas já realizadas (RN-AGM-010), e o fluxo completo de bloqueio de horário (RN-AGM-004), incluindo a confirmação do solicitante de que o próprio médico pode desfazer um bloqueio e que o complemento é opcional nesse fluxo. Duas decisões de UI ainda não pedidas ao solicitante ficaram marcadas [PROPOSTA] no corpo (granularidade de horário do formulário de bloqueio; confirmação leve inline ao desfazer) — a confirmar antes do Protótipo (Seção 3). Deixado [A DEFINIR] se um bloqueio já ocorrido também deveria entrar no bloco recolhido de RN-AGM-010, por não estar coberto pela redação literal da regra.
  • v1.0 (revisão) (01/09/2026) — Submetido ao subagente crítico técnico antes de fechar a fase (Lição #20). Achados verificados contra o texto e corrigidos: (1) campo type renomeado para attendanceMode no JSON de exemplo, com nota de nomenclatura no corpo distinguindo-o de appointmentType — nomes muito parecidos, campos diferentes; (2) removido o campo delayMinutes do JSON de exemplo — não estava descrito em nenhuma regra nem na lista de elementos exibidos, ficou como campo “fantasma”; (3) resposta do DELETE /blocks/{id} renomeada de status para result, evitando colisão de nome com o status de atendimento clínico usado no GET /day; (4) adicionado "kind": "block" na resposta do POST /blocks, alinhando com o formato do item de bloqueio já usado no GET /day; (5) adicionadas CA-001.6, CA-001.7, CA-003.4 e CA-004.3, cobrindo os quatro Fluxos de Exceção (EX-AGM-02/03/04/05) que não tinham nenhum Critério de Aceitação correspondente; (6) corrigida a tabela de Mapeamento de Componentes — EX-AGM-02 estava listado como “Toast”, mas o texto do próprio fluxo descreve um estado de erro em tela cheia; criadas linhas próprias para os estados de erro/vazio de tela cheia (EX-AGM-02, EX-AGM-03), mantendo Toast só para EX-AGM-04/05; (7) qualificada a linha “AGORA” (RN-AGM-010) como exclusiva do dia de hoje — em outro dia a lista abre no topo, sem a linha; (8) identificada e resolvida a tensão entre RN-AGM-003 (“sem limite de horizonte”) e o endpoint days-with-appointments, que exige from/to limitados: criado um segundo endpoint, GET /next-appointment-date, sem limite de intervalo, dedicado ao atalho sequencial — days-with-appointments passa a servir só o marcador visual do calendário (mês exibido); (9) criado EX-AGM-06 (falha ao consultar dias com consulta), cobrindo um cenário de erro que não tinha nenhum tratamento especificado, com degradação silenciosa por não ser caminho crítico; (10) removido o código 403 do DELETE /blocks/{id} — como o endpoint já é escopado ao médico logado (RN-AGM-002), um bloqueio de outro médico não é alcançável nesse escopo; consolidado em 404, evitando revelar a existência de bloqueios de outros médicos; (11) adicionada validação [PROPOSTA] impedindo bloqueio de horário já no passado — não pedida ao solicitante, a confirmar. Nenhum achado do crítico foi descartado como falso positivo nesta rodada.
  • v1.1 (29/09/2026) — Acompanha 01-Definição v1.6 (alinhamento com a Central do Médico e decisões do solicitante desta rodada). Pedidos/decisões do solicitante: (1) cabeçalho com a mesma busca de pacientes da Central do Médico — nova subseção “Cabeçalho do Sistema”, reutilizando por referência a Seção 2.1 da Central (US-CM-000, mensagens MSG_search*, GET /api/v1/patients/search), e a barra de navegação do dia passa a ficar abaixo dele; (2) bloqueio já ocorrido permanece como item individual — resolvido o [A DEFINIR] da v1.0, com CA-005.6; (3) bloqueio no passado proibido — [PROPOSTA] da v1.0 confirmada (RN-AGM-012): botão desabilitado em dias passados (CA-001.7 ajustado, CA-003.5 novo), opções de início só futuras no dia de hoje (CA-003.6), revalidação ao confirmar (EX-AGM-07, MSG-EAGM-06, CA-003.7, código 422 no POST); extensão ao desfazer — só antes de o bloqueio começar (EX-AGM-08, MSG-EAGM-07, CA-004.4/004.5, código 422 no DELETE); (4) selo de atraso como na Central — delayMinutes volta ao JSON de GET /day (tinha sido removido na revisão técnica da v1.0 como campo “fantasma”, mas é um elemento herdado da Central), exibido só no dia de hoje (CA-001.9); (5) consultas realizadas e canceladas em dois blocos separados, não num bloco único “já realizadas” (que misturava canceladas e faltas sob um rótulo de “realizadas”) — subseção reescrita, CA-005.1 a 005.4, MSG-BAGM-07/08; (6) as duas [PROPOSTA] de UI da v1.0 confirmadas: formulário com intervalos de 15 minutos e durações 15/30/45/60/Outro (detalhado: campo de data somente leitura, término calculado, limite no fim do dia, validação de “Outro” — MSG-EAGM-08, CA-003.9, código 400) e desfazer com confirmação rápida inline — o intervalo de “alguns segundos” da v1.0 foi fixado em 5 segundos, valor escolhido pelo Designer nesta versão, sujeito a ajuste. Alinhamento com a Central do Médico (aprovado pelo solicitante): tags pelo critério de RN-CM-025 (CA-001.2 reescrito — antes citava só o bit de agendas) e tag “1ª Consulta” por RN-CM-028 (CA-001.8); status canceled com chip “Cancelado” em token neutro (color-neutral-light/color-neutral-dark) — a paleta da Central não tinha esse status porque a Central não exibe canceladas; destaque por borda lateral de “Recepcionado”/“Aguardando Recepção” (CA-001.10); lista de valores de status explicitada como a de AppointmentStatus() da Central (Seção 4.1); atualização automática a cada 1 minuto e indicador “Offline” (RN-AGM-013 — CA-001.11/001.12, EX-AGM-09, MSG-TAGM-03); indicador “Consultas Hoje” da Central como segundo ponto de entrada (CA-001.1); bloqueios fora do painel e do KPI da Central (RN-AGM-011 — CA-006.1, detalhe na Central do Médico, Especificação v4.37). Outros ajustes desta versão: (a) a linha “AGORA” passa a ser posicionada cronologicamente entre os itens individuais, não mais “logo abaixo do bloco” — ajuste necessário porque, com bloqueios já ocorridos e consultas atrasadas permanecendo como itens individuais, a descrição anterior colocaria a linha acima de itens anteriores ao horário atual (esclarecimento de comportamento, proposto pelo Designer); (b) atalho “próximo dia com consulta”, marcador do calendário e endpoints days-with-appointments/next-appointment-date passam a ignorar bloqueios (consequência de RN-AGM-011) e consultas canceladas (proposta do Designer, ver Histórico da Definição v1.6); (c) mecanismo do registro de bloqueio (paciente/convênio reservados, TipoAgenda = 'C' como default, decidido pelo solicitante) deixa de ser citado no corpo do POST — fica para a Seção 4; (d) layout mobile-first explicitado (premissa da Definição v1.6), com comportamento do cabeçalho e da barra em telas estreitas; (e) formulário de bloqueio como modal / bottom sheet (a v1.0 deixava “modal ou tela dedicada” para a Seção 3); (f) nova linha de componentes: cabeçalho do sistema, badge de atraso, pill de duração, indicador offline; estado “past” do item bloqueado; (g) nova subseção de conformidade ECF.03.11/03.14 (por referência). Acompanha 03-Protótipo v1.0.
  • v1.1 — Revisão por Dois Críticos (29/09/2026, mesma versão, antes da entrega ao solicitante) — Crítico técnico (subagente independente, com execução do protótipo no Playwright em 360px e 1280px) sobre esta Especificação e o Protótipo v1.0; 30 achados, cada um verificado contra o texto/código antes de aplicar. Aplicados nesta Especificação: (1) posição inicial da rolagem escondia consultas atrasadas ainda pendentes atrás das barras fixas (medido) — rolagem passa a ir para a primeira consulta que ainda exige ação, com deslocamento pela altura real das barras (CA-005.5 reescrito; mesmo achado do crítico de negócio); (2) referência de tempo ambígua (relógio do dispositivo × fuso da clínica) — novo campo serverNow em GET /day e parágrafo “Referência de tempo”; regra de virada do dia (CA-005.7); (3) polling podia substituir o estado de erro de EX-AGM-02 e o skeleton (medido) — polling só com o dia carregado (CA-001.11 ampliado), sem perda de foco e sem anunciar a lista inteira a leitores de tela; (4) estado “Confirmar?” sofria condição de corrida com re-renderizações (reproduzido) — explicitado que o estado pertence ao item; (5) DELETE 404 sem tratamento — novo EX-AGM-10, MSG-IAGM-01, CA-004.6; POST 400 mapeado para MSG-EAGM-08/02; (6) EX-AGM-06 era o único fluxo de exceção sem CA — novo CA-002.6; (7) textos exibidos fora do catálogo — incluídos MSG-TAGM-04/05/06, MSG-SAGM-01/02, MSG-BAGM-12 e novos rótulos em MSG-BAGM-11; “Cancelado” passa a reutilizar o termo status-cancelado da Central do Médico, Seção 2.6 (MSG-BAGM-10 removida — tinha es-419 divergente, “Cancelada”); (8) dica do botão desabilitado via title não chega ao toque nem ao teclado — botão passa a aria-disabled, com toast ao acionar (CA-003.5 reescrito); (9) contraste do avatar com texto branco sobre as cores claras de status (1,27:1 a 1,66:1) — texto color-neutral-darker; nova linha “Avatar” nos componentes; (10) attendanceMode tinha três representações (código no JSON, termo traduzido no Mapeamento da Central, rótulo no protótipo) — declarado como termo já traduzido, igual à Central 4.1, exemplo corrigido; (11) firstTime boolean e tags nunca contém “1ª Consulta” (evita duplicidade com a Central, que lista 1a_consulta como valor de tags); (12) “24:00” declarado explicitamente em endTime; (13) códigos 400/500 dos GETs, intervalo máximo de 62 dias em days-with-appointments (valor escolhido pelo Designer — dois meses de calendário) e momento de chamada de next-appointment-date; (14) auditoria da leitura da agenda (GET /day) e da abertura do PEP; (15) acessibilidade do formulário (foco preso, aria-invalid), alvos de toque de 44px em telas estreitas, itens dos blocos expandidos esmaecidos como na Central, complemento validado sem espaços nas pontas com o contador contando o mesmo valor; (16) ECF.05.02 como atendimento parcial e títulos/estágios corretos de ECF.03.11/03.14 (conferidos na planilha SBIS — ver Definição v1.6). Registrados como pendência, sem mudança: o contrato de tags (strings) não traz cor nem descrição, embora a cor venha do cadastro da tag (RN-CM-025) — o endpoint de consultas da Central tem a mesma lacuna; a resolver junto com a Seção 4 das duas funcionalidades. Não aplicados: contraste de textos secundários (color-neutral-medium/neutral-pure) e da tag de quimioterapia, e divergência do par de cores do chip “Finalizado” — padrões pré-existentes da Central/Design System, fora do escopo desta revisão; prefixo AGM nos IDs de CA — o padrão atual CA-00X.Y é o mesmo da v1.0 e de outros documentos; padrão combobox completo da busca — comportamento da Central, registrado como achado para aquele documento.
  • v1.2 (29/09/2026) — Acompanha 01-Definição v1.7. Redesenho de layout a pedido do solicitante (“a parte fixa com a data e os botões de navegação ocupa muito espaço; no celular ocuparia metade da tela”), a partir de uma revisão por dois subagentes independentes — um médico oncologista (uso real entre consultas, no celular) e um especialista em UX mobile — que mediram o protótipo v1.0: área fixa de 181px (28% da tela útil num Android, 33% num iPhone SE) e, somando os grupos recolhidos e o bloqueio passado, primeira consulta que exige ação só depois de 55–64% da tela. Aplicado o que os dois recomendaram em comum e o solicitante aprovou (“pode seguir”): (1) barra do dia em uma linha — a data vira o botão que abre o calendário (sai o botão de calendário separado), o rótulo “Hoje” separado dá lugar ao estado ativo do botão “Hoje”, e no celular os atalhos de dia com consulta saem da barra para o menu ⋮ e para o topo do calendário, com a data de destino escrita (o ícone de seta dupla, sozinho, não foi entendido pelo médico-crítico); em telas largas continuam ao lado das setas; área fixa-alvo ~116px; (2) coluna de horário à esquerda (início em destaque, término abaixo) — o horário ficava pequeno, cinza e à direita; (3) anatomia fixa do card em 2 linhas (+1 opcional para tags/complemento) — o card quebrava de 2 a 4 linhas de forma imprevisível; avatar removido no celular (a coluna de horário ocupa o lugar; em telas largas continua); (4) grupos de realizadas/canceladas viram uma linha de resumo com dois botões lado a lado; bloqueio terminado vira linha fina; (5) snackbar na base da tela, não no topo; ícone de “sem conexão” dentro do botão da data; formulário de bloqueio mais compacto (data no título, durações numa linha, complemento de uma linha que cresce, rodapé fixo). Decisões do solicitante nesta rodada: (a) “Bloquear horário”: entre o botão flutuante (proposta do UX) e o menu ⋮ + toque num horário livre (proposta do médico), o solicitante escolheu a segunda para ver no protótipo — novas linhas de horário livre (RN-AGM-016, US-AGM-007), tocáveis, que abrem o formulário pré-preenchido com o intervalo; em telas largas o botão continua na barra; (b) sinalizador de atraso separado em “Esperando” (forte) e “Não chegou” (neutro), só nesta tela — a Central do Médico está congelada para fechar a versão (achado do médico-crítico: o mesmo selo vermelho servia para “o paciente está me esperando” e “o paciente não chegou”); (c) status “Agendado” continua sempre exibido, por consistência (o médico-crítico propôs escondê-lo); o modo de atendimento presencial (padrão) é omitido e teleatendimento/customizado aparecem sempre — o JSON de GET /day passa a trazer attendanceMode como { code, label }, porque a omissão não pode depender do rótulo traduzido (na v1.1 era só o termo traduzido); (d) horário de trabalho (RN-AGM-015): o bloqueio só pode ser criado dentro dele — opções de início limitadas, término limitado ao fim do intervalo de trabalho, novo EX-AGM-11 / MSG-EAGM-09 / CA-003.10 / CA-003.11 e MSG-TAGM-07; aviso de ausência no dia (MSG-TAGM-08, CA-007.4); GET /day ganha workWindows, absence e freeSlots (horários livres calculados pelo back-end — escolha do Designer, para uma única fonte de verdade com o futuro Agendamento); (e) quem desfaz: qualquer bloqueio da agenda do médico, criado por ele ou pela Recepção — o critério é Agenda.IdChRecurso, sem saber quem criou (esclarecimento do solicitante) — US-AGM-004 e textos ajustados; (f) encerrar antes do fim um bloqueio em andamento — nova US-AGM-006, novo endpoint PATCH /blocks/{id}/end (término definido pelo servidor, no minuto exato — escolha do Designer; o solicitante não definiu arredondamento), EX-AGM-12, MSG-EAGM-10/11, MSG-SAGM-03, MSG-BAGM-13, CA-004.7/004.8; EX-AGM-08 e MSG-EAGM-07 passam a orientar para “Encerrar agora”. Não aplicado, registrado: coluna lateral com mini-calendário fixo em telas largas (proposta opcional dos dois críticos) — deixada para avaliação depois de o solicitante ver o novo layout; gesto de deslizar entre dias (proposta do médico) — não implementado (o UX alertou para conflito com o gesto de voltar do sistema); resumo numérico do dia (“2 esperando · 5 restantes”) proposto pelo médico — não incluído, para não somar uma linha à área fixa. Mensagens renumeradas/ajustadas: MSG-BAGM-02 passa a “Desfazer” (antes “Desfazer bloqueio”), MSG-BAGM-07/08 com ”✓”/”✕”, MSG-BAGM-09 passa a rótulo acessível, novas MSG-BAGM-14/15/16. Acompanha 03-Protótipo v1.1.
  • v1.3 (29/09/2026) — Acompanha 01-Definição v1.8. Decisões do solicitante ao avaliar o Protótipo v1.1: (1) início do bloqueio só entre os horários livres, em fatias de 15 minutos (antes: qualquer horário dentro do horário de trabalho, com a sobreposição validada só ao confirmar) — descrição do formulário, CA-003.6 reescrito com a lista exata de opções, validações; o término passa a ser limitado pelo fim do horário livre, não mais do intervalo de trabalho — EX-AGM-11 e MSG-EAGM-09 reescritos, durações que não cabem ficam indisponíveis (CA-003.10 reescrito, novo CA-003.13); EX-AGM-01 passa a cobrir só a condição de corrida no envio (CA-003.2 reescrito); MSG-TAGM-04 generalizada para “nenhum horário livre no dia”; (2) duração inicial de 15 minutos ao abrir o formulário, por qualquer entrada (antes, a partir de um horário livre, o intervalo inteiro — escolha do Designer na v1.2) — CA-003.1 reescrito, novo CA-003.12; (3) “Bloquear horário” sai da barra em telas largas e fica no menu ⋮ em qualquer largura — o solicitante achou o botão destacado demais para uma ação de uso eventual; os atalhos « » continuam visíveis em telas largas — novo CA-003.14, linha do botão removida da tabela de componentes; (4) “Encerrar agora” e troca de layout em 640px aprovados, sem mudança. Acompanha 03-Protótipo v1.2.