Épico 6 — Seleção de Clínica — Mapeamento de Banco de Dados (v2.0)
Documentos deste épico: 01 - Definição (v2.0) · 02 - Especificação (v2.0) · 03 - Protótipo (v2.0, código executável em 03 - Protótipo v2.0.html) · 04 - Mapeamento de Banco de Dados (v2.0). Este épico faz parte do documento guarda-chuva Check-in de Usuários.
SEÇÃO 4 — MAPEAMENTO DE BANCO DE DADOS
Tabelas existentes utilizadas
| Tabela | Banco | Campos utilizados neste épico |
|---|---|---|
| UsuarioCliente | IpSeguranca | Id, UsuarioId, ClienteBDId, Situacao (domínio C/V/B — RN-CIU-011, em CIU-E3), RemovidoEm — ClienteBDId é uma coluna de chave estrangeira escalar (um único valor por linha, não uma tabela de associação N:N), o que por si só garante que cada vínculo ou convite pertence a exatamente um Ambiente — usado para saber a qual grupo Cliente/Ambiente a resposta de accept-invite pertence (02 - Especificação); a restrição UNIQUE(UsuarioId, ClienteBDId) é uma regra de negócio distinta (não permitir dois vínculos do mesmo usuário no mesmo Ambiente), não a origem do fato acima |
| ClienteBD | IpSeguranca | Id, Nome, Tipo, ClienteId — é o Ambiente de trabalho (RN-CIU-031); Nome é exibido no cabeçalho do container quando o Ambiente não é de produção; ClienteId liga o Ambiente ao Cliente contratante, usado para formar o grupo (Rótulo de Cliente); Tipo (char(1), sem CHECK explícito) é o campo indicado pelo solicitante como o que diferencia produção de homologação/teste — [PROPOSTA] o mapeamento exato de valor de domínio para “produção”/“homologação” ainda não está catalogado na Biblioteca de Schema (DDL), pendência já registrada em Log de Mudanças Estruturais do Banco de Dados desde a revisão da Seção 1 |
| Cliente | IpSeguranca | Id, Nome, CaminhoLogo — é o Cliente contratante (RN-CIU-031); Nome é o texto exibido no rótulo discreto acima de cada grupo, suprimido quando o profissional só tem vínculo com 1 Cliente; CaminhoLogo é a fonte do campo clienteLogo usado como fallback no card de convite sem clínica associada (02 - Especificação, GET .../clinic-selection) |
| ClienteEmpresa | IpSeguranca | Id, Nome, CaminhoLogo, ClienteBDId, RemovidoEm — exibidos na lista de clínicas e no card de convite; cada registro é uma clínica dentro de um Ambiente; a Lista de Clínicas filtra RemovidoEm IS NULL |
| ClientePerfilAcesso | IpSeguranca | Id, Nome, ClienteBDId — consultado na verificação de Perfil de acesso (RN-CIU-015) |
| ClientePerfilAcessoUsuario | IpSeguranca | Id, ClientePerfilAcessoId, UsuarioClienteId, ClienteEmpresaId, EmpresaPadrao — consultado na verificação de Perfil de acesso (RN-CIU-015) e na montagem da resposta de accept-invite/clinic-selection (02 - Especificação); é a partir daqui que a lista de clínicas acessíveis é composta, uma por ClienteEmpresa distinta; EmpresaPadrao (char(1), DEFAULT 'N') é o campo que determina qual clínica é a principal do profissional dentro de um Ambiente (RN-CIU-031, item 2) — [PROPOSTA] o domínio de valores não está catalogado explicitamente na Biblioteca de Schema, presume-se 'S'/'N' pelo padrão já usado em outros campos char(1) de sinalização binária deste mesmo schema, a confirmar |
| UsuarioClienteConvite | IpSeguranca | Token, DataExpiracao — consultados/apagados no aceite do card (mesma tabela criada em CIU-E3, Seção 4; deleção física ao aceitar, RN-CIU-024) |
Nenhuma tabela nova. Este épico passa a consultar também ClienteBD e Cliente — ambas já existentes e já mapeadas em outros épicos deste conjunto (E1, para nome/logo do Cliente no login) — para exibir o agrupamento por Ambiente e por Cliente definido em RN-CIU-031; nenhuma estrutura de dados nova foi necessária.
Fonte de dado candidata para a ordenação por acesso recente (RN-CIU-031, item 4)
A ordenação da lista pela clínica acessada mais recentemente pelo profissional (critério ainda [PROPOSTA] quanto à janela de tempo e ao desempate — ver 01 - Definição) depende de uma fonte de dado de acesso por clínica que este épico, até esta revisão, não havia precisado consultar. Foi localizada na Biblioteca de Schema (DDL) uma tabela candidata:
| Tabela | Banco | Campos candidatos |
|---|---|---|
| SessaoUsuario | IpSeguranca | EmpresaId (clínica acessada), ClienteId, ClienteBDId (Ambiente da sessão), CriadoEm (data/hora de criação da sessão), Status, RemovidoEm, UsuarioIdCriado (candidato a “profissional dono da sessão” — ver ressalva abaixo) |
[PROPOSTA — a confirmar com o solicitante antes de implementar]: SessaoUsuario registra cada sessão de trabalho já com a clínica (EmpresaId), o Cliente (ClienteId) e o Ambiente (ClienteBDId) associados, e CriadoEm daria a data do acesso — o candidato natural para MAX(CriadoEm) agrupado por EmpresaId, filtrado pelo profissional autenticado e por uma janela de tempo (7 dias, sugestão do solicitante na Seção 1, ainda a validar). A tabela, porém, não tem uma coluna própria de “usuário dono da sessão”: tem apenas UsuarioIdCriado, um campo de auditoria (padrão “criado por” repetido em quase toda tabela deste schema), não uma coluna semântica de propriedade. Usar UsuarioIdCriado como “o profissional dessa sessão” é uma inferência razoável para uma sessão de login — mas é uma inferência, não um fato confirmado na Biblioteca de Schema nem pelo solicitante; por isso permanece como proposta, não como mapeamento definitivo, seguindo a mesma cautela já aplicada a outras inferências de schema deste conjunto de épicos (Skill Designer, Lição #21). Se confirmada, a resposta do endpoint de listagem (02 - Especificação, campo ultimoAcesso) viria de uma subconsulta MAX(CriadoEm) por EmpresaId, e não exigiria nenhuma tabela ou coluna nova.
Domínio de campos ainda não catalogados
| Campo | Tabela | Situação |
|---|---|---|
Tipo | ClienteBD | char(1) NOT NULL, sem CHECK no DDL — valores que distinguem produção de homologação/teste não catalogados; pendência já registrada em Log de Mudanças Estruturais do Banco de Dados |
EmpresaPadrao | ClientePerfilAcessoUsuario | char(1) DEFAULT 'N' NOT NULL, sem CHECK no DDL — domínio presumido 'S'/'N' por convenção do schema, não confirmado |
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.mdem 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) — trocada
ClienteporClienteEmpresanas tabelas existentes utilizadas;ClientePerfilAcessoUsuariopassa a listar o campoClienteEmpresaId, base para compor a lista de clínicas acessíveis (RN-CIU-009, em CIU-E1). Nenhuma tabela nova — ambas já existem em produção. -
v1.2 (14/09/2026) — atualização de referência: 01-Definição, 02-Especificação e 03-Protótipo deste épico foram revisados (v1.2, v1.2 e v1.1, respectivamente); a nota sobre
ClientePerfilAcessoUsuariofoi complementada para deixar explícito que essa tabela também é usada para montar a resposta do endpointaccept-invite(02 - Especificação, “Tabelas envolvidas”), lacuna apontada em revisão técnica. Nenhuma tabela nova nem alteração na lista de tabelas já mapeada na v1.1. -
v1.3 (14/09/2026) — adicionado o campo
RemovidoEmà tabelaClienteEmpresa, usado para filtrar a Lista de Clínicas às apenas ativas — consequência da confirmação do solicitante de que umaClienteEmpresadesativada tira o acesso dos usuários hoje associados a ela. Nenhuma tabela nova. -
v2.0 (18/09/2026) — adicionadas
ClienteBDeClienteàs tabelas utilizadas, para exibir o agrupamento por Ambiente e por Cliente de RN-CIU-031 (01 - Definição v2.0); campoEmpresaPadrao(ClientePerfilAcessoUsuario) documentado como a marcação de clínica principal usada para ordenar dentro de um Ambiente. Confirmado na Biblioteca de Schema (DDL),IpSeguranca.sql:UsuarioCliente.ClienteBDIdé uma coluna de chave estrangeira escalar, o que garante que um vínculo/convite pertence a exatamente um Ambiente — usado para simplificar a resposta deaccept-invite(02 - Especificação); a restriçãoUNIQUE(UsuarioId, ClienteBDId), também presente nessa tabela, é uma regra de negócio distinta (não permitir dois vínculos do mesmo usuário no mesmo Ambiente) e não a origem desse fato — atribuição corrigida nesta revisão após achado da revisão de crítico técnico (ver abaixo). Nova seção “Fonte de dado candidata para a ordenação por acesso recente”: localizada a tabelaSessaoUsuario(colunasEmpresaId,ClienteId,ClienteBDId,CriadoEm,UsuarioIdCriado) como candidata a fonte do critério de ordenação por acesso recente ([PROPOSTA] em RN-CIU-031, item 4) — registrada como [PROPOSTA, não implementação definitiva], já que a tabela não tem uma coluna semântica de “usuário dono da sessão” (apenas o campo de auditoriaUsuarioIdCriado), e usá-la para esse fim é uma inferência a confirmar com o solicitante, não um fato já estabelecido. Nova seção “Domínio de campos ainda não catalogados”, consolidando duas pendências de domínio (ClienteBD.Tipo, já registrada desde a revisão da Seção 1;ClientePerfilAcessoUsuario.EmpresaPadrao, nova nesta revisão) — nenhuma delas exige tabela ou coluna nova, apenas confirmação de valores.Revisão de crítico técnico (Skill Designer, “Revisão por Dois Críticos”) — rodada como subagente independente sobre as Seções 2, 3 e 4 revisadas nesta mesma rodada (agrupamento Cliente/Ambiente, RN-CIU-031). Cada achado foi reverificado por mim contra o schema real ou o documento-fonte antes de decidir a correção; nenhum foi aplicado sem essa verificação. 17 problemas encontrados:
- Contradição entre esta seção (Histórico dizia “revisão registrada ao final desta entrada”) e a 02 - Especificação v2.0 (Histórico dizia “revisão pendente de execução conjunta”) — nenhuma das duas de fato continha a lista de achados. Corrigido: ambas as entradas de Histórico reescritas nesta revisão para refletir a revisão concluída, com a lista completa registrada apenas aqui.
- Tabela
Clienteacima listava apenasId, Nome, omitindoCaminhoLogo— coluna real (confirmada na Biblioteca de Schema (DDL)) que sustenta o campoclienteLogousado como fallback pela 02 - Especificação. Corrigido:CaminhoLogoadicionado à linhaClienteacima. - No protótipo (03 - Protótipo v2.0.html), o estado inicial (
default) exibia o rótulo de Cliente mesmo havendo apenas 1 grupo de Cliente na tela — contradiz RN-CIU-031 item 1 / CA-006.6 (rótulo suprimido com 1 único Cliente). Corrigido: funçãoupdateClientLabels()implementada no protótipo, reavaliando a contagem de grupos e aplicando a classesingle-client(já existente em CSS) na carga inicial e após o aceite de convite criar um novo grupo. - Texto de MSG-I6-02 catalogado em 02 - Especificação não coincidia literalmente com o texto exibido no protótipo. Corrigido: textos alinhados nos dois documentos.
- O toast exibido no protótipo para o caso “convite aceito com clínica” (“Convite aceito. Selecione a clínica na lista para entrar.”) não tinha um MSG-ID catalogado. Corrigido: nova mensagem MSG-I6-03 adicionada ao catálogo de 02 - Especificação.
- MSG-I6-02 estava documentada em 02 - Especificação como um componente “Banner”, mas implementada no protótipo como um toast transiente. Corrigido: decisão de manter MSG-I6-02 como banner persistente (é um estado contínuo — o profissional segue sem acesso até liberação do administrador, não uma confirmação pontual); o protótipo foi alterado para inserir um banner inline no lugar do card de convite, e o toast passou a ser exclusivamente o de MSG-I6-03 (caso “convite aceito com clínica”, transiente por natureza).
- MSG-T6-02, catalogada como “título da lista”, é na verdade o H1 da tela (“Selecione uma clínica”); a lista tem um título próprio, não catalogado. Corrigido: descrição de MSG-T6-02 corrigida para “Título da tela (H1)”; nova MSG-T6-03 adicionada para “Suas clínicas” (título da seção de lista).
- CA-006.9 estava redigido de forma incompatível com o mecanismo de remoção reativa descrito em EX-E6-05. Corrigido: CA-006.9 reescrito para descrever a remoção imediata do item e do container do Ambiente, referenciando EX-E6-05 diretamente.
- A justificativa técnica de “um vínculo/convite pertence a exatamente um Ambiente” (Card de Convite Pendente e endpoint
accept-invite, em 02 - Especificação; tabelaUsuarioClienteacima e o parágrafo do Histórico da v2.0 anterior a esta correção) atribuía o fato à restriçãoUNIQUE(UsuarioId, ClienteBDId)— tecnicamente incorreto: a causa éClienteBDIdser uma coluna de chave estrangeira escalar (um valor por linha); aUNIQUEé uma regra de negócio à parte (impedir vínculo duplicado no mesmo Ambiente), não a origem da propriedade “um único Ambiente por linha”. Corrigido nos três lugares (02 - Especificação, tabelaUsuarioClienteacima, e nesta mesma entrada de Histórico). - A tabela de “campos candidatos” de
SessaoUsuario(seção acima) omitiaUsuarioIdCriado, mesmo o parágrafo logo abaixo dependendo inteiramente dessa coluna para propor (com ressalva) o uso da tabela. Corrigido:UsuarioIdCriadoadicionado à tabela. - Falhas de contraste WCAG 2.1 AA identificadas no protótipo: (a) texto do cabeçalho do Ambiente de testes (
--color-highlight-darksobre--color-highlight-light, ≈2,1:1) — corrigido, trocado para--color-neutral-darker; (b) texto do rótulo de Cliente (--color-neutral-light) — corrigido, trocado para--color-neutral-pure; (c) texto de corpo geral em--color-neutral-puresobre fundo claro e (d) texto branco sobre--color-primary-purenos botões primários — não corrigidos nesta revisão: são padrões pré-existentes, herdados sem alteração do protótipo oficial já publicado (v1.1) e usados em todo o produto: corrigi-los apenas neste documento criaria inconsistência com o restante do Gemed já em produção. Registrado aqui como observação de nível Design System, a ser tratada centralmente (Design System.md), não como pendência deste épico. - Elementos decorativos sem
aria-hidden="true": os 6 chevrons (.chevron) e as 7 iniciais de avatar (.clinic-avatar) do protótipo. Corrigido:aria-hidden="true"adicionado a todas as ocorrências. - Os atributos
role="button"etabindex="0"das linhas de clínica (.clinic-row) só eram aplicados via JavaScript (bindClinicRow()), ficando ausentes até a execução do script. Corrigido: atributos movidos para HTML estático nas linhas interativas (dentro de#clinic-groupse no template de clínica criado pelo aceite de convite); a atribuição via JS foi removida por redundante. As linhas de exemplo do estadoblocked(demonstração estática, sem manipulador de clique) foram deixadas sem esses atributos, por não serem de fato interativas nesse estado do protótipo. - Ícones fora do padrão do projeto (lucide.dev / react-icons): o chevron (caractere
›), o ícone de erro (!), o ícone de sucesso (✓) e o ícone do banner informativo (i) eram caracteres de texto/CSS, não ícones das bibliotecas aprovadas. Corrigido: os quatro substituídos por SVG inline dos ícones Lucide correspondentes (chevron-right,alert-circle,check,info). - O atributo
data-cliente-bd-idno protótipo, ao ser lido via.dataset(que converte para camelCase), pode ser confundido com um espelho literal do campoclienteBDIdda API — hoje inofensivo (não é lido por nenhum script), mas uma fonte de confusão futura. Corrigido: comentário explicativo adicionado no HTML, esclarecendo que é uma anotação visual/de demonstração do mock, não um campo de resposta da API. - A abertura da 03 - Protótipo v2.0.md afirmava cobrir os “touchpoints de ponta a ponta”, mas os desfechos de falha do aceite de convite (EX-E6-02, EX-E6-03) não são demonstrados no protótipo. Corrigido: frase suavizada para “principais estados desta tela”, com nota explícita de que EX-E6-02/EX-E6-03 não são demonstrados.
- A descrição do endpoint
GET .../clinic-selection(02 - Especificação) não deixava explícito queambientes[], e não apenasclinicas[], também segue a ordenação de RN-CIU-031 item 4. Corrigido: frase adicionada explicitando que o critério de ordenação se aplica tanto entregrupos[](Cliente) quanto entre os itens deambientes[]dentro de um mesmo grupo.