teqo-backup/docs/plans/aproximar-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

9.6 KiB
Raw Blame History

Aproximar o Mapa das Praças à região filtrada

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 Item do roadmap: docs/roadmap.md (Trilha B, item B12) Impeccable: B — encaixe em PlazaMapPanel / BahiaMap (sem rota nova) Appetite: ~0,5 dia eng; fitToKeys no Leaflet + chaves estáveis do bundle + teste/checklist Responsável: —

Revisão 2026-07-21 (implementação): escopo ampliado com correção de três bugs de hover reportados em campo — (1) ênfase agressiva (fillOpacity + stroke grosso), (2) hover em municípios fora do filtro B7, (3) ênfase sticky no mouseout (setHoveredKey restyleava antes de limpar hoveredKey + selectedKey espelhava hover). Solução: interactiveKeys compartilhando chaves com fitToKeys (plazasByIbgeCode); hover stroke-only (fill inalterado, weight 2); clear síncrono de hoveredKey/selectedKey no mouseout desktop; touch pinned inalterado.

Design (Impeccable)

Âncoras: PRODUCT.md / DESIGN.md (register product — Field Desk) · tema data-theme='campaign' · shells existentes (PlazaMapPanel, BahiaMap).

Na implementação (implement-roadmap-item): craft compacto → critique → polish (comportamento de viewport; sem shape longo).

Brief compacto:

  • Persona / contexto: Alex (coordenador) ou Assessor filtra por TI / tipo / cobertura e olha o mapa — hoje a Bahia inteira continua no frame e a região filtrada fica pequena demais para ler.
  • Job principal: ao mudar o conjunto filtrado (URL ou escopo de access), o mapa aproxima o footprint geográfico desse conjunto.
  • Estratégia de cor: Restrained — coroplético inalterado; só muda o viewport.
  • Anti-goals: não redesenhar o painel; não esconder polígonos fora do filtro (já é contrato B7); não re-zoom a cada troca de ano/escala; não segunda interação “resetar zoom” neste item se o fit automático bastar.

Contexto

B7 (mapa-pracas-filtrado.md) alinhou os dados do mapa ao buildPlazaListWhere da lista — o coroplético só pinta o conjunto filtrado. O viewport, porém, continua em BAHIA_BOUNDS (src/components/campaign/BahiaMap.tsx): PlazaMapPanel não passa highlightKeys, e o else do render faz map.fitBounds(BAHIA_BOUNDS).

O próprio BahiaMap já sabe aproximar: quando highlightKeys tem itens, monta L.geoJSON(highlightedFeatures).getBounds() e chama fitBounds com padding: [24, 24] e maxZoom: 10. B7 adiara exatamente esse uso:

fitBounds ao footprint filtrado — revisit when coordenação filtrar TI/região e reportar que a Bahia inteira no viewport atrapalha.

Pedido de produto (2026-07-21): o gatilho disparou — ao filtrar, aproximar a região.

Objetivos

  • Com filtro URL (ou escopo de assessor) que reduz o conjunto de Praças, o mapa abre/ajusta o viewport ao bounding box dos municípios (codarea) presentes no bundle.
  • Sem filtro (coordenador com Bahia quase completa), o viewport permanece equivalente à Bahia inteira (fit nas chaves ≈ BAHIA_BOUNDS).
  • Trocar Ano / Escala / Comparar não deve re-disparar zoom (só o footprint geográfico).
  • Guardrails: sem migration, sem collection, sem Consent, sem server action; sem mudar o contrato de dados do B7.

Decisões travadas

  • Item próprio B12 (não reabrir B7 nem absorver em R6/B6). Gatilho explícito do B7; é UX de viewport, não filtro de dados, polish genérico nem setStyle. (2026-07-21, roadmap-item.) Rejeitado: fill-in sem plano (edge cases de re-zoom e highlight merecem decisão); fase “entregue” do B7 (histórico as-built fica; trabalho novo ganha ID).
  • Prop dedicada fitToKeys (não reusar highlightKeys). highlightKeys hoje acopla destaque visual (borda/weight vermelhos) + fitBounds. Passar todos os codarea filtrados como highlight pintaria a TI inteira com stroke grosso. Rejeitado: highlightKeys={Object.keys(...)} (ruído visual); esconder GeoJSON fora do filtro (muda o contrato B7 / rabbit hole).
  • Chaves = Object.keys(bundle.plazasByIbgeCode) — footprint estável do conjunto já escopado (access + B7), independente de year / scaleMode / displayValues. Rejeitado: chaves de displayValues (troca de ano re-zooma); só quando a URL tem filtro explícito (assessor com poucas Praças sem ?region= também precisa aproximar).
  • fitBounds só quando a identidade de fitToKeys muda (string canônica sorted join), não a cada rebuild de layer por values. Separar do efeito que reaplica estilo/dados, ou early-return se o footprint não mudou. Rejeitado: deixar o fit dentro do efeito atual ligado a values (B11/ano resetam o pan do usuário).
  • i18n e naming (AGENTS.md): fitToKeys, fitKey / fitBoundsKey; strings visíveis inalteradas neste item.

Questões em aberto

  • Botão “Ver Bahia toda” / reset de viewport? Opções: A) não neste item (pan/zoom Leaflet + limpar filtro bastam) | B) controle explícito. Recomendação: A — appetite; revisitar se a coordenação pedir após usar o fit automático. (assumido — validar no craft)
  • Limiar para não “aproximar” (ex. >N municípios ≈ Bahia)? Opções: A) sempre fitBounds nas chaves (N grande ≈ Bahia) | B) se keys.length > threshold, forçar BAHIA_BOUNDS. Recomendação: A no v1 — menos knobs; B só se profiling/UX mostrar jitter no fit de ~400 features.
  • maxZoom / padding? Opções: A) manter maxZoom: 10, padding: [24, 24] do caminho highlight | B) tunar. Recomendação: A; polish só se um município único ficar apertado demais no craft.

Abordagem proposta

flowchart LR
  bundle["PlazaMapBundle.plazasByIbgeCode"] --> panel["PlazaMapPanel"]
  panel -->|"fitToKeys = Object.keys(...)"| map["BahiaMap"]
  map -->|"fitKey mudou"| fit["map.fitBounds(features)"]
  map -->|"só values/ano/escala"| style["rebuild layer / setStyle — sem re-fit"]

Componentes:

  • BahiaMap (src/components/campaign/BahiaMap.tsx): props fitToKeys?: string[] e interactiveKeys?: string[] (default [] = fit Bahia / tudo interativo). Efeito quando canonicalMapKeysKey(fitToKeys) muda → fitMapToHighlights (sem alterar estilo). interactiveKeys com gate em handlers + interactive: false nos paths fora do set. Hover stroke-only + clear síncrono no mouseout. Reusa fitMapToHighlights e canonicalMapKeysKey em bahiaMapStyle.ts.
  • PlazaMapPanel (src/components/campaign/PlazaMapPanel.tsx): memo scopedKeys = Object.keys(bundle.plazasByIbgeCode) → fitToKeys + interactiveKeys. Não passar as mesmas chaves em highlightKeys.
  • Teste: unit do helper de chave canônica / “quando fitKey muda”; checklist manual: filtrar TI → aproxima; limpar filtros → Bahia; trocar Ano/Escala → viewport estável; assessor com poucas Praças → aproxima sem filtro URL.
  • Migration: Sem migration, sem collection, sem server action.

Dependências

  • Dura: R2 (mapa) + B7 (conjunto filtrado no bundle) — ambos entregues.
  • Suave: nenhuma. B6 (setStyle) e B8 (polígonos-zona) são independentes; B8 pode no futuro refinar fit em SSA/CMS, fora deste item.
  • Reusa: BahiaMap fitBounds existente, plazasByIbgeCode do B10/B7, geometrias bahiaGeometries.

Não escopo

  • Filtrar / omitir dados do mapa → B7 (já entregue).
  • setStyle incremental / perf de rebuild → B6 (escala-dry-pos-b3.md).
  • Polígonos Praças-zona SSA/CMS (fit por zona) → B8 (poligonos-pracas-zona.md).
  • Mover filtros acima do mapa / polish visual → R6.
  • Esconder polígonos cinza fora do filtro; animação flyTo obrigatória.

Rabbit holes

  • Acoplar zoom a highlightKeys. Se alguém “só passar as chaves”: stroke vermelho em dezenas de municípios. Mitigação: prop fitToKeys só para viewport.
  • Re-fit a cada values. Troca de ano/escala (B11) ou hover rebuild reseta o pan. Mitigação: dependência só da identidade de fitToKeys.
  • Dissolver polígonos / PostGIS / clip do layer. Mitigação: só fitBounds; geometria continua Bahia inteira.
  • Fit por Praça-zona em Salvador/Camaçari. Sem polígono de zona o bbox é o município. Mitigação: aceitar agregado municipal até B8.

Adiado com gatilho

  • Controle “Ver Bahia toda”. Revisitar se, após B12, usuários pedirem reset sem limpar filtros.
  • Threshold N→BAHIA_BOUNDS. Revisitar se fit em ~400 codarea causar jank perceptível.
  • Restyle diff só nos paths cujo interactive mudou. Revisitar se troca frequente de filtro causar jank no O(n) restyleAllPaths (hoje full pass quando interactiveKey muda).
  • flyTo / animate: true. Revisitar se critique pedir transição; default Leaflet fitBounds basta no v1.

Referências

  • docs/roadmap.md (Trilha B, item B12; gatilho de B7)
  • docs/plans/mapa-pracas-filtrado.md — Adiado fitBounds (gatilho disparado 2026-07-21)
  • src/components/campaign/BahiaMap.tsx — BAHIA_BOUNDS, fit em highlightKeys (linhas ~310–333)
  • src/components/campaign/PlazaMapPanel.tsx — <BahiaMap> sem viewport keys hoje
  • src/utilities/plazaMapData.ts — PlazaMapBundle.plazasByIbgeCode
  • src/lib/bahiaGeometries.ts — getMunicipalityFeature
  • AGENTS.md — Campaign map / naming
  • PRODUCT.md / DESIGN.md — Field Desk; mapa como instrumento de coordenação