You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(007): tres bugs que so apareciam com o dedo, a tela estreita e a venda seguinte (AD-192)
Revisao da 007 logo apos a implementacao, com conferencia no navegador real
em 390px. Os tres primeiros achados sao bugs de comportamento que a propria
feature introduziu.
1. O campo de desconto de capa reabria **vazio** com um desconto valendo. O
controle so vive na etapa 2, entao ir a revisao e voltar (ou cruzar o
breakpoint) o remontava -- e como o `onBlur` trata campo vazio como "desisti
do ajuste", bastava tabular por ele para o desconto sumir sem que ninguem o
tivesse removido. Nao e perda cosmetica: e dinheiro negociado com o cliente
na frente do caixa.
2. O wizard ficava preso na etapa 3 depois de finalizar. `data-model.md` §2
supunha que finalizar remontaria a arvore; nao remonta -- zera o carrinho e
chama `abrirSessaoDeVenda` na mesma arvore. O reinicio segue a identidade da
sessao (o `VENDA_INICIADA` que abre o historico), nunca o tamanho dele, que
jogaria o operador a etapa 1 a cada bipagem.
3. A camera do scanner reiniciava sozinha no meio da mira: `onCodigoLido`
estava nas dependencias do efeito e o slot cria uma funcao nova a cada render
do pai. Lida por `ref` agora. Junto, a guarda que faltava depois do `await`
do `detect` -- fechar a janela cancela o proximo frame, nunca a promessa em
voo, e um codigo lido apos o gesto de sair virava item numa tela que ja nao
estava la.
Renderizacao: `flex-wrap` sozinho nao quebrava nada (toda celula era
`flex-1 min-w-0`, e quem encolhe ate zero nunca "nao cabe") -- o piso por
celula e o que faz a quebra; a notificacao do goey-toast saia pela direita da
tela e a primeira correcao empatava em especificidade com o pacote, que
carrega depois (medido: caixa em 358px, titulo em 801px indo ate 827);
o cabecalho mobile mostrava o titulo do desktop e virava "Centrium ...",
perdendo as duas informacoes -- o Pencil escreve so "Centrium Checkout" no no
`YXaRZ`. Mais alvos de toque de 40-44px abaixo de `md`, total da lista em
`font-mono` e o cabecalho do card de cliente quebrando em linhas.
Fecha o item 47 de PENDENCIES.md de verdade: o gate da 014 em viewport
compacta ganhou o par recusado/aceito -- sem o aceito, "nenhum pagamento
aplicado" passaria tambem se o gate nao fosse consultado no mobile.
Verificacao: tsc e eslint limpos; 1097 testes unit/integracao e 173 E2E;
toast e cabecalho reconferidos no navegador em 390px, zero estouro horizontal
medido em toda a arvore.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Ki1CPfueZmf4M4t1s52Ps
Copy file name to clipboardExpand all lines: .specs/project/STATE.md
+23-1Lines changed: 23 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2520,4 +2520,26 @@ Com isso, `erro-recuperavel` passa a ter dois desfechos: **com** marca, `ErrorRe
2520
2520
2521
2521
**`FR-005` (sem atalhos no mobile) já estava cumprido pela 013**, e por ausência estrutural: `projetarAtalhos` recebe a plataforma e devolve lista vazia no compacto, então `DicaAtalhos` não registra tecla alguma no mapa central. A 007 acrescentou a verificação automatizada disso (`appShell.spec.tsx`), que antes não existia.
2522
2522
2523
-
**Impact:** criados — `src/client/domain/layout/{classificarLayout,suportaScannerCamera}.ts`, `src/client/layout/{useIsMobile,obterPlataforma,AppShell}.ts(x)`, `src/client/layout/desktop/DesktopLayout.tsx`, `src/client/layout/mobile/{MobileWizard,EtapaClienteProdutos,EtapaPagamento,EtapaRevisao,ScannerCamera}.tsx`, `src/client/features/pagamento/ConfiguracaoPagamento.tsx`; alterados — `src/client/App.tsx` (perde `TelaDeVenda`, renderiza `AppShell`), `src/client/layout/usePlataforma.ts`, `src/client/features/pagamento/PainelPagamentoETotais.tsx`, `src/client/features/finalizacao-suspensao/AcoesFinaisVenda.tsx`, `src/client/features/carrinho/EntradaRapidaProduto.tsx` (slot `renderizarCaptura` e `flex-wrap` na linha de prévia); testes — 3 unitários de domínio, 6 de integração (`appShell`, `mobileWizard`, `pagamentoMobile`, `scannerCamera`, `ausenciaEstrutural`, `semDuplicacaoRegra`), 4 E2E (`layout-desktop`, `layout-mobile`, `layout-responsivo`, `layout-scanner`) e o helper `tests/support/layout.tsx`. Verificação: `tsc --noEmit` limpo; 1064 testes unit/integração e 171 E2E passando.
2523
+
### AD-192: a 007 quebrou três coisas que só apareciam com o dedo, a tela estreita e a venda seguinte (rodada de revisão, 2026-09-09)
2524
+
2525
+
**Origem:** revisão da 007 logo após a implementação — cobertura de comportamento e conferência visual em 390px no navegador real. Os três primeiros achados são **bugs de comportamento** que a feature introduziu; os demais são de renderização.
2526
+
2527
+
**1. O campo de desconto de capa reabria vazio com um desconto valendo — e tabular por ele o apagava.** `ControleDescontoCapa` nasceu numa tela que nunca desmontava. A 007 criou dois caminhos de remonte que não existiam: ir à revisão e voltar (o controle só vive na etapa 2) e cruzar o breakpoint. Em ambos o campo reabria em branco enquanto o desconto seguia aplicado na venda — e, como o `onBlur` trata campo vazio como "desisti do ajuste", bastava o operador passar por ele com TAB para o desconto sumir sem que ninguém o tivesse removido. O TSDoc do `modo` já dizia a regra ("um desconto já aplicado continua mandando"); faltava o número obedecê-la. **Não é perda cosmética: é dinheiro negociado com o cliente na frente do caixa.**
2528
+
2529
+
**2. O wizard ficava preso na etapa 3 depois de finalizar.** `data-model.md` §2 supunha que finalizar trocaria de tela e remontaria o wizard (I1). Não é o que acontece: `useFinalizarOuSuspenderVenda` zera o carrinho e chama `abrirSessaoDeVenda('NOVA')` **na mesma árvore montada**. Sem reinício explícito, o operador terminava a venda e continuava na "Revisão e finalização" de uma venda vazia, com o campo de código do próximo cliente uma etapa atrás. A identidade da sessão é o `VENDA_INICIADA` que abre o histórico — objeto novo a cada `resetarAuditoria` e o mesmo durante toda a venda; pendurar o reinício no **tamanho** do histórico jogaria o operador à etapa 1 a cada bipagem.
2530
+
2531
+
**3. A câmera do scanner reiniciava sozinha no meio da mira.** `onCodigoLido` estava nas dependências do efeito que liga a câmera, e o slot `renderizarCaptura?.((codigo) => …)` cria uma função nova a cada render do pai. Qualquer re-render enquanto a janela estava aberta — uma query que assenta, um item que entra na lista — derrubava o efeito, encerrava a trilha de vídeo e chamava `getUserMedia` de novo. Resolvido lendo a callback por `ref`. Junto veio a guarda que faltava **depois** do `await` do `detect`: fechar a janela cancela o próximo `requestAnimationFrame`, nunca a promessa já em voo, e um código decodificado após o gesto de sair virava produto no carrinho de uma tela que não estava mais lá.
2532
+
2533
+
**4. `flex-wrap` sozinho não quebra linha nenhuma.** A correção original da barra de entrada rápida acrescentou `flex-wrap` e parou aí. Toda célula era `flex-1 min-w-0`, e um item que pode encolher até zero nunca chega a "não caber": as sete se espremiam na mesma linha de 326px, o campo de código ficava com 39px e quatro rótulos se sobrepunham. O que faz a quebra acontecer é o **piso por célula** (`min-w-*`, revogado em `md:`).
2534
+
2535
+
**5. A notificação saía pela direita da tela, e a primeira correção não pegou.** `goey-toast` monta a faixa compacta como `width: fit-content` com o título em `white-space: nowrap`: no desktop um aviso de 826px ainda sobra na tela de 1440; em 390px o operador lia "Esta venda já tem forma de pagamento aplicada e cada ven…". O teto de largura entrou na caixa de conteúdo (que é a que o pacote mede para desenhar a bolha) e funcionou. A quebra de linha do título **não**: o pacote declara exatamente `.gooey-header > .gooey-title`, a mesma especificidade (0,2,0) da nossa regra, e a folha dele carrega depois — num empate ganha quem vem por último. Medido no navegador: caixa em 358px, título ainda em 801px com a borda direita em 827px. Corrigido subindo para (0,3,0) com `.gooey-content` na frente, sem `!important` — que se reservaria para uma briga contra `!important`, e não é o caso.
2536
+
2537
+
**6. O cabeçalho mobile mostrava o título do desktop.** Reusar `tituloDoProduto` acrescentava a empresa ("Centrium Checkout - Organizações Tabajara") num cabeçalho de 358px, e o resultado era "Centrium …" — perdendo **as duas** informações. O Pencil escreve só "Centrium Checkout" no nó `YXaRZ`; a empresa não está lá por decisão de desenho. `NOME_DO_PRODUTO` passou a ser exportado do domínio. Encolher a pílula do operador não bastava: as duas metades do cabeçalho encolhem juntas e o flex tira mais de quem é maior, então um `UsuarioNome` longo ficava inteiro enquanto o título cedia — daí o teto de largura no nome do operador, com `title` guardando o valor completo.
2538
+
2539
+
**Além disso:** alvos de toque de 28px (lápis/lixeira da lista mobile, barras do indicador de etapa) subiram para 40–44px só abaixo de `md`, o utilitário `cc-alvo-toque` cresce a área sensível sem mexer num pixel do desenho, o total da lista mobile passou a `font-mono tabular-nums` (regra de tipografia do projeto, estava em Inter), e o cabeçalho do card de cliente quebra em linhas no compacto — antes "CONSUMIDOR FINAL" virava "CO" e o vendedor sumia sob a palavra "Recolhido".
2540
+
2541
+
**O item 47 de `PENDENCIES.md` fechou de verdade:** o cenário de recusa do gate da 014 em viewport compacta existe agora em `tests/e2e/validacao-previa.spec.ts`, com o par aceito/recusado — sem o aceito, "nenhum pagamento aplicado" passaria também se o gate simplesmente não fosse consultado no compacto.
2542
+
2543
+
**Impact:** alterados — `src/client/features/pagamento/ControleDescontoCapa.tsx`, `src/client/layout/mobile/{MobileWizard,ScannerCamera}.tsx`, `src/client/features/carrinho/{EntradaRapidaProduto,ListaItensMobile}.tsx`, `src/client/features/cliente/CampoClienteVenda.tsx`, `src/client/domain/sessao/identidadePdv.ts` (`NOME_DO_PRODUTO` exportado), `src/client/styles/global.css` (`cc-alvo-toque` + as duas regras do toast); testes — `tests/integration/layoutPreservacaoEstado.spec.tsx` (novo, 7 casos, incluindo o TAB que apagava o desconto), acréscimos em `appShell`/`mobileWizard`/`scannerCamera`/`ausenciaEstrutural`/`semDuplicacaoRegra` e o bloco compacto de `tests/e2e/validacao-previa.spec.ts`. Verificação: `tsc --noEmit` e `eslint` limpos; 1097 testes unit/integração e 173 E2E passando; toast e cabeçalho reconferidos no navegador em 390px (título do aviso passou a 338px de largura, borda direita em 364 sobre 390 — dentro da tela), e zero estouro horizontal medido em toda a árvore.
2544
+
2545
+
**Impact (AD-191):** criados — `src/client/domain/layout/{classificarLayout,suportaScannerCamera}.ts`, `src/client/layout/{useIsMobile,obterPlataforma,AppShell}.ts(x)`, `src/client/layout/desktop/DesktopLayout.tsx`, `src/client/layout/mobile/{MobileWizard,EtapaClienteProdutos,EtapaPagamento,EtapaRevisao,ScannerCamera}.tsx`, `src/client/features/pagamento/ConfiguracaoPagamento.tsx`; alterados — `src/client/App.tsx` (perde `TelaDeVenda`, renderiza `AppShell`), `src/client/layout/usePlataforma.ts`, `src/client/features/pagamento/PainelPagamentoETotais.tsx`, `src/client/features/finalizacao-suspensao/AcoesFinaisVenda.tsx`, `src/client/features/carrinho/EntradaRapidaProduto.tsx` (slot `renderizarCaptura` e `flex-wrap` na linha de prévia); testes — 3 unitários de domínio, 6 de integração (`appShell`, `mobileWizard`, `pagamentoMobile`, `scannerCamera`, `ausenciaEstrutural`, `semDuplicacaoRegra`), 4 E2E (`layout-desktop`, `layout-mobile`, `layout-responsivo`, `layout-scanner`) e o helper `tests/support/layout.tsx`. Verificação: `tsc --noEmit` limpo; 1064 testes unit/integração e 171 E2E passando.
0 commit comments