teqo/docs/plans/hover-mapa-pracas.md
Francisco Ignácio Fontoura Solla adc0f31f5b Sync all project docs with the deployed remodel and hardening; compile roadmap
- 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.
2026-07-24 02:04:45 -03:00

10 KiB
Raw Permalink Blame History

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 rotas pracas/ → 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 matchMedia separado 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). setStyle incremental ao trocar ano → B6 ✓.

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 / Link para 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 values já passado ao mapa — sem fetch por hover/click.
  • Guardrails: sem migration, sem collection, sem Consent, sem server action; leader continua 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: coarse obrigatórios). Estado selectedKey: hover (desktop) e 1º tap (mobile) selecionam; click/tap com key === selectedKey navega; hover não navega. Em desktop o hover já deixa selectedKey igual 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; matchMedia mobile/desktop com dois comportamentos divergentes sem o mesmo selected.
  • Cliente-only; bundle traz plazasByIbgeCode. Nome no readout pode vir do GeoJSON; navegação precisa de slug(s) por codarea — incluir no PlazaMapBundle um mapa ibgeCode → { 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 lista zoneBreakdown (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/button para 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): acrescentar plazasByIbgeCode: 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): onEachFeature com mouseover / mouseout / click. Hover → callback onFeatureSelect(info); mouseout → onFeatureSelect(null) (só se o ponteiro for fino / não após tap recente — ref pointerType ou “touch pin” até tap fora). Click → callback onFeatureActivate(key) (o painel decide select vs navigate). Destaque via setStyle + bringToFront; mouseout restaura estilo do coroplético. Depth check: sem wrapper novo.
  • PlazaMapPanel: estado selected; readout; em onFeatureActivate: se key !== selected → select; se igual → navigateForIbge(key) (1 slug → router.push; N>1 → scrollIntoView na seção zonas). Opcional: link na faixa quando slugs.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 (setStyle) entregue; B7 (filtro → plazasByIbgeCode só 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

Rabbit holes

  • react-leaflet / Popup Radix. Mitigação: Leaflet nativo + faixa HTML.
  • Hit-test de zonas sem polígono. Mitigação: agregado + scroll para zoneBreakdown até B8.
  • Detectores isMobile frágeis. Mitigação: modelo selected+activate; no máximo pointerType no evento para não limpar seleção no “mouseout” sintético pós-tap.
  • Conflito pan vs 2º tap. Mitigação: só navegar em click Leaflet do path (não em moveend); 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 / highlight
  • src/components/campaign/PlazaMapPanel.tsx — selected + readout + nav
  • src/utilities/plazaMapData.ts / plazaMapNavigation.ts — plazasByIbgeCode
  • docs/plans/mapa-pracas-filtrado.md — B7
  • docs/plans/escala-dry-pos-b3.md — B6
  • docs/plans/poligonos-pracas-zona.md — B8
  • AGENTS.md — naming, mapa dinâmico /campanha, access assessor
  • PRODUCT.md / DESIGN.md — Field Desk