From b83c7722313c82dbedbe3af365d2e50ea49d01c2 Mon Sep 17 00:00:00 2001 From: adm01-debug Date: Fri, 8 May 2026 21:15:16 -0300 Subject: [PATCH] fix(contacts): atualizar total na branch search + remover countData morto (Onda 2 PR 2.2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve issue do CodeRabbit no PR #99 (Onda 1.3): `countData` declarado mas nunca usado, e `setTotal` não chamado na branch de search → UI mostra valores stale do query anterior. ## Fixes em src/components/contacts/useContactsPagination.ts ### 1) Removida variável morta ```diff - const countData: { count: number } | null = null; ``` Linha 75 declarava mas nunca usava. Era um vestígio (provavelmente de uma tentativa anterior de implementar count que ficou pelo caminho). ### 2) setTotal adicionado na branch search de loadContacts ```diff data = (searchData ?? []).map(sanitizeRow); + // TODO: search_contacts RPC não retorna count exato. Por ora, setamos total = qtd carregada + // (acumulado em loadMore). Resolver criando count_search_contacts() RPC. + setTotal(data.length); ``` ### 3) setTotal acumulando em loadMore (branch search) ```diff data = (searchData ?? []).map(sanitizeRow); + // Search RPC: acumular total conforme loadMore traz mais resultados + setTotal((prev) => prev + data.length); setContacts((prev) => [...prev, ...data]); ``` ## Por que essa estratégia (e não count exato) O RPC `search_contacts` retorna apenas a `TABLE` de resultados, sem coluna count. As alternativas seriam: - **A) Query paralela de count**: complexa porque o RPC usa `websearch_to_tsquery('portuguese', unaccent(...))` — duplicar essa lógica em outra query é frágil - **B) Modificar o RPC pra adicionar window function `count(*) over()`**: mudança de DB schema, vai pra Onda 5 (TS hardening + DB) - **C) Setar total = qtd carregada acumulada (esta solução)**: aproximação razoável, cresce conforme o usuário rola, e é correto quando `data.length < PAGE_SIZE` (resultado completo cabe) Estratégia C foi escolhida pelo trade-off: resolve o bug de stale (impacto direto na UX) sem mudar DB nem duplicar lógica de search. TODO documentado pra resolver definitivamente quando criarmos `count_search_contacts()` RPC. ## Trade-off: o que muda na UX | Cenário | Antes (bug) | Depois | |---|---|---| | Search retorna 30 resultados | "5000 contatos encontrados" (stale) | "30 contatos encontrados" ✓ | | Search retorna 200, paginação 50/pg | "5000 contatos encontrados" (stale) | "50 → 100 → 150 → 200 conforme rola" ✓ | | Filtro normal (sem search) | exato (`count: 'exact'`) | exato (sem mudança) | Vale lembrar que `hasMore` continua exato (`data.length === PAGE_SIZE` indica se há mais). Apenas o display de "X encontrados" mostra o quanto foi carregado, não o total absoluto. ## Stress-test - bun run build: OK (1m 6s) ✓ - TypeScript: sem novos erros ✓ - Comportamento em filtros normais: preservado (count exato continua funcionando) ✓ ## Refs - /workspace/notes/coderabbit-feedback-pr99.md (issue #1) - PR #99 (#issuecomment do CodeRabbit Pro) --- src/components/contacts/useContactsPagination.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/contacts/useContactsPagination.ts b/src/components/contacts/useContactsPagination.ts index 2079cacd2..38bbb7f01 100644 --- a/src/components/contacts/useContactsPagination.ts +++ b/src/components/contacts/useContactsPagination.ts @@ -71,7 +71,6 @@ export function useContactsPagination(workspaceId: string) { try { let data: ContactListItem[] = []; - const countData: { count: number } | null = null; if (f.search.trim()) { // Use full-text search RPC (handles unaccent + trigram) @@ -82,6 +81,9 @@ export function useContactsPagination(workspaceId: string) { }); if (error) throw error; data = (searchData ?? []).map(sanitizeRow); + // TODO: search_contacts RPC não retorna count exato. Por ora, setamos total = qtd carregada + // (acumulado em loadMore). Resolver criando count_search_contacts() RPC. + setTotal(data.length); } else { // Standard query with filters let query = dbFrom('contacts') @@ -138,6 +140,8 @@ export function useContactsPagination(workspaceId: string) { }); if (error) throw error; data = (searchData ?? []).map(sanitizeRow); + // Search RPC: acumular total conforme loadMore traz mais resultados + setTotal((prev) => prev + data.length); } else { let query = dbFrom('contacts') .select('id, name, phone, email, company, tags, channel, avatar_url, last_seen_at, created_at')