- Compile docs/roadmap.md into a next-steps document (277→206 lines): R0–R5/M1–M5 tables and the delivered map/list item paragraphs compressed into 'Já entregue'; janelas, grafo and cortes list only open IDs; C10/C11/C8 fill-ins keep their original triggers; C5 moved under 'A validar'. - Record the production deploy of the destructive remodel chain (2026-07-23 Vercel builds) across roadmap, AGENTS.md, the project notebook and plans — supersedes every 'deploy pendente'; smoke pós-deploy + onboarding remain the open blockers. - Fix AGENTS.md internal contradictions: Município locked-decisions bullet (435, Camaçari whole, candidate role), campaignUser role enum, deploy-checklist item 7 as historical, access/* structure note, election-aggregates artifact + election-tse revalidate runbook. - Sync open plans to real identifiers (plaza*/nucleus* → municipality*): the intelligence program (master + E8–E15/B13/C12 with artifact/caching context and satisfied deps), B8 rescoped to Salvador ZE 1–19 only, WhatsApp D3–D5, D2, A6 (now the insight layer over stateDeputy), RS+, onda-0 smoke step. - Close items verified done in code: C8 F4 (DRY de forms) complete, filtros-auto shared pending shell, petition generateStaticParams select/depth; ledger the surviving user-visible 'Praça' copy in src as new P2 debt. - Banner delivered praça-era plans (production note + rename pointer); mark remodelagem-pracas.md superseded and remodelagem-municipios.md delivered. - TECH-DEBT smell statuses, TESTING RBAC row, README quality gates, campanha UI rules updated to current component names.
10 KiB
Filtros auto-aplicados na lista de municípios
Status: entregue (2026-07-21; em produção desde 2026-07-23)
Atualizado em: 2026-07-24
Item do roadmap: docs/roadmap.md (Fill-ins)
Impeccable: B — encaixe em MunicipalityFilters (/campanha/municipios); sem rota nova
Appetite: ~0,5 dia eng; só client em MunicipalityFilters (+ pending a11y); sem migration
Responsável: —
Revisão 2026-07-24 (remodelagem M1 + hardening): identificadores renomeados
plaza*→municipality*(MunicipalityFilters,buildMunicipalityFiltersKey,shouldUpdateMunicipalitySearchUrlemmunicipalityUi.ts, rota/campanha/municipios); nomes antigos abaixo são da época. O hardening 2026-07-23 entregou o shell compartilhado de pending (CampaignListPendingBoundary+useCampaignListPending), fechando um dos itens Adiados.
Revisão 2026-07-21 (pós-implementação + /simplify + capture-review-debts): debounce 1s + Enter imediato + pending UI; commitNavigation com no-op key; testes unit. Débitos S1–S2 registrados em Adiado (não reabrir no simplify).
Design (Impeccable)
Âncoras: PRODUCT.md / DESIGN.md (register product — Field Desk) · tema data-theme='campaign' · shells existentes (CampaignPageShell, CampaignSearchInput, PlazaFilters).
Na implementação (implement-roadmap-item): craft compacto → critique → polish (sem shape longo — interação, não redesign).
Brief compacto:
- Persona / contexto: Alex / Assessor monta um recorte de Praças sob pressão (TI + tipo + busca por município); espera que a lista reaja sem um passo extra de confirmação.
- Job principal: filtrar a lista (e, com B7, o mapa) só mudando controles — busca com atraso curto, selects imediatos.
- Estratégia de cor: Restrained (inalterada).
- Anti-goals: segundo botão de confirmação; redesign do disclosure de filtros; unificar todas as listas da vertical neste item.
Contexto
Em /campanha/pracas, PlazaFilters (src/components/campaign/PlazaFilters.tsx) já aplica selects na URL via router.replace + buildPlazaListHref no onChange (região, tipo, assessoria, tendência, prioridade). Só o campo de texto q fica em estado local e só sobe para a URL no onSubmit do form — daí o botão "Buscar".
Na era Núcleos (2026-07-17) a listagem já aplicava filtros pelo estado da URL sem botão de confirmação (notebook). A remodelagem R2 reintroduziu o padrão "digitar + Buscar" só na busca textual. Pedido de produto (2026-07-21): aplicar automaticamente ao selecionar valores; na busca, disparar a atualização após 1 s sem digitar, para não navegar a cada caractere.
Precedente de pending UI: ActionPlanFilters (useTransition + data-pending + aria-live "Atualizando resultados…"). SupporterFilters ainda usa Buscar no q — fora deste item.
Objetivos
- Remover a necessidade do botão Buscar em
/campanha/pracas: selects continuam imediatos;qatualiza a URL após 1000 ms de quietude (debounce). - Enter no campo de busca aplica
qimediatamente (sem esperar o timer) — acessibilidade e atalho de poder. - Manter Limpar (zera local +
router.replace('/campanha/pracas')). - Preservar remontagem canônica por
key={buildPlazaFiltersKey(state)}na página (servidor = fonte da verdade; voltar/avançar sincroniza). - Feedback de atualização (
isPending/aria-busy/ live region) alinhado aActionPlanFilters. - Guardrails: sem migration, sem collection, sem Consent, sem server action; URL e
parsePlazaListParamsinalterados.
Decisões travadas
- Fill-in com plano (não B8 nem fase de R6/B7). É restauração de interação barata, paralelizável, cortável; não muda contrato lista↔mapa (B7) nem ciclo visual R6. (2026-07-21, classificação roadmap-item.) Rejeitado: item de trilha B (infla grafo por polish de ½ dia); absorver só em R6 (atrasa quick win e some no critique largo); fase de mapa-pracas-filtrado.md (preocupação distinta: where do mapa).
- Debounce só em
q= 1000 ms; selects sem debounce. Pedido explícito de produto; selects já são baratos (um replace por mudança). Rejeitado: debounce em todos os controles (atrasa feedback de select); Buscar só no texto e selects auto (estado atual — deixa o botão como falso afunilamento mental). - Remover o botão Buscar; Enter aplica na hora. Com debounce + Enter, o botão é redundante. Rejeitado: manter Buscar como atalho (ruído visual Field Desk; era Núcleos já operava sem).
- Não extrair hook/shared filter shell neste item. Um call site de debounce;
ActionPlanFilters/SupporterFiltersnão unificam ainda. Rejeitado:useDebouncedPlazaFilters/ factory de listas (<3 call sites — classitis). - i18n e naming (AGENTS.md): identificadores em inglês (
SEARCH_DEBOUNCE_MS,navigate,isPending); strings visíveis em pt-BR (live region, labels existentes).
Questões em aberto
- Espelhar em apoiadores / lideranças / organizações? Opções: A) neste item | B) fill-ins separados sob demanda | C) lote em R6. Recomendação: B — escopo = Praças (pedido); cada lista tem forma diferente (disclosure, tabs). Gatilho abaixo.
- Constante 1000 ms vs prop? Opções: A)
const SEARCH_DEBOUNCE_MS = 1000no arquivo | B) prop no componente. Recomendação: A — um call site; prop só se o 2º consumidor divergir.
Abordagem proposta
flowchart LR
select["NativeSelect onChange"] --> nav["navigate → router.replace"]
type["CampaignSearchInput onChange"] --> local["setSearch + timer 1s"]
local -->|"quietude"| nav
enter["Enter / form submit"] --> nav
nav --> url["URL ?q=®ion=…"]
url --> rsc["RSC refresh lista/overview/(mapa B7)"]
Componentes:
PlazaFilters(src/components/campaign/PlazaFilters.tsx):- Manter
navigate(next)compage: 1escroll: false. - Em
onChangedo search: atualizarsearchlocal;clearTimeout+setTimeout(1000)que chamanavigate({ ...state, q: trimmed || undefined })só se o valor canônico diferir destate.q(evitar replace no-op). - Cleanup do timer no unmount; cancelar timer ao Limpar / ao Enter (flush imediato).
- Envolver replaces em
startTransition; espelhardata-pending/aria-busy/ live region deActionPlanFilters. - Remover o
Button type="submit""Buscar"; form pode permanecer comonSubmit= flush imediato doq(Enter). - Depth check: reusar
buildPlazaListHref/PlazaListState/CampaignSearchInput— sem utility nova de debounce até o 3º call site.
- Manter
pracas/page.tsx: sem mudança obrigatória (já passakey={buildPlazaFiltersKey(state)}). Soft: com B7 entregue, cada replace de filtro também refresca o mapa — aceitável no appetite.- Teste: unit leve do helper de “próximo
q/ skip no-op” se extrair uma função pura de 5 linhas; senão E2E/manual: digitar “salvador”, esperar ≥1s, assert URLq=; mudar TI, assert URL imediata sem clique. Precedente de filtros URL: era Núcleos / notebook. - Migration: Sem migration, sem collection, sem server action.
Dependências
- Dura: nenhuma de outro plano (R2 já entregue).
- Suave: B7 (mapa-pracas-filtrado.md) ✓ entregue — cada replace de filtro também refresca o mapa filtrado.
- Reusa:
plazaUi.ts(buildPlazaListHref,PlazaListState), padrão pending deActionPlanFilters.tsx, dwell doRecentVisitTracker(já re-arma o timer a cadahref— debounce 1s + dwell ~2s evita visitar intermediários).
Não escopo
- Filtro do mapa pela URL → B7.
- Critique/polish visual / mover filtros acima do mapa → R6.
- Auto-busca em
/campanha/apoiadores, lideranças, organizações → fill-ins futuros (gatilho abaixo). setStyleincremental no mapa → B6.
Rabbit holes
- Hook compartilhado
useCampaignListFilters. Se alguém “só generalizar”: redesenha três listas. Mitigação: inline neste arquivo; extrair no 3º call site. - Debounce via lib (
use-debounceetc.). Dependência nova por 10 linhas. Mitigação:setTimeout+ cleanup. - Otimistic list client-side. Duplicaria
buildPlazaListWhereno browser. Mitigação: URL + RSC como hoje.
Já resolvido no simplify/critique (não reabrir)
- Alias
plazaSearchParamFromInputremovido;normalizedText+shouldUpdatePlazaSearchUrlemplazaUi.ts. - No-op de navegação via
buildPlazaFiltersKey(não doisbuildPlazaListHref). hasActiveFiltersinclui texto local de busca (Limpar visível antes do debounce).clearDebounceduplicado noonSubmitremovido.- Impeccable
detect.mjsno alvo: 0 findings (sem critique formal nesta sessão).
Adiado com gatilho
- Sync
searchlocal ↔state.qno back/forward do browser.key={buildMunicipalityFiltersKey(state)}remonta na maioria dos casos; edge case: campo pode mostrar texto stale após voltar/avançar. Gatilho: QA ou R6 reportar desync em/campanha/municipios. Shell compartilhado de pending UI— entregue no hardening 2026-07-23:CampaignListPendingBoundary/useCampaignListPendingcompartilhados porMunicipalityFilters,ActionPlanFilters,SupporterFilters,CampaignSearchForme paginação (dim +aria-busy).- Auto-aplicação com debounce nas demais listas com Buscar (
SupporterFilters,CampaignSearchFormde lideranças/organizações/dobradinhas — hoje têm pending compartilhado, mas oqainda é submit-driven). Revisitar quando produto pedir ou quando R6/critique citar “Buscar” como atrito repetido em ≥2 listas. - Extrair
useDebouncedSearchParam. Revisitar no 3º call site idêntico (municípios + apoiadores + 1).
Referências
docs/roadmap.md(Fill-ins; R6)src/components/campaign/MunicipalityFilters.tsx— alvo da mudançasrc/components/campaign/ActionPlanFilters.tsx— precedente auto-apply + pendingsrc/components/campaign/SupporterFilters.tsx/CampaignSearchForm.tsx— pending compartilhado;qainda submit-drivensrc/components/campaign/CampaignListPending.tsx— shell de pending do hardeningsrc/app/(campaign)/campanha/(app)/municipios/page.tsx—buildMunicipalityFiltersKeysrc/utilities/plazaUi.ts— URL canônica- AGENTS.md — naming; superfície
(campaign) PRODUCT.md/DESIGN.md— Field Desk / clarity under pressure- Notebook — precedente 2026-07-17 filtros auto na lista de Núcleos