# Épico 5 — Menu Lateral — Especificação (v1.3)

**Documentos deste épico:** [01 - Definição](./01%20-%20Defini%C3%A7%C3%A3o%20v1.5.md) (v1.5) · [02 - Especificação](./02%20-%20Especifica%C3%A7%C3%A3o%20v1.3.md) (v1.3) · [03 - Protótipo](./03%20-%20Prot%C3%B3tipo%20v1.2.md) (v1.2, código executável em `03 - Protótipo v1.2.html`) · [04 - Mapeamento de Banco de Dados](./04%20-%20Mapeamento%20de%20Banco%20de%20Dados%20v1.4.md) (v1.4). Este épico faz parte do documento guarda-chuva [Check-in de Usuários](../00%20-%20Guarda-chuva%20v2.18.md).

---

## SEÇÃO 2 — ESPECIFICAÇÃO

### Histórias de Usuário

- **US-CIU-010:** Como usuário autenticado, quero navegar entre os módulos que meu perfil de acesso libera a partir de um único menu lateral, para encontrar rapidamente as funcionalidades da minha rotina sem me perder em opções que não uso.

### Descrição Funcional Detalhada

#### Cabeçalho do Menu (identificação da clínica)

No topo do menu, antes dos grupos, um indicador visual (círculo colorido) e o nome da clínica em que o usuário está trabalhando nesta sessão (E1, E6) — ex.: "Clínica Onco". No mobile, o mesmo nome aparece na barra superior fixa, sem o indicador visual. Quando o menu está recolhido no desktop (ver "Comportamento Responsivo"), só o indicador visual permanece; o nome é ocultado junto com o restante do texto do menu recolhido.

Motivador: dar ao usuário uma referência constante de em qual clínica está trabalhando, especialmente relevante para quem atua em mais de uma (RN-CIU-009, em CIU-E1) — sem essa identificação, trocar de clínica (ver "Botão Trocar" abaixo) não teria uma confirmação visual de que a troca realmente ocorreu.

#### Estrutura do Menu

Ao carregar, o menu busca a árvore de grupos e itens já filtrada pelo perfil de acesso do usuário na clínica atual (RN-CIU-027). Cada grupo é exibido com seu nome e a lista de itens que contém, na ordem definida para aquele perfil de acesso; itens sem grupo aparecem soltos, na lista principal, junto dos grupos. Um grupo começa recolhido, exibindo apenas seu nome (ver "Premissas", 01 - Definição), e pode ser expandido/recolhido individualmente ao ser tocado, sem afetar os demais grupos (RN-CIU-026). Cada item exibe um ícone e o nome do módulo; ao ser selecionado, navega para a rota daquele item.

Motivador: a filtragem no carregamento (RN-CIU-027), em vez de itens desabilitados na tela, evita qualquer ambiguidade sobre o que o usuário pode ou não acessar — o item simplesmente não existe ali para ele.

#### Seção Favoritos

Exibida no topo do menu, acima dos grupos, apenas quando o usuário tem ao menos um item favoritado nesta clínica (RN-CIU-028) — sem favoritos, a seção não é renderizada. Cada item favoritado aparece com o mesmo ícone e nome de sua entrada original, e um indicador de estrela preenchida; o mesmo indicador aparece junto ao item em seu grupo de origem, permitindo favoritar/desfavoritar a partir de qualquer um dos dois lugares. Os itens da seção Favoritos são listados em ordem alfabética pelo nome (RN-CIU-028). Tocar na estrela de um item não favoritado o adiciona à seção Favoritos (CA-010.2); tocar na estrela preenchida de um item já favoritado o remove da seção, sem afetar sua presença no grupo de origem (CA-010.3).

Quando o menu está recolhido no desktop (ver "Comportamento Responsivo"), apenas os ícones dos itens desta seção permanecem visíveis.

Motivador: separar Favoritos da árvore normal, em vez de reordenar os itens dentro dos próprios grupos, evita que o usuário perca a referência de onde cada módulo mora na estrutura fixa do sistema (RN-CIU-028, Seção 1).

#### Busca

Campo de busca no topo do menu, abaixo da seção Favoritos (quando presente). A cada caractere digitado, filtra a lista de itens pelo nome, em tempo real, sem exigir confirmação (RN-CIU-030). Durante uma busca com texto, os grupos são ignorados como unidade de exibição — a lista mostra diretamente os itens cujo nome corresponde ao texto buscado, independentemente do grupo a que pertencem estarem expandidos ou recolhidos (CA-010.4). Limpar o campo de busca restaura a exibição normal, com os grupos no estado de expansão em que estavam antes da busca. Quando nenhum item corresponde ao texto buscado, é exibido um estado vazio (EX-E5-02).

Motivador: a busca é uma ferramenta de atalho para quem já sabe o nome do que procura — achatar a árvore evita a etapa extra de abrir manualmente o grupo certo.

#### Comportamento Responsivo

No desktop (largura suficiente para o menu ao lado do conteúdo), o menu tem dois estados: expandido (padrão) e recolhido, alternados por um controle posicionado fora do próprio menu, numa posição fixa que não se desloca entre os dois estados — evita que o usuário precise localizar o controle em lugares diferentes da tela conforme o menu muda de largura. Expandido, ocupa uma faixa lateral fixa com grupos e itens por extenso; o conteúdo principal ocupa o espaço restante. Recolhido, mostra apenas os ícones dos itens da seção Favoritos, em coluna estreita; o conteúdo principal se redimensiona para ocupar o espaço liberado (CA-010.7). Um ícone do menu recolhido navega direto para o item ao ser clicado — não é necessário expandir o menu antes.

No mobile (largura insuficiente para o menu ao lado do conteúdo), o menu fica oculto por padrão; um ícone no cabeçalho da tela o abre como uma camada sobreposta ao conteúdo principal, sem redimensionar nada por baixo (CA-010.8). Selecionar um item ou tocar fora da camada fecha o menu, revelando o conteúdo como estava.

Motivador: RN-CIU-029, Seção 1 — os dois comportamentos (redimensionar vs. sobrepor) são deliberadamente opostos, cada um adequado ao espaço disponível na respectiva largura de tela.

#### Botão Trocar (clínica)

Ícone discreto (sem rótulo de texto, só título/rótulo acessível) posicionado no cabeçalho do menu, ao lado do nome da clínica — não um botão largo separado. Exibido apenas quando o usuário tem acesso a mais de uma clínica (RN-CIU-009, em CIU-E1) — com exatamente 1 clínica acessível, o ícone não aparece (CA-010.5). Ao ser tocado, leva o usuário à tela de seleção de clínica (E6, RN-CIU-025), onde pode escolher outra clínica de trabalho (CA-010.6). Trocar de clínica reconstrói o menu inteiro (itens, favoritos e a identificação da clínica no cabeçalho) a partir do perfil de acesso da nova clínica (RN-CIU-028, Seção 1) — não há tentativa de preservar o estado da clínica anterior. Some junto com o resto do texto do cabeçalho quando o menu está recolhido no desktop (ver "Comportamento Responsivo").

Motivador: ocultar o ícone para quem só tem uma clínica evita oferecer uma ação sem efeito útil; reconstruir o menu do zero ao trocar reflete o fato de que itens e favoritos são específicos da combinação vínculo × perfil × clínica (RN-CIU-028). Posição discreta junto ao nome da clínica, em vez de um botão de destaque separado, reflete que trocar de clínica é uma ação pouco frequente para a maioria dos usuários — não deve competir visualmente com a navegação principal do menu.

#### Rodapé

Faixa fixa na base do menu com o texto "Powered by Interprocess TI" (MSG-T5-01) — rótulo estático, sem link nem ação associada.

### Critérios de Aceitação

**CA-010.1**: Dado um usuário autenticado com um perfil de acesso definido, quando o sistema monta o menu, então só aparecem os itens permitidos por esse perfil (RN-CIU-027).

**CA-010.2**: Dado um item não favoritado, quando o usuário toca na estrela ao lado dele, então o item passa a aparecer na seção Favoritos.

**CA-010.3**: Dado um item na seção Favoritos, quando o usuário toca na estrela preenchida, então o item some dos Favoritos (mas continua na árvore, desfavoritado).

**CA-010.4**: Dado o usuário digitando no campo de busca, quando o texto corresponde a um ou mais itens permitidos, então só esses itens aparecem, independentemente do estado de expansão dos grupos.

**CA-010.5**: Dado um usuário com acesso a exatamente 1 clínica, quando o menu é exibido, então o botão "Trocar" não aparece.

**CA-010.6**: Dado um usuário com acesso a mais de 1 clínica, quando toca em "Trocar", então é redirecionado à tela de seleção de clínica (E6).

**CA-010.7**: Dado o menu expandido no desktop, quando o usuário o recolhe, então o conteúdo principal se redimensiona para ocupar o espaço liberado, e só os ícones dos favoritos permanecem visíveis.

**CA-010.8**: Dado o mobile, quando o usuário aciona o ícone de abertura, então o menu aparece sobre o conteúdo, sem redimensioná-lo.

**CA-010.9**: Dado um grupo recolhido, quando o usuário toca nele, então ele expande mostrando seus itens, sem afetar o estado dos demais grupos.

### Fluxos de Exceção

**EX-E5-01 — Perfil de acesso sem nenhum item de menu liberado**

Gatilho: perfil de acesso do usuário, na clínica atual, não libera nenhum item para exibição no menu (RN-CIU-027). Comportamento: menu exibido vazio, com mensagem informativa orientando a contatar o administrador da clínica (MSG-I5-01). Recuperação: usuário aguarda o administrador configurar seu perfil de acesso. Nota: cenário de baixa probabilidade nesta versão do sistema — a Central do Médico é hoje o único módulo com item de menu real cadastrado, e todo médico deveria ter perfil de acesso a ele — mas o comportamento vale para qualquer perfil, presente ou futuro, que eventualmente fique sem nenhum item liberado.

**EX-E5-02 — Busca sem nenhum resultado**

Gatilho: texto digitado no campo de busca não corresponde a nenhum item permitido pelo perfil de acesso. Comportamento: estado vazio no lugar da lista, com mensagem orientando a limpar o filtro (MSG-I5-02). Recuperação: usuário apaga ou ajusta o texto buscado.

**EX-E5-03 — Falha ao favoritar/desfavoritar um item**

Gatilho: erro de rede ou falha do back-end ao processar a ação de favoritar ou desfavoritar um item. Comportamento: o estado visual da estrela é revertido ao que era antes do toque, e uma mensagem de erro é exibida (MSG-E5-01). Recuperação: usuário tenta novamente.

**EX-E5-04 — Falha ao carregar os itens do menu no login**

Gatilho: erro de rede ou falha do back-end ao buscar a árvore de itens do menu para o usuário autenticado. Comportamento: estado de erro no lugar do menu, com mensagem (MSG-E5-03) e ação "Tentar novamente" (MSG-B5-01) — sem menu carregado, o usuário não tem como navegar para lugar nenhum. Recuperação: usuário aciona "Tentar novamente"; se persistir, orientação a contatar o suporte.

### Validações de Campos

O único campo de entrada desta funcionalidade é o campo de busca — texto livre, sem obrigatoriedade, sem validação de formato: qualquer texto digitado (incluindo vazio, que restaura a exibição normal) é aceito e usado para filtrar a lista de itens.

### Mapeamento de Componentes de Interface

|Componente|Variante|Estados|Tokens aplicados|Uso nesta tela|
|---|---|---|---|---|
|Painel lateral|Menu (desktop expandido)|default|`--color-base-pure` bg, `--shadow-level-medium`, largura fixa|Estrutura principal do menu, desktop|
|Painel lateral|Menu (desktop recolhido)|default|`--color-base-pure` bg, largura reduzida a ícones|Estado recolhido, só ícones de favoritos|
|Painel overlay|Menu (mobile)|default, entrando/saindo|`--shadow-level-heavy`, sobreposto ao conteúdo com camada de fundo semitransparente|Estado aberto do menu em mobile|
|Item de grupo (accordion)|Grupo|default, expandido, recolhido|`--spacing-sm` padding, `--color-neutral-pure` texto de rótulo, ícone de chevron rotativo|Cada grupo do menu (RN-CIU-026)|
|Item de menu|Padrão / Favorito|default, hover, active (rota atual), focus|`--radius-m`, `--color-primary-pure` no item ativo, `--spacing-xs` entre ícone e texto|Cada item dentro de um grupo, da lista sem grupo ou da seção Favoritos|
|Ícone de estrela|Favoritar|vazia (não favoritado), preenchida (favoritado), loading|`--color-highlight-pure` (preenchida), `--color-neutral-light` (vazia)|Ação de favoritar/desfavoritar, junto de cada item|
|Input de busca|Busca do menu|default, focus, com texto|`--input-height` reduzido (32px), `--radius-pill`, `--color-neutral-lighter` bg|Campo de busca, topo do menu (RN-CIU-030)|
|Indicador + texto|Marca/Identificação da clínica|default|`--color-primary-pure` (indicador), `--font-size-lg` (nome)|Cabeçalho do menu — nome da clínica atual, oculto (só o indicador permanece) no menu recolhido|
|Button (icon-only)|Ghost (Trocar)|default, hover|`--icon-btn` 36px, sem borda|Ícone "Trocar" no cabeçalho do menu, ao lado do nome da clínica, quando visível (CA-010.5/6)|
|Button (icon-only)|Ghost (Recolher/expandir)|default, hover, pressed|`--icon-btn` 36px, borda `--color-neutral-lighter`, `--shadow-level-medium`|Controle de recolher/expandir o menu, fora do menu, em posição fixa (desktop, CA-010.7)|
|Banner/Ícone informativo|Info|default|`--color-secondary-pure`|Mensagem de perfil sem itens (MSG-I5-01) e de busca sem resultado (MSG-I5-02)|
|Toast|Error|default|`--color-error-pure`|Falha ao favoritar (MSG-E5-01)|
|Estado de erro em tela cheia|Erro de carregamento|default|`--color-error-light` bg do ícone, `--color-error-dark` texto do ícone|Falha ao carregar o menu (EX-E5-04)|
|Skeleton|Menu|loading|`--color-neutral-lighter`|Carregamento inicial do menu|
|Rodapé|Marca|default|`--font-size-xs`, `--color-neutral-pure`|Texto estático "Powered by Interprocess TI" (MSG-T5-01)|

### Integração com Backend

**GET /api/v1/menu**
Função: retorna, para o usuário autenticado, a árvore de grupos e itens de menu já filtrada pelo perfil de acesso vigente na clínica atual, com a marcação de quais itens estão favoritados.
Request: nenhum parâmetro (usuário e clínica atual identificados pelo token de sessão, já resolvido em E1/E6).
Response: `{ clinicaAtual: { clienteEmpresaId, nome, caminhoLogo }, grupos: [{ processoGrupoId, nome, itens: [{ clientePerfilAcessoProcessoId, processoId, nome, icone, rota, favorito }] }], itensSemGrupo: [{ clientePerfilAcessoProcessoId, processoId, nome, icone, rota, favorito }], favoritos: [{ clientePerfilAcessoProcessoId, processoId, nome, icone, rota }] }` — os itens já vêm na ordem definida para o perfil de acesso (RN-CIU-027); `favoritos` é a lista já pronta para a seção Favoritos e para o menu recolhido, evitando que o front-end precise recalculá-la a partir dos grupos. A quantidade de clínicas acessíveis, que decide a visibilidade do ícone Trocar (CA-010.5/6), não é recalculada aqui — reaproveita o campo `clinicasAcessiveis` já retornado pelo login (E1). **[PROPOSTA]** `clinicaAtual` é campo novo nesta versão, para o cabeçalho do menu (nome/logo da clínica atual) sobreviver a um recarregamento de página sem depender de o front-end reter o dado desde o login/E6 — mesmos campos (`nome`, `caminhoLogo`) já usados em `ClienteEmpresa` por E3/E6; nenhuma tabela nova, `ClienteEmpresa` já é uma das tabelas consultadas por este endpoint (ver Seção 4).
Códigos: 200 (sucesso — `grupos`, `itensSemGrupo` e `favoritos` podem vir vazios); 401 (não autenticado); 500 (falha ao carregar — EX-E5-04).
Tabelas envolvidas: Processo, ProcessoGrupo, ClienteProcesso, ClientePerfilAcesso, ClientePerfilAcessoUsuario, ClientePerfilAcessoProcesso, ClienteUsuarioMenuFavorito, ClienteEmpresa (IpSeguranca).

**POST /api/v1/menu/items/{clientePerfilAcessoProcessoId}/favorite**
Função: favorita um item do menu para o usuário autenticado, na clínica atual.
Request: nenhum corpo — `clientePerfilAcessoProcessoId` no path identifica o item já filtrado pelo perfil de acesso vigente.
Response: `{ clientePerfilAcessoProcessoId, favorito: true }`.
Códigos: 201 (favoritado); 404 (item não encontrado ou não liberado pelo perfil de acesso atual); 500 (falha — EX-E5-03).
Tabelas envolvidas: ClienteUsuarioMenuFavorito (insere o par ClientePerfilAcessoUsuarioId/ClientePerfilAcessoProcessoId — IpSeguranca).

**DELETE /api/v1/menu/items/{clientePerfilAcessoProcessoId}/favorite**
Função: desfavorita um item do menu para o usuário autenticado, na clínica atual.
Request: nenhum corpo — `clientePerfilAcessoProcessoId` no path.
Response: `{ clientePerfilAcessoProcessoId, favorito: false }`.
Códigos: 200 (desfavoritado); 404 (item não estava favoritado); 500 (falha — EX-E5-03).
Tabelas envolvidas: ClienteUsuarioMenuFavorito (remove o par — IpSeguranca).

Nota: a busca (RN-CIU-030) não tem endpoint próprio — opera em memória, no front-end, sobre a árvore já retornada por `GET /api/v1/menu`, filtrando por nome a cada caractere digitado. Isso evita uma chamada de rede a cada tecla, num campo cujo próprio propósito é agilizar a navegação.

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

**Erros:**

|ID|Contexto|pt-BR|en-US|es-419|
|---|---|---|---|---|
|MSG-E5-01|Falha ao favoritar/desfavoritar um item (EX-E5-03)|"Não foi possível atualizar seus favoritos. Tente novamente."|"Could not update your favorites. Please try again."|"No fue posible actualizar sus favoritos. Inténtelo de nuevo."|
|MSG-E5-03|Falha ao carregar os itens do menu (EX-E5-04)|"Não foi possível carregar o menu. Tente novamente."|"Could not load the menu. Please try again."|"No fue posible cargar el menú. Inténtelo de nuevo."|

**Informativas / estado vazio:**

|ID|Contexto|pt-BR|en-US|es-419|
|---|---|---|---|---|
|MSG-I5-01|Perfil de acesso sem nenhum item liberado (EX-E5-01)|"Seu perfil de acesso ainda não libera nenhum item de menu. Entre em contato com o administrador da sua clínica."|"Your access profile doesn't release any menu item yet. Contact your clinic's administrator."|"Su perfil de acceso aún no libera ningún elemento de menú. Comuníquese con el administrador de su clínica."|
|MSG-I5-02|Busca sem nenhum resultado (EX-E5-02)|"Nenhum item encontrado. Tente limpar o filtro de busca."|"No items found. Try clearing the search filter."|"No se encontraron elementos. Intente borrar el filtro de búsqueda."|

**Placeholder:**

|ID|Contexto|pt-BR|en-US|es-419|
|---|---|---|---|---|
|MSG-P5-01|Campo de busca do menu|"Buscar no menu"|"Search menu"|"Buscar en el menú"|

**Textos orientadores:**

|ID|Contexto|pt-BR|en-US|es-419|
|---|---|---|---|---|
|MSG-T5-01|Rodapé do menu (rótulo estático, sem tradução de marca)|"Powered by Interprocess TI"|"Powered by Interprocess TI"|"Powered by Interprocess TI"|

**Botões/links:**

|ID|Contexto|pt-BR|en-US|es-419|
|---|---|---|---|---|
|MSG-B5-01|Ação de recuperação (erro de carregamento do menu)|"Tentar novamente"|"Try again"|"Intentar de nuevo"|
|MSG-B5-02|Botão de troca de clínica no cabeçalho do menu|"Trocar"|"Switch"|"Cambiar"|

### Conformidade SBIS (detalhamento)

**ECF.17.18 — Idioma do S-RES** (estágio 1, obrigatório) — todos os rótulos, ícones textuais e mensagens do menu são exibidos em português do Brasil, com suporte adicional a en-US e es-419 (catálogo acima); o requisito cita "menus" explicitamente entre os elementos que devem seguir essa regra.

**ECF.17.19 — Mensagens do sistema** (estágio 1, obrigatório) — todas as mensagens do menu (erro, informativa, estado vazio, orientadora) estão catalogadas acima, em linguagem não técnica, sem termos de banco de dados ou de infraestrutura visíveis ao usuário.

**NGS1.03.01 — Impedir acesso por pessoas não autorizadas** (estágio 1, obrigatório) — o menu nunca exibe um item para o qual o usuário não tenha permissão: a filtragem por perfil de acesso (RN-CIU-027) ocorre no carregamento da árvore, no back-end, não como uma regra visual aplicada depois de uma lista completa chegar ao front-end.

**NGS1.03.03 — Gerenciamento de perfis** (estágio 1, obrigatório) — o menu consome, sem redefinir, a atribuição de permissões por perfil de acesso já gerenciada no Contexto de Segurança; cada alteração de quais itens um perfil libera se reflete no menu no próximo carregamento.

**NGS1.03.07 — Atribuição de mais de um perfil para um usuário** (estágio 1, obrigatório) — como um usuário pode ter um perfil de acesso diferente em cada clínica, o menu (itens e favoritos) é recarregado por completo a cada troca de clínica (RN-CIU-028), refletindo sempre o perfil vigente naquela clínica, nunca um acúmulo de permissões de clínicas diferentes.

---

## Histórico de Versões

- **v1.3** (15/09/2026) — pedido do solicitante ao revisar o protótipo: nova subseção "Cabeçalho do Menu (identificação da clínica)", documentando pela primeira vez o indicador + nome da clínica no topo do menu (antes só a marca do produto, sem essa informação — gap não documentado). "Botão Trocar" reescrito para a nova posição discreta, ao lado do nome da clínica. "Comportamento Responsivo" ganhou nota sobre o controle de recolher/expandir ficar fora do menu, em posição fixa. Mapeamento de Componentes atualizado (nova linha para a identificação da clínica, linha do Trocar revisada para ícone, nova linha para o controle de recolher/expandir, antes ausente da tabela). `GET /api/v1/menu` ganhou o campo `clinicaAtual` (**[PROPOSTA]**) na resposta, e `ClienteEmpresa` entra na lista de tabelas envolvidas do endpoint — ver Seção 4.
- **v1.2** (15/09/2026) — grupo passa a começar recolhido (não expandido) a cada nova sessão; seção Favoritos passa a listar os itens em ordem alfabética pelo nome — ambos confirmados pelo solicitante (ver 01 - Definição, Histórico de Versões, v1.2).
- **v1.1** (15/09/2026) — Terminologia revisada de "profissional" para "usuário", termo próprio do Contexto de Check-in de Usuários (ver 01 - Definição, Histórico de Versões, v1.1). Sem mudança de conteúdo funcional.
- **v1.0** (15/09/2026) — versão inicial do épico. Especificação construída a partir dos critérios de aceitação e fluxos de exceção já confirmados, e da estrutura de dados de menu/RBAC/favoritos localizada na Biblioteca de Schema (DDL) — nenhuma tabela nova, 3 endpoints (leitura do menu, favoritar, desfavoritar), busca resolvida inteiramente no front-end sobre a árvore já carregada.
