Épico 6 — Seleção de Clínica — Especificação (v1.2)

Documentos deste épico: 01 - Definição (v1.2) · 02 - Especificação (v1.2) · 03 - Protótipo (v1.1, código executável em 03 - Protótipo v1.1.html) · 04 - Mapeamento de Banco de Dados (v1.2). Este épico faz parte do documento guarda-chuva Check-in de Usuários.


SEÇÃO 2 — ESPECIFICAÇÃO

Histórias de Usuário

  • US-CIU-006: Como profissional com acesso a mais de uma clínica, quero escolher em qual clínica vou trabalhar logo após o login, para acessar rapidamente o contexto certo.
  • US-CIU-007: Como profissional com um convite pendente de uma nova clínica, quero aceitar esse convite na mesma tela onde escolho minha clínica, sem repetir a leitura de regras que já conheço, para agilizar meu acesso sem uma tela extra.

Descrição Funcional Detalhada

Lista de Clínicas

Exibe um item por clínica acessível (ClienteEmpresa) do profissional autenticado — agregando todos os vínculos ativos (UsuarioCliente.Situacao = 'V') e, para cada um, todas as ClienteEmpresa associadas via ClientePerfilAcessoUsuario — nome da clínica (ClienteEmpresa.Nome) e logo (ClienteEmpresa.CaminhoLogo, quando presente). Cada item é clicável e dispara a seleção daquela clínica específica (RN-CIU-015). [PROPOSTA — critério de ordenação não fornecido pelo solicitante] lista ordenada alfabeticamente por nome da clínica, na ausência de um critério mais específico (ex.: última clínica acessada).

Por construção, todo item que entra nesta lista já tem, no momento em que a lista é carregada, um registro em ClientePerfilAcessoUsuario — ou seja, já tinha Perfil de acesso associado. A seleção sem Perfil de acesso descrita em CA-006.3/EX-E6-01 abaixo não é, portanto, o caso comum: é uma condição de corrida — o administrador removeu o Perfil de acesso daquela clínica (via gestão de usuários da clínica, fora do escopo deste épico) no intervalo entre a lista ser carregada e o profissional clicar no item. O mesmo tipo de condição de corrida já é tratado, para a clínica inteira ser removida, em EX-E6-05; CA-006.3/EX-E6-01 é o equivalente para a associação de Perfil de acesso especificamente.

Motivador: é a lista normal de navegação do profissional que atende em mais de uma clínica — existia antes deste épico (E1, roteamento por múltiplos vínculos) e é apenas formalizada aqui.

Card de Convite Pendente

Um card por convite pendente (UsuarioCliente.Situacao = 'C'), visualmente destacado da lista de clínicas (RN-CIU-025) — borda ou fundo diferenciado (token --color-highlight-*, reservado para estados que pedem atenção, não para navegação normal), nome da(s) clínica(s) já associadas ao Perfil de acesso do convite (ou do Cliente convidante, como fallback, enquanto nenhuma ClienteEmpresa está associada — CIU-E3), botão primário “Aceitar”. Fica acima ou separado da lista, nunca misturado aos itens de seleção.

Motivador: o card representa uma ação pendente do profissional (decisão a tomar), não uma opção de navegação — misturá-lo à lista faria o profissional confundir “aceitar convite” com “selecionar clínica” (mesmo motivador já registrado em RN-CIU-025 / Seção 1).

Ao clicar em “Aceitar”: chama o endpoint de aceite (Seção “Integração com Backend”), atualiza Situacao para ‘V’, remove o card da área de destaque e insere a(s) clínica(s) associada(s) ao Perfil de acesso do convite na lista normal — sem selecionar nenhuma delas. O profissional permanece na tela, agora com um ou mais itens a mais na lista, conforme a quantidade de ClienteEmpresa associadas ao Perfil de acesso do convite. Quando o convite não tinha nenhuma ClienteEmpresa associada (RN-CIU-011, em CIU-E3, permite deixar essa seleção pendente), nenhum item novo entra na lista — comportamento visual desse caso ainda [A DEFINIR] (ver 01 - Definição, RN-CIU-025 e lista de itens [PROPOSTA]).

Seleção de Clínica

Clicar num item da lista dispara a verificação de Perfil de acesso (RN-CIU-015) para aquela clínica. Dois desfechos:

  • Perfil de acesso encontrado: navega para a tela inicial do profissional naquela clínica (cockpit — CM_CockPit_do_medico, ou equivalente de cada perfil profissional).
  • Perfil de acesso ausente: permanece nesta tela, exibindo mensagem orientativa (MSG-I6-01) — mesmo texto e tratamento usados no E3 para o caso equivalente pós-aceite do vínculo (RN-CIU-015). Como descrito em “Lista de Clínicas” acima, este desfecho só ocorre por condição de corrida (Perfil de acesso removido entre o carregamento da lista e o clique) — todo item chega à lista já com Perfil de acesso associado.

Motivador: a verificação de Perfil de acesso é sempre a mesma, o momento em que roda é que muda — antes (E3, aceite via tela cheia) ou aqui (na seleção) (ver RN-CIU-025).

Critérios de Aceitação

CA-006.1: Dado que estou autenticado e tenho acesso a mais de uma clínica, quando o roteamento pós-login me traz a esta tela, então vejo a lista de clínicas acessíveis, uma por item.

CA-006.2: Dado que estou nesta tela, quando seleciono uma clínica da lista e tenho Perfil de acesso associado a ela, então sou levado à tela inicial do meu perfil profissional naquela clínica.

CA-006.3: Dado que estou nesta tela, quando seleciono uma clínica da lista cujo Perfil de acesso foi removido depois que a lista foi carregada (condição de corrida — ver “Lista de Clínicas”, Descrição Funcional), então permaneço na tela e vejo a mensagem orientativa MSG-I6-01.

CA-006.4: Dado que tenho acesso a exatamente 1 clínica e nenhum convite pendente, quando faço login, então não vejo esta tela — vou direto ao cockpit (RN-CIU-009).

CA-007.1: Dado que tenho ao menos um vínculo ativo e um convite pendente de outra clínica, quando chego a esta tela, então vejo um card em destaque com o nome da clínica convidante e um botão “Aceitar”, separado da lista normal.

CA-007.2: Dado que vejo o card de convite pendente, quando clico em “Aceitar”, então o vínculo passa a Situacao = 'V', o card sai da área de destaque e a(s) clínica(s) associada(s) ao Perfil de acesso do convite passam a constar na lista normal.

CA-007.3: Dado que acabei de aceitar um convite pelo card, quando a ação é concluída, então não sou levado automaticamente para nenhuma das clínicas recém-aceitas — preciso selecionar uma delas na lista, como qualquer outra.

CA-007.4: Dado que tenho exatamente 1 vínculo ativo e um convite pendente simultâneo, quando faço login, então vejo esta tela (não o cockpit direto) — com a clínica do vínculo ativo na lista e o card do convite em destaque.

CA-007.5: Dado que o card de convite pendente ainda não foi aceito, quando verifico meu Perfil de acesso para a clínica em vias de aceitar, então nenhuma verificação de Perfil de acesso é feita — ela só ocorre depois, quando eu selecionar essa clínica já aceita na lista.

Fluxos de Exceção

EX-E6-01 — Seleção sem Perfil de acesso associado

Gatilho: profissional seleciona uma clínica da lista para a qual não existe registro em ClientePerfilAcessoUsuario (mesma verificação de RN-CIU-015, reaproveitada do E3) — na prática, uma condição de corrida: todo item chega à lista já com Perfil de acesso associado (ver “Lista de Clínicas”, Descrição Funcional), então isso só acontece se o administrador removeu essa associação depois de a lista ter sido carregada. Comportamento: permanece na tela de seleção, exibe mensagem orientativa MSG-I6-01 associada ao item da lista. Recuperação: profissional aguarda o administrador da clínica configurar seu Perfil de acesso, ou seleciona outra clínica da lista.

EX-E6-02 — Falha ao aceitar convite pelo card

Gatilho: back-end retorna erro ao tentar atualizar Situacao de ‘C’ para ‘V’ no aceite do card. Comportamento: toast de erro MSG-E6-01; o card permanece na área de destaque, inalterado. Recuperação: profissional tenta “Aceitar” novamente.

EX-E6-03 — Convite expira enquanto o profissional está na tela

Gatilho: profissional tenta aceitar um card cujo convite expirou (RN-CIU-024, em CIU-E3) entre o carregamento da tela e o clique em “Aceitar”. Comportamento: toast de erro MSG-E6-02; o card sai da área de destaque (o convite não existe mais como pendente). Recuperação: profissional contata o administrador da clínica convidante para receber um novo convite — mesma orientação do E3 (RN-CIU-024).

EX-E6-04 — Falha ao carregar a lista de clínicas e convites

Gatilho: erro de rede ou falha do back-end ao buscar vínculos ativos e convites pendentes do profissional autenticado. Comportamento: estado de erro na tela (mensagem MSG-E6-03 + ação “Tentar novamente”), sem lista nem cards exibidos. Recuperação: profissional aciona “Tentar novamente”; se persistir, orientação a contatar o suporte.

EX-E6-05 — Clínica removida entre o carregamento da lista e a seleção

[PROPOSTA — cenário não descrito pelo solicitante] Gatilho: profissional seleciona uma clínica cujo registro em ClienteEmpresa foi removido (RemovidoEm preenchido) no intervalo entre carregar a lista e clicar no item. Comportamento proposto: toast de erro MSG-E6-04, lista recarregada automaticamente sem o item removido. Recuperação: profissional seleciona outra clínica da lista atualizada. Sinalizado como proposta por ser um cenário de borda não descrito pelo solicitante — a confirmar se vale a pena tratar nesta primeira versão ou se é aceitável deixar como erro genérico de seleção.

Validações de Campos

Esta tela não tem campos de entrada de dados — apenas seleção de itens de lista e um botão de aceite por card. Não há, portanto, validações de campo a documentar (diferente de E2/E3, que têm formulários).

Mapeamento de Componentes de Interface

ComponenteVarianteEstadosTokens aplicadosUso nesta tela
CardSeleção (clínica)default, hover, focus, loading--radius-l (12px), --shadow-level-light, --color-base-pure bg, --spacing-m paddingUm por clínica com vínculo ativo, na lista normal
CardDestaque (convite pendente)default, loading (durante aceite)--radius-l (12px), borda --border-medium em --color-highlight-pure (#FFC400), --color-highlight-light bg (#FFF8E1)Um por convite pendente, separado da lista
ButtonPrimarydefault, hover, loading, disabled--btn-height (40px), --color-primary-pure bg (#00E676), --color-base-pure texto, --radius-m (8px)Botão “Aceitar” no card de convite (MSG-B6-01)
Banner/Ícone informativoInfodefault--color-secondary-pure (#40C4FF)Mensagem de bloqueio sem Perfil de acesso (MSG-I6-01), associada ao item da lista
ToastErrordefault--color-error-pure (#F44336)Falhas de aceite de convite, expiração, carregamento (MSG-E6-01 a 04)
SkeletonListaloading--color-neutral-lighter (#E5EAF0)Estado de carregamento inicial da tela, antes da lista e dos cards chegarem

Integração com Backend

GET /api/v1/checkin/clinic-selection Função: retorna, para o profissional autenticado, a lista de clínicas acessíveis e os convites pendentes. Request: nenhum parâmetro (usuário identificado pelo token de sessão). Response: { clinicasAtivas: [{ usuarioClienteId, clienteBDId, clienteEmpresaId, nome, caminhoLogo }], convites: [{ usuarioClienteId, clienteBDId, clienteNome, clienteLogo, clienteEmpresas: [{ clienteEmpresaId, nome, caminhoLogo }] }] }clienteEmpresas vem vazio enquanto o convite ainda não tem Perfil de acesso/clínica associados (CIU-E3); nesse caso o front-end usa clienteNome/clienteLogo (Cliente) como fallback no card. O campo é chamado convites (não convitesPendentes) de propósito — no login (E1, /api/auth/login) convitesPendentes é um número (contagem), usado só para decidir o desvio de roteamento; aqui é uma lista completa de objetos, e reaproveitar o mesmo nome para um formato diferente criaria ambiguidade entre os dois documentos. Códigos: 200 (sucesso, listas podem vir vazias apenas para convites); 401 (não autenticado); 500 (falha ao carregar — EX-E6-04). Tabelas envolvidas: UsuarioCliente, ClientePerfilAcessoUsuario, ClienteEmpresa (IpSeguranca).

POST /api/v1/checkin/clinic-selection/{usuarioClienteId}/accept-invite Função: aceita o convite pendente de um card, sem selecionar nenhuma clínica. Request: nenhum corpo — usuarioClienteId no path identifica o vínculo. Response: { usuarioClienteId, clienteEmpresas: [{ clienteEmpresaId, nome, caminhoLogo }] } (uma ou mais clínicas, conforme associadas ao Perfil de acesso do convite — agora prontas para entrar na lista normal; vem vazio quando o convite não tinha nenhuma ClienteEmpresa associada — RN-CIU-011, em CIU-E3). Códigos: 200 (sucesso); 404 (convite não encontrado ou já resolvido); 409 (convite expirado — EX-E6-03, retorna MSG-E6-02); 500 (falha ao atualizar — EX-E6-02). Tabelas envolvidas: UsuarioCliente (atualiza Situacao), ClientePerfilAcessoUsuario e ClienteEmpresa (para montar o array clienteEmpresas da resposta), UsuarioClienteConvite (deleta o registro — mesma tabela e mecanismo do E3, RN-CIU-024).

POST /api/v1/checkin/clinic-selection/{usuarioClienteId}/{clienteEmpresaId}/select Função: seleciona a clínica específica para a sessão de trabalho, rodando a verificação de Perfil de acesso. Request: nenhum corpo — usuarioClienteId e clienteEmpresaId no path. Response, com Perfil de acesso: { perfilEncontrado: true, redirecionarPara: "<rota do cockpit do perfil>" }. Sem Perfil de acesso: { perfilEncontrado: false, mensagem: "MSG-I6-01" }. Códigos: 200 (sucesso, com ou sem perfil — o corpo diferencia); 404 (clínica removida ou vínculo inválido — EX-E6-05); 401 (não autenticado). Tabelas envolvidas: UsuarioCliente, ClientePerfilAcesso, ClientePerfilAcessoUsuario (filtrado também por ClienteEmpresaId) (IpSeguranca) — mesma verificação de RN-CIU-015, em CIU-E3.

Nota sobre padronização de nomes: os três endpoints acima e o E3 (GET /api/convites/validar, 02 - Especificação) usam o mesmo par de nomes para o mesmo conceito — array clienteEmpresas, item com campo clienteEmpresaId — eliminando a inconsistência que existia entre clienteEmpresas: [{ id, ... }] (E3), clienteEmpresas: [{ clienteEmpresaId, ... }] (este GET) e clinicas: [{ clienteEmpresaId, ... }] (este accept-invite, agora renomeado para clienteEmpresas).

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

Erros:

IDContextopt-BRen-USes-419
MSG-E6-01Falha ao aceitar convite pelo card”Não foi possível aceitar o convite. Tente novamente.""Could not accept the invitation. Please try again.""No fue posible aceptar la invitación. Inténtelo de nuevo.”
MSG-E6-02Convite expirado ao tentar aceitar”Este convite expirou. Entre em contato com o administrador da clínica para receber um novo convite.""This invitation has expired. Contact the clinic’s administrator to receive a new invitation.""Esta invitación ha expirado. Comuníquese con el administrador de la clínica para recibir una nueva invitación.”
MSG-E6-03Falha ao carregar lista de clínicas e convites”Não foi possível carregar suas clínicas. Tente novamente.""Could not load your clinics. Please try again.""No fue posible cargar sus clínicas. Inténtelo de nuevo.”
MSG-E6-04Clínica removida entre carregar a lista e selecionar [PROPOSTA]“Esta clínica não está mais disponível.""This clinic is no longer available.""Esta clínica ya no está disponible.”

Informativas:

IDContextopt-BRen-USes-419
MSG-I6-01Perfil de acesso ausente na clínica selecionada”Você ainda não tem um perfil de acesso configurado nesta clínica. Aguarde ou entre em contato com o administrador.""You don’t have an access profile configured at this clinic yet. Wait or contact the administrator.""Aún no tiene un perfil de acceso configurado en esta clínica. Espere o comuníquese con el administrador.”

Textos orientadores:

IDContextopt-BRen-USes-419
MSG-T6-01Título da área de destaque de convites”Convite pendente""Pending invitation""Invitación pendiente”
MSG-T6-02Título da lista de clínicas”Selecione uma clínica""Select a clinic""Seleccione una clínica”

Botões/links:

IDContextopt-BRen-USes-419
MSG-B6-01Botão (card de convite)“Aceitar""Accept""Aceptar”
MSG-B6-02Ação de recuperação (erro de carregamento)“Tentar novamente""Try again""Intentar de nuevo”

Conformidade SBIS (detalhamento)

NGS1.03.01 — Impedir acesso por pessoas não autorizadas (estágio 1, obrigatório) — a seleção de clínica só concede acesso à tela inicial do profissional quando há Perfil de acesso associado (RN-CIU-015); sem ele, o profissional permanece bloqueado nesta tela, com orientação (MSG-I6-01).

NGS1.03.03 — Gerenciamento de perfis (estágio 1, obrigatório) — este épico consome a verificação de Perfil de acesso a cada seleção de clínica, não apenas uma vez no aceite do vínculo (E3) — reforça o controle a cada troca de contexto de trabalho.

ECF.17.19 — Mensagens do sistema (estágio 1, obrigatório) — todas as mensagens exibidas ao profissional estão catalogadas acima, 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 (29/08/2026) — Arquivo criado pela divisão do documento único (CIU-E6) Seleção de Clínica v1.0.md em arquivos por seção (ver Skill Designer, “Organização Física: Pasta por Funcionalidade”, Lição #18). Conteúdo sem alteração de substância em relação à v1.0 original — apenas reorganização física (o protótipo HTML já era um arquivo separado desde a v1.0 original, só foi renomeado para a convenção nova). Histórico de revisões anterior a esta divisão (v0.1 a v1.0) preservado integralmente no documento original arquivado.
  • v1.1 (14/09/2026) — Lista de Clínicas e card de convite pendente passam a operar por ClienteEmpresa (clínica), não por Cliente (tenant): cada item da lista é uma clínica acessível, e o aceite de um convite pode inserir mais de um item de uma vez, conforme a quantidade de ClienteEmpresa associadas ao Perfil de acesso do convite. Endpoints atualizados (GET .../clinic-selection, .../accept-invite, .../select — este último agora recebe clienteEmpresaId no path). Ver RN-CIU-009 (CIU-E1) e RN-CIU-011/015 (CIU-E3) para a regra completa.
  • v1.2 (14/09/2026) — quatro correções sobre a v1.1: (1) esclarecida a aparente contradição entre “Lista de Clínicas” (todo item já tem Perfil de acesso por construção) e CA-006.3/EX-E6-01 (seleção sem Perfil de acesso): o segundo caso é uma condição de corrida — Perfil de acesso removido entre carregar a lista e selecionar — mesmo padrão já usado em EX-E6-05 para a clínica inteira ser removida; texto de ambos os pontos explicitado; (2) Tabelas envolvidas do endpoint accept-invite estava incompleta — incluía apenas UsuarioCliente e UsuarioClienteConvite, mas a resposta monta um array de ClienteEmpresa que depende de ClientePerfilAcessoUsuario e ClienteEmpresa; ambas adicionadas; (3) padronização de nomes entre E3 e E6: o array de clínicas de um convite agora se chama clienteEmpresas com campo clienteEmpresaId nos três lugares onde aparece (E3 GET /convites/validar, este GET .../clinic-selection, este POST .../accept-invite — que usava clinicas, renomeado); (4) o array de convites pendentes de GET .../clinic-selection, antes convitesPendentes, renomeado para convites — no login (E1) convitesPendentes é um número, e reaproveitar o nome aqui para uma lista de objetos criava um conflito de tipos entre os dois documentos. RN-CIU-025 (01 - Definição) também ganhou, nesta mesma revisão, uma nota sobre o caso de borda de convite aceito sem nenhuma ClienteEmpresa associada — refletida aqui na Descrição Funcional e no endpoint accept-invite.