- 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
Hover no Mapa das Praças (destaque + total de votos + navegação)
Nota (2026-07-24): entregue na era Praças e em produção desde 2026-07-23. A remodelagem M1 renomeou os identificadores
plaza*→municipality*e as rotaspracas/→municipios/; os nomes neste plano são os da época. Nomes atuais: ver remodelagem-municipios.md.
Status: entregue 2026-07-21 (em produção desde 2026-07-23)
Atualizado em: 2026-07-21 (navegação: desktop click / mobile 2º tap; B6 ✓ setStyle incremental)
Item do roadmap: docs/roadmap.md (Trilha B, item B10 — entregue 2026-07-21)
Impeccable: B — encaixe em PlazaMapPanel / BahiaMap (sem rota nova)
Appetite: ~0,5–1 dia eng; handlers Leaflet + faixa + mapa ibge→slug + teste/checklist
Responsável: —
Revisão 2026-07-21 (pós-implementação + /simplify): BahiaMap com onFeatureSelect / onFeatureActivate; MapFeatureReadout; plazasByIbgeCode + resolvePlazaMapNavigation; SSA/CMS N>1 → scroll #plaza-zone-breakdown. Cleanup: dedup hover, merge effects, emphasizeFeature. Débito hot path O(n) eachLayer → B6 ✓ (escala-dry-pos-b3.md).
Design (Impeccable)
Âncoras: PRODUCT.md / DESIGN.md (register product — Field Desk) · tema data-theme='campaign' · shells existentes (PlazaMapPanel, BahiaMap, ChoroplethLegend).
Na implementação (implement-roadmap-item): craft compacto → critique → polish (superfície já existe; interação nova, sem shape longo).
Brief compacto:
- Persona / contexto: Assessor/Coordenador Geral (Alex) inspeciona o coroplético, lê o número sob o cursor e, quando quiser, abre a Praça sem voltar à lista.
- Job principal: destacar o polígono ativo, mostrar nome + total da métrica corrente, e navegar para
/campanha/pracas/[slug]com o gesto certo (click desktop / 2º tap mobile). - Estratégia de cor: Restrained — borda/weight de destaque com primário
#C51414; fill do coroplético inalterado. - Anti-goals: não navegar no hover; não tooltip SaaS ornamentado; não segunda legenda; não redesenhar o painel; não exigir
matchMediaseparado se o modelo selected+click unificar desktop/mobile.
Contexto
O Mapa das Praças (PlazaMapPanel em /campanha/pracas) já pinta municípios pelo valor do ano selecionado (TSE 2014/2018/2022 ou estimativas 2026) e no modo comparar pelo diff Solla − outro. O renderer BahiaMap monta L.geoJSON com estilo estático e highlightKeys só para zoom/destaque programático — não há mouseover / readout / click→Praça.
Pedido de produto (2026-07-21): hover destaca + mostra votos; click navega. Em mobile (sem hover): primeiro toque seleciona (mesmo readout); segundo toque na já selecionada navega.
Não cobre: filtro URL (B7), polígonos por zona (B8). → B6 ✓.setStyle incremental ao trocar ano
Objetivos
- Hover (desktop) sobre um município: borda/weight mais forte + readout com nome + valor formatado da métrica corrente; não navega.
- Click (desktop) no polígono sob o hover:
router.push/Linkpara a Praça correspondente. - Touch: 1º tap = seleciona (destaque + readout); 2º tap na mesma seleção = navega; tap noutro polígono = troca a seleção.
- O valor exibido é o mesmo de
valuesjá passado ao mapa — sem fetch por hover/click. - Guardrails: sem migration, sem collection, sem Consent, sem server action;
leadercontinua sem mapa; pan/zoom do Leaflet não devem competir com o 2º tap (ver abordagem).
Decisões travadas
- Item próprio B10 (não absorver em B6, B7 nem R6). Interação de inspeção+atalho ≠ filtro URL, ≠ perf
setStyle, ≠ polish genérico. (2026-07-21.) Rejeitado: fill-in sem plano; fase de B7. - Modelo unificado selected + click (não forks
pointer: coarseobrigatórios). EstadoselectedKey: hover (desktop) e 1º tap (mobile) selecionam; click/tap comkey === selectedKeynavega; hover não navega. Em desktop o hover já deixaselectedKeyigual ao polígono, então o 1º click navega. Em mobile o 1º tap só seleciona. (Produto, 2026-07-21.) Rejeitado: só highlight sem navegação; navegação no hover;matchMediamobile/desktop com dois comportamentos divergentes sem o mesmo selected. - Cliente-only; bundle traz
plazasByIbgeCode. Nome no readout pode vir do GeoJSON; navegação precisa deslug(s) porcodarea— incluir noPlazaMapBundleum mapaibgeCode → { slug, name }[](já filtrado pelo escopo de access / futuro B7). Rejeitado: Server Action por click; adivinhar slug a partir do nome do município no client. - Faixa de leitura sob o mapa + destaque Leaflet. Touch-friendly e
aria-live="polite". Tooltip Leaflet opcional só no polish. Rejeitado: só tooltip hover-only; painel inspect lateral. - Mesma métrica do seletor Ano / Comparar. Rejeitado: segundo número no hover v1.
- Salvador / Camaçari (N>1 Praças no mesmo
ibgeCode): seleção mostra agregado municipal + readout; navegação no 2º gesto não escolhe zona às cegas — faz scroll/focus na listazoneBreakdown(ou na faixa, com hint “ver Praças por zona abaixo”). Se o escopo do usuário tiver exatamente 1 Praça-zona naquele município, aí sim navega para o único slug. (2026-07-21.) Rejeitado: sempre ir à ZE 1 / primeira do catálogo; inventar hit-test por zona (= B8). - i18n e naming (AGENTS.md):
onFeatureSelect,selectedFeature,plazasByIbgeCode,MapFeatureReadout; strings em pt-BR.
Questões em aberto
- Polígono sem valor (0 / ausente no
values)? Opções: A) ainda seleciona e mostra “0” / “Sem dados”; navegação só se houver slug(s) no mapa | B) ignora interação. Recomendação: A — “Sem dados” se chave ausente; “0” se presente e zero; click-nav segue a regra de slugs (município sem Praça no escopo → sem navegação, só readout). - Faixa clicável como atalho de navegação? Opções: A) só o polígono | B) faixa também é link/
buttonpara o mesmo destino. Recomendação: B quando há exatamente 1 slug (a11y / alvo 44px); com N>1 a faixa não navega (só o scroll hint). (assumido — validar no craft)
Abordagem proposta
flowchart LR
bundle["PlazaMapBundle values + plazasByIbgeCode"] --> panel["PlazaMapPanel"]
panel --> map["BahiaMap onFeaturePointer"]
map -->|"hover / 1º tap"| select["selectedKey + destaque + readout"]
map -->|"click / 2º tap same key"| nav{"slugs.length?"}
nav -->|"1"| push["router.push /pracas/slug"]
nav -->|"N>1"| zones["scroll zoneBreakdown"]
map -->|"mouseout desktop"| clear["limpa selected se não pinned touch"]
Componentes:
loadPlazaMapBundle/PlazaMapBundle(src/utilities/plazaMapData.ts): acrescentarplazasByIbgeCode: Record<string, { slug: string; name: string }[]>a partir das Praças já carregadas (município = 1 entrada; SSA/CMS = N zonas no escopo). Sem query extra.BahiaMap(src/components/campaign/BahiaMap.tsx):onEachFeaturecommouseover/mouseout/click. Hover → callbackonFeatureSelect(info); mouseout →onFeatureSelect(null)(só se o ponteiro for fino / não após tap recente — refpointerTypeou “touch pin” até tap fora). Click → callbackonFeatureActivate(key)(o painel decide select vs navigate). Destaque viasetStyle+bringToFront; mouseout restaura estilo do coroplético. Depth check: sem wrapper novo.PlazaMapPanel: estadoselected; readout; emonFeatureActivate: sekey !== selected→ select; se igual →navigateForIbge(key)(1 slug →router.push; N>1 →scrollIntoViewna seção zonas). Opcional: link na faixa quandoslugs.length === 1.- Alinhamento com B6: mesma função de estilo no reset pós-hover. Não bloqueia B10.
- Teste: unit do resolver
navigateForIbge(0 / 1 / N slugs); checklist manual desktop (hover→readout, click→ficha) e mobile (tap1 select, tap2 navigate, tap outro troca). - Migration: Sem migration, sem collection, sem server action.
Dependências
- Dura: R2 (mapa Praças) — entregue.
- Suave: A9 (métrica 2026);
B6 (entregue; B7 (filtro →setStyle)plazasByIbgeCodesó do conjunto filtrado); B8 (aí o 2º tap em SSA/CMS pode passar a ir à Praça-zona). - Reusa:
PlazaMapPanel,BahiaMap,plazaMapData, formatters existentes.
Não escopo
- Filtrar mapa pela URL da lista → B7 (mapa-pracas-filtrado.md).
Rebuild/perf do layer ao trocar ano→ B6 ✓ (escala-dry-pos-b3.md).- Polígonos / hover por Praça-zona SSA/CMS → B8 (poligonos-pracas-zona.md).
- Redesenho do painel / mover filtros → R6.
Rabbit holes
react-leaflet/ Popup Radix. Mitigação: Leaflet nativo + faixa HTML.- Hit-test de zonas sem polígono. Mitigação: agregado + scroll para
zoneBreakdownaté B8. - Detectores
isMobilefrágeis. Mitigação: modelo selected+activate; no máximopointerTypeno evento para não limpar seleção no “mouseout” sintético pós-tap. - Conflito pan vs 2º tap. Mitigação: só navegar em
clickLeaflet do path (não emmoveend); se pan arrastar, Leaflet não dispara click no feature.
Adiado com gatilho
- 2º tap em SSA/CMS → Praça-zona específica. Revisitar quando B8 entregar polígonos (hit-test real) ou produto pedir picker de zona no readout.
- Tooltip Leaflet além da faixa. Revisitar no polish se critique pedir reforço desktop.
Referências
docs/roadmap.md(Trilha B, item B10 — entregue)src/components/campaign/BahiaMap.tsx—onFeatureSelect/onFeatureActivate/ highlightsrc/components/campaign/PlazaMapPanel.tsx— selected + readout + navsrc/utilities/plazaMapData.ts/plazaMapNavigation.ts—plazasByIbgeCodedocs/plans/mapa-pracas-filtrado.md— B7docs/plans/escala-dry-pos-b3.md— B6docs/plans/poligonos-pracas-zona.md— B8- AGENTS.md — naming, mapa dinâmico
/campanha, access assessor PRODUCT.md/DESIGN.md— Field Desk