0017 — Salvar em Parâmetros (mobile) trava os botões da tab bar do rodapé¶
| Status | CORRIGIDO |
| Branch | spec/0050-analise-credito-boavista (achado do usuário durante a spec 0050; corrigido na mesma branch por já estar aberta — não relacionado ao bureau de crédito) |
| Aberto em | 2026-09-05 |
| Severidade | MÉDIA |
| Apps afetados | web |
| Revisão do diagnóstico | não realizada — corrigido em fluxo de conversação, sem o portão formal do /fix |
| Revisão da correção | não realizada — mesmo motivo |
Este registro documenta uma correção que não passou pelo laço de revisão do
/fix(subagente de contexto limpo, rubrica, rodadas). Foi diagnosticada e corrigida dentro de uma sessão em curso da spec 0050, a pedido direto do usuário, com evidência reproduzida ao vivo contra o dev-remote e red-green manual — mas sem o portão. Registrado aqui para manter a convenção de numeração e o histórico, não como prova de que passou pela régua normal.
1. Sintoma¶
Relato do usuário: "Em parametrizações no mobile quando clico em salvar configurações trava os botões do rodape menu." — depois de salvar em Parâmetros (/settings) no mobile, os botões da tab bar (Painel · Análise de Crédito · Garantias · Mais) param de responder ao toque por alguns segundos, sem nenhum indício visual do motivo.
2. Reprodução¶
- Autenticado como
ADMIN_TENANT, viewport ≤ 599px (casca mobile). - Abrir a gaveta (aba "Mais") → Parâmetros.
- Tocar em "Salvar configurações".
- Imediatamente depois, tentar tocar em qualquer aba da tab bar (ex.: "Garantias") — o toque não navega, pelos ~3 segundos em que o aviso "Configurações salvas com sucesso" está na tela.
Reproduzido contra o dev-remote (https://dev.garantora.com.br/app), com um script Playwright ad hoc (não versionado) que fez login como gestor@e2e.test e mediu, via document.elementFromPoint, qual elemento realmente recebe o toque na região da tab bar no instante em que o aviso está visível.
3. Esperado × Atual¶
- Esperado: a tab bar do rodapé continua respondendo ao toque a qualquer momento, inclusive com um aviso de sucesso na tela.
- Atual: por ~3s depois de qualquer salvamento bem-sucedido em Parâmetros, o toque na tab bar é interceptado pelo próprio
Snackbarde confirmação.
4. Causa raiz¶
- Onde:
apps/web/src/pages/settings/index.tsx— o<Snackbar open={success} .../>(fim do arquivo) não tinhaanchorOriginnemsxde posição, usando o padrão do MUI ({ vertical: "bottom", horizontal: "left" }, que no breakpointxsestica para quase toda a largura, a 8px do fundo da viewport). - O mecanismo: o
Snackbaré renderizado por portal, direto emdocument.body— fora da árvore de layout da casca (components/mobile-shell/index.tsx) —, composition: fixed,z-index: 1400(padrão MUI) epointer-events: auto. A tab bar da casca (data-testid="mobile-tabbar",TABBAR_H = 62px) ocupa exatamente a mesma faixa vertical do fundo da viewport onde o Snackbar aparece por padrão — medido no dev-remote em 375×812: tab bar emtop: 750, bottom: 812; Snackbar emtop: 754.5, bottom: 804, quase 100% sobreposto. Como o Snackbar temz-indexmuito mais alto que a tab bar (que não define nenhum) epointer-events: auto, ele fica acima na pilha de empilhamento e intercepta o toque — confirmado comdocument.elementFromPointem cinco pontos ao longo da largura da tab bar: todos resolviam para elementos do Snackbar (MuiSnackbarContent-message/-root), nenhum para a tab bar. Nada quebra visualmente porque o texto do aviso, sobre o fundo escuro da tab bar, é pouco contrastante — daí parecer que os botões simplesmente pararam de responder, sem indicar por quê. - O alcance: o mesmo padrão (
<Snackbar>sem deslocamento vertical seguro para mobile) existia em mais dois lugares —apps/web/src/pages/my-garantidora/index.tsxeapps/web/src/pages/settings/credit-analysis-documents/index.tsx. Os dois já tinhamanchorOrigin={{ horizontal: "center" }}, o que evita a sobreposição horizontal (cobrir a largura toda) mas não a vertical — o Snackbar continua na mesma faixa de 62px do fundo, agora só centralizado, cobrindo a aba que calhar de estar no meio. Verificado ao vivo (mesma técnica) emmy-garantidora: antes da correção, o Snackbar ficava emtop: 754.5, dentro da faixa da tab bar; depois, emtop: 692, bottom: 742, acima dela.credit-analysis-documentsrecebeu a mesma correção por analogia (mesmo código, mesma causa) — não verificado ao vivo por não ter sido priorizado no tempo disponível.
5. Revisão do diagnóstico¶
Não realizada — ver nota no cabeçalho.
6. Correção¶
apps/web¶
components/mobile-shell/index.tsx—TABBAR_H(já existia, só uso interno) passou aexport const, com docstring explicando o novo consumidor.pages/settings/index.tsx,pages/my-garantidora/index.tsx,pages/settings/credit-analysis-documents/index.tsx— cada<Snackbar>ganhousx={{ bottom: { xs: \${TABBAR_H + 8}px` } }}, somando a altura da tab bar ao deslocamento só até o breakpointxs(abaixo desm`) — acima disso o app usa a sider desktop e não há tab bar para desviar, então o comportamento padrão do MUI (24px do fundo) continua valendo sem alteração.- Por que não uma trava de
pointer-eventsouz-indexmenor no Snackbar, em vez de movê-lo: deixaria o toque passar, mas o aviso continuaria visualmente encostado (e parcialmente ilegível) sobre a tab bar — o problema visual sobreviveria, só o funcional sumiria. Mover o Snackbar acima da tab bar resolve os dois.
apps/e2e¶
- Spec novo:
tests/e2e/settings-mobile-snackbar.spec.ts, dois casos — (1) no instante em que o aviso aparece,document.elementFromPointsobre o centro da aba "Garantias" resolve para a própria aba, não para o Snackbar (teste determinístico: não usa.click(), que tem retentativa e mascararia o defeito depois que o Snackbar some sozinho); (2) o retângulo do Snackbar não invade verticalmente o da tab bar. Cobre sópages/settings/index.tsx— os outros dois pontos não ganharam spec próprio.
7. Risco / impacto colateral¶
- Nenhum — é reposicionamento puro de um elemento sem estado nem dado; não toca contrato de API, permissão nem cálculo de negócio.
credit-analysis-documents/index.tsxnão foi verificado ao vivo (só por analogia de código) — risco baixo, mesmo padrão exato dos outros dois, mas fica registrado como lacuna.
8. Critérios de aceite¶
- [x] CA1. Depois de salvar em Parâmetros no mobile, a tab bar do rodapé continua clicável enquanto o aviso de sucesso está na tela. (
settings-mobile-snackbar.spec.ts, caso 1) - [x] CA2. O retângulo do Snackbar não se sobrepõe ao da tab bar. (mesma spec, caso 2)
- [x] CA3. O mesmo defeito, achado por inspeção de código nos outros dois usos de
<Snackbar>do app, recebeu a mesma correção. (my-garantidoraverificado ao vivo;credit-analysis-documentspor analogia — ver seção 7)
9. Plano¶
- [x] Reproduzir o defeito contra o dev-remote com evidência de geometria/hit-test, não só relato
- [x] Escrever o teste e2e e vê-lo falhar contra o build com o defeito
- [x] Corrigir a causa raiz nos três arquivos afetados
- [x] Red-green verificado: revertido
pages/settings/index.tsx→ rebuild → os dois casos do spec novo falham (um comnullno lugar demobile-tab-garantias, outro com804 > 750) → restaurado → rebuild → os dois voltam a passar - [x]
tsc --noEmitlimpo depois da correção - [x]
make test-web(suíte inteira) — 214/214 verdes, sem regressão
10. Decisões em aberto¶
Nenhuma — a correção não exigiu nenhuma decisão de negócio, só técnica.
11. Revisão da correção¶
Não realizada — ver nota no cabeçalho.
O teste sobreviveria à reversão? Sim, confirmado por red-green de verdade (seção 9), contra o dev-remote real, não em ambiente isolado.
12. Registro¶
O que era: o Snackbar de confirmação de "Salvar configurações" (e dois outros, my-garantidora e credit-analysis-documents) usava posicionamento padrão do MUI, que no mobile cai exatamente na faixa de 62px que a tab bar da casca ocupa no fundo da tela. Por ter z-index mais alto e renderizar via portal fora da árvore de layout do app, o Snackbar intercepta todo toque destinado à tab bar enquanto está visível (~3-4s), sem indicar visualmente o motivo — parece que os botões "travaram".
O que virou: TABBAR_H (constante já existente em mobile-shell/index.tsx) exportada; os três Snackbar do app ganharam sx={{ bottom: { xs: \${TABBAR_H + 8}px` } }}`, subindo acima da tab bar só no breakpoint mobile.
Como foi achado e confirmado: a pedido do usuário, reproduzido com um script Playwright ad hoc contra o dev-remote (login como gestor@e2e.test, usuário de e2e já seedado — app.db.seed_e2e, idempotente), medindo com document.elementFromPoint/getBoundingClientRect que o Snackbar de fato ocupa a mesma região da tab bar e intercepta o toque ali. Depois da correção, a mesma medição confirmou o Snackbar acima da tab bar, sem sobreposição, em dois dos três pontos afetados (settings, my-garantidora); o terceiro (credit-analysis-documents) recebeu a mesma correção por analogia, sem verificação ao vivo separada.
Não passou pelo portão do /fix: diagnosticado e corrigido dentro do fluxo de conversação da spec 0050 (branch já aberta), a pedido direto do usuário — sem o laço de revisão de subagente de contexto limpo que o /fix implement normalmente exige. Marcado explicitamente no cabeçalho para não passar como se tivesse sido revisado.
Efeito colateral do diagnóstico: a checagem por .click() do Playwright no caso 1 do spec novo teve que ser trocada por um hit-test direto (elementFromPoint) porque o .click() tem retentativa automática — como o Snackbar some sozinho em ~3s, um clique comum eventualmente funciona mesmo com o defeito presente (confirmado: a primeira versão do teste passou tanto antes quanto depois da correção, o que não prova nada).