Skip to content

fix: botões sem resposta — remove v7_startTransition + scroll-lock proativo + guard 300ms#328

Merged
adm01-debug merged 3 commits into
mainfrom
fix/button-no-response-root-causes
May 25, 2026
Merged

fix: botões sem resposta — remove v7_startTransition + scroll-lock proativo + guard 300ms#328
adm01-debug merged 3 commits into
mainfrom
fix/button-no-response-root-causes

Conversation

@adm01-debug
Copy link
Copy Markdown
Owner

@adm01-debug adm01-debug commented May 25, 2026

Bug reportado

"Quando clico em um botão solicitando uma ação (alterar módulo ou executar uma ação dentro do módulo) nada acontece, mas se eu clicar no botão e logo em seguida dar um hard refresh o sistema realiza a ação."


Diagnóstico: 3 causas raiz sobrepostas

Bug #1 — PRINCIPAL: v7_startTransition: true abandona navegações (App.tsx)

v7_startTransition: true no BrowserRouter envolve todo navigate() em React.startTransition(), tornando navegações low-priority.

Sequência do problema:

  1. Usuário clica "alterar módulo" → navigate('/rota-x') é chamado
  2. React.startTransition() agenda a transição como baixa prioridade
  3. Simultaneamente: RootInteractivityGuard dispara setRecoveries() a cada 1500ms, Supabase Realtime publica eventos, outros re-renders ocorrem — todos alta prioridade
  4. React abandona a transição de navegação
  5. URL atualiza no window.history (pushState ocorre), mas o componente React não re-renderiza/monta
  6. Tela parece congelada — "nada acontece"
  7. Hard refresh carrega a URL já atualizada → módulo aparece normalmente

Fix: Remove v7_startTransition: true. O flag v7_relativeSplatPath: true é mantido.


Bug #2pointer-events: none preso no body (RouteScrollReset.tsx)

O próprio scroll-lock.ts documenta: "A well-known race can leave those styles stuck after the overlay closes/unmounts, which makes the entire UI unclickable."

O RootInteractivityGuard corrige isso, mas só checa a cada 1500ms. Quando Dialog/Dropdown/Sheet fecha com race condition, cliques são engolidos em silêncio por até 1,5 segundos.

Fix: RouteScrollReset agora chama releaseScrollLockIfIdle() em toda mudança de rota — proativo, sem esperar o watchdog. releaseScrollLockIfIdle() é no-op se houver overlay legítimo aberto.


Bug #3 — Intervalo do watchdog muito longo (RootInteractivityGuard.tsx)

Fix: Intervalo reduzido de 1500ms → 300ms. Overhead negligível (getComputedStyle em 3 elementos). Boot-time timeouts adensados: [0, 100, 300, 600, 1000].


Arquivos alterados

Arquivo Mudança
src/App.tsx Remove v7_startTransition: true do BrowserRouter
src/components/common/RouteScrollReset.tsx Adiciona releaseScrollLockIfIdle() em toda mudança de rota
src/components/system/RootInteractivityGuard.tsx Intervalo 1500ms → 300ms, boot timeouts adensados

Ação adicional recomendada (não neste PR)

Mutations que gravam no Supabase devem chamar queryClient.invalidateQueries() no onSuccess. Sem isso, a UI continua mostrando dados stale do cache (staleTime global = 10min) mesmo após a gravação ter sido bem-sucedida. Hard refresh força refetchOnMount: true e busca os dados atualizados.

Exemplo padrão:

const mutation = useMutation({
  mutationFn: salvarDados,
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['products'] });
  },
});

Summary by cubic

Corrige os “botões sem resposta” e navegações que falhavam silenciosamente. Cliques e trocas de rota agora aplicam de imediato, sem precisar de hard refresh.

  • Bug Fixes
    • Remove v7_startTransition no BrowserRouter (react-router-dom) para evitar navegações abandonadas; mantém v7_relativeSplatPath.
    • Libera o scroll-lock de forma proativa em cada mudança de rota via releaseScrollLockIfIdle() (impede pointer-events: none preso após overlays).
    • Reduz o intervalo do RootInteractivityGuard de 1500ms para 300ms e adensa os boot checks [0, 100, 300, 600, 1000] para recuperação mais rápida.

Written for commit 7298042. Summary will update on new commits. Review in cubic

Copilot AI review requested due to automatic review settings May 25, 2026 02:14
@vercel
Copy link
Copy Markdown

vercel Bot commented May 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
we-dream-big Ready Ready Preview, Comment May 25, 2026 2:15am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

Warning

Review limit reached

@adm01-debug, we couldn't start this review because you've used your available PR reviews for now.

Your plan includes 5 reviews of capacity. Refill in 31 minutes and 26 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6c79ead3-f047-48d0-973e-91a4d7a9bfce

📥 Commits

Reviewing files that changed from the base of the PR and between 5da2332 and 7298042.

📒 Files selected for processing (3)
  • src/App.tsx
  • src/components/common/RouteScrollReset.tsx
  • src/components/system/RootInteractivityGuard.tsx
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/button-no-response-root-causes

Comment @coderabbitai help to get the list of available commands and usage tips.

@supabase
Copy link
Copy Markdown

supabase Bot commented May 25, 2026

This pull request has been ignored for the connected project doufsxqlfjyuvxuezpln because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Este PR corrige um cenário em que cliques/navegações aparentavam “não responder” na SPA, eliminando uma causa de navegação abandonada por concorrência, liberando scroll-lock residual de forma proativa e reduzindo a janela de recuperação do watchdog de interatividade.

Changes:

  • Remove v7_startTransition: true do BrowserRouter para evitar navegações “low priority” abandonadas sob carga de re-renders concorrentes.
  • Em toda mudança de rota, chama releaseScrollLockIfIdle() para desfazer pointer-events: none preso em <html>/<body> quando não há overlay legítimo aberto.
  • Reduz o intervalo do RootInteractivityGuard de 1500ms para 300ms e adensa timeouts de boot.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
src/App.tsx Remove v7_startTransition do router e documenta o motivo do bug/fix.
src/components/common/RouteScrollReset.tsx Libera scroll-lock residual do Radix em mudanças de rota antes do scroll reset.
src/components/system/RootInteractivityGuard.tsx Acelera o watchdog e ajusta boot-time checks para reduzir janela de UI “travada”.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +25 to +28
* BUG FIX: intervalo reduzido de 1500ms -> 300ms para encurtar a janela de
* tempo em que a UI pode ficar travada entre dois ciclos do watchdog.
* O overhead e negligivel (apenas getComputedStyle em 3 elementos por ciclo).
* Boot-time timeouts tambem adensados: [0, 100, 300, 600, 1000].
Comment on lines +155 to +157
// BUG FIX: intervalo reduzido 1500ms -> 300ms. Boot-times adensados.
const timeouts = [0, 100, 300, 600, 1000].map((d) => window.setTimeout(() => check(true), d));
const interval = window.setInterval(() => check(true), 300);
Comment on lines +8 to +21
* Em navegacoes SPA (PUSH/REPLACE), rola a window suavemente ate o topo,
* para que o conteudo da nova rota seja exibido a partir do inicio.
*
* Regras:
* - POP (back/forward) preserva o scroll do navegador.
* - Se a URL contém hash âncora (#id), respeita o destino e não força topo.
* - Se a URL contem hash ancora (#id), respeita o destino e nao forca topo.
* - Honra `prefers-reduced-motion` (fallback para `behavior: "auto"`).
* - Skip no primeiro mount (evita interferir em deep-links com âncora).
* - Skip no primeiro mount (evita interferir em deep-links com ancora).
*
* BUG FIX: A cada mudanca de rota, libera proativamente qualquer scroll-lock
* residual do Radix UI (pointer-events: none preso em <html>/<body>). Isso
* previne o cenario em que um Dialog/Dropdown fecha com race condition e
* deixa a UI completamente nao-clicavel ate o watchdog de 300ms agir.
* releaseScrollLockIfIdle() e no-op se houver overlay legitimo aberto.
Comment thread src/App.tsx
Comment on lines +44 to +58
{/*
* BUG FIX: v7_startTransition REMOVIDO.
*
* v7_startTransition: true envolvia toda chamada navigate() em
* React.startTransition(), tornando navegacoes low-priority.
* Com rendering concorrente ativo (Supabase Realtime, intervals
* do RootInteractivityGuard, etc.), o React abandonava transicoes
* de navegacao — a URL atualizava no window.history mas o
* componente nao re-renderizava, dando a impressao de que o clique
* nao fez nada. Hard refresh carregava a URL ja atualizada e
* parecia "executar" a acao.
*
* v7_relativeSplatPath mantido — normaliza apenas matching de
* splat routes e nao afeta rendering concorrente.
*/}
@adm01-debug adm01-debug merged commit c4760af into main May 25, 2026
34 of 41 checks passed
@adm01-debug adm01-debug deleted the fix/button-no-response-root-causes branch May 25, 2026 02:19
adm01-debug added a commit that referenced this pull request May 25, 2026
…ntInput

main and this PR independently converged on a fix for the non-distributive
Omit over the BridgeStatusEvent union. main shipped first (PR #328 chain)
with a mapped-type `BridgeStatusEventInput`; this PR had an equivalent
`DistributiveOmit`. Adopting main's version keeps a single source of truth
and unblocks the merge.
adm01-debug added a commit that referenced this pull request May 25, 2026
* fix(supabase): realign frontend with live DB schema

Regenerate src/integrations/supabase/types.ts from the live database
(141->296 tables, 1->115 views, 65->385 functions) so the typed client
matches reality, then fix every drift the accurate types surfaced.

- Decode src/lib/supabase-untyped.ts and scripts/lint-untyped-from.sh,
  which were committed base64-encoded since #319 and broke compilation.
- Fix nullability drift across admin/security, dashboard, quotes and
  intelligence hooks (DB columns are nullable; code assumed non-null).
- Fix real runtime bugs hidden by the stale types:
  - quotes.client_name is NOT NULL -> stop inserting null.
  - quote_items.subtotal is required -> compute it on insert.
  - search_analytics uses user_id (not seller_id) and has no
    filters_used column -> correct the insert.
- Restore the set_quote_number BEFORE INSERT trigger (migration) lost in
  the migration-replay drift; without it quote creation fails the
  quote_number NOT NULL constraint. Inserts now pass an empty
  quote_number for the trigger to populate.
- Migrate intelligence untypedFrom() calls to typed supabase.from();
  drop now-unnecessary `as any` casts in the kill-switch client/telemetry.

tsc baseline: 484 errors (-24 vs 508, no regressions). eslint baseline
clean. Build and affected unit tests pass.

* fix(merge): resolve conflict with main — adopt main's BridgeStatusEventInput

main and this PR independently converged on a fix for the non-distributive
Omit over the BridgeStatusEvent union. main shipped first (PR #328 chain)
with a mapped-type `BridgeStatusEventInput`; this PR had an equivalent
`DistributiveOmit`. Adopting main's version keeps a single source of truth
and unblocks the merge.

* fix(merge): resolve conflict — adopt main's useKillSwitchBanner (lighthouse placeholder + dynamic import)

* fix(merge): resolve conflict — adopt main's kill-switch-client tests

* fix(merge): resolve conflict — adopt main's kill-switch-client (rollout gradual + typed cast)

* fix(merge): resolve conflict — adopt main's kill-switch-telemetry (typed cast for kill_switch_hits)

---------

Co-authored-by: Claude <noreply@anthropic.com>
adm01-debug added a commit that referenced this pull request May 26, 2026
* fix(supabase): realign frontend with live DB schema

Regenerate src/integrations/supabase/types.ts from the live database
(141->296 tables, 1->115 views, 65->385 functions) so the typed client
matches reality, then fix every drift the accurate types surfaced.

- Decode src/lib/supabase-untyped.ts and scripts/lint-untyped-from.sh,
  which were committed base64-encoded since #319 and broke compilation.
- Fix nullability drift across admin/security, dashboard, quotes and
  intelligence hooks (DB columns are nullable; code assumed non-null).
- Fix real runtime bugs hidden by the stale types:
  - quotes.client_name is NOT NULL -> stop inserting null.
  - quote_items.subtotal is required -> compute it on insert.
  - search_analytics uses user_id (not seller_id) and has no
    filters_used column -> correct the insert.
- Restore the set_quote_number BEFORE INSERT trigger (migration) lost in
  the migration-replay drift; without it quote creation fails the
  quote_number NOT NULL constraint. Inserts now pass an empty
  quote_number for the trigger to populate.
- Migrate intelligence untypedFrom() calls to typed supabase.from();
  drop now-unnecessary `as any` casts in the kill-switch client/telemetry.

tsc baseline: 484 errors (-24 vs 508, no regressions). eslint baseline
clean. Build and affected unit tests pass.

* fix(merge): resolve conflict with main — adopt main's BridgeStatusEventInput

main and this PR independently converged on a fix for the non-distributive
Omit over the BridgeStatusEvent union. main shipped first (PR #328 chain)
with a mapped-type `BridgeStatusEventInput`; this PR had an equivalent
`DistributiveOmit`. Adopting main's version keeps a single source of truth
and unblocks the merge.

* fix(merge): resolve conflict — adopt main's useKillSwitchBanner (lighthouse placeholder + dynamic import)

* fix(merge): resolve conflict — adopt main's kill-switch-client tests

* fix(merge): resolve conflict — adopt main's kill-switch-client (rollout gradual + typed cast)

* fix(merge): resolve conflict — adopt main's kill-switch-telemetry (typed cast for kill_switch_hits)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants