Commit graph

987 commits

Author SHA1 Message Date
Francisco Ignácio Fontoura Solla
6fc3bd04b2 Register B39–B42: session persistence, biometrics, and municipality list UX. 2026-07-26 22:15:39 -03:00
Francisco Ignácio Fontoura Solla
f4e7b4f1e0 Merge branch 'cursor/3907847e' 2026-07-26 21:27:45 -03:00
Francisco Ignácio Fontoura Solla
a911c7191f chore: format after E12 (pre-push gate). 2026-07-26 21:27:23 -03:00
Francisco Ignácio Fontoura Solla
f06e573f15 E12: camada TI com rollups MAUP e benchmark intra-TI.
Estende territórios com cobertura/captura/classe agregadas e compara captura do município aos pares do mesmo TI no card Conta da cadeira.
2026-07-26 21:26:47 -03:00
Francisco Ignácio Fontoura Solla
ba90a3b2dc Merge branch 'cursor/6b2ac7b2' 2026-07-26 21:16:40 -03:00
Francisco Ignácio Fontoura Solla
06ea1ff295 B36: chips de lideranças na lista de dobradinhas
Espelha a aresta leadership.stateDeputies do B31 do lado do deputado, com célula compartilhada bidirecional, loadLeadershipOptions e casca própria de formActions.
2026-07-26 21:15:44 -03:00
Francisco Ignácio Fontoura Solla
162d4c10fe perf(campanha): roda as funções em gru1 e encurta o waterfall de queries
As funções rodavam no default iad1 (Virgínia) enquanto o Neon fica em
sa-east-1 (São Paulo): cada query do Payload atravessava o equador duas
vezes (~120 ms por ida-e-volta) e uma página do /campanha faz uma dúzia.
Medido em produção: 410-470 ms de TTFB para um 307 que não toca o banco.

- vercel.json fixa regions: [gru1], mesma região AWS do banco (Hobby
  permite uma região, mas permite escolher qual).
- loadStatewideSuggestedGoals passa a ser exportada e é disparada dentro
  do Promise.all que já lê o escopo, na lista e no dashboard: o read do
  global campaignGoals saía sozinho no fim da requisição.
- loadMunicipalityScope lê os pledges em paralelo quando o ator é
  irrestrito e não há filtro (aí o IN de 435 ids não exclui nada);
  qualquer escopo mais estreito continua esperando os ids.

Débitos medidos no caminho e registrados em TECH-DEBT: o fan-out de
leituras do ator em getFreshCampaignUser (~5 por render) e a leitura
dupla em authenticateCampaignToken.
2026-07-26 21:00:06 -03:00
Francisco Ignácio Fontoura Solla
8b8a242073 Merge branch 'cursor/c2dfe0d1' 2026-07-26 20:47:36 -03:00
Francisco Ignácio Fontoura Solla
d6455df5e7 B30: convite WhatsApp na lista de lideranças
Staff gera convite de autopreenchimento na coluna de ações da lista, reutilizando createCampaignInvite e alinhando validação de celular ao link de contato do B28.
2026-07-26 20:47:11 -03:00
Francisco Ignácio Fontoura Solla
013f167d47 Fix /campanha/liderancas 500: RSC-safe phone display
CampaignCopyableCell is a Client Component; passing formatDisplay as a
function from the leaderships RSC page broke serialization in production.
Use a preformatted displayValue string at the call sites instead.
2026-07-26 20:16:01 -03:00
Francisco Ignácio Fontoura Solla
3d2cd4e163 Merge branch 'cursor/b37ff931' 2026-07-26 19:54:39 -03:00
Francisco Ignácio Fontoura Solla
d869e6a653 docs(B31): marca adiado do Context como resolvido pela 2ª rodada de /simplify 2026-07-26 19:53:57 -03:00
Francisco Ignácio Fontoura Solla
ef3d5b6c22 B31: coluna de dobradinhas na lista de lideranças
Coluna "Dobradinhas" em /campanha/liderancas com chips clicáveis
para /campanha/dobradinhas/<slug> e edição via Popover+Command
(padrão B27, escrita via server action delta + lock, como B19).

- LeadershipRowViewModel.stateDeputies (StateDeputySummary[]) via
  loadStateDeputySummaries, substituindo stateDeputyNames.
- setLeadershipStateDeputyMembership(Record): delta com
  withPayloadTransaction + acquireTextAdvisoryLocks + revalidatePath.
- nextStateDeputyIdsAfterMembership (lib/leadershipStateDeputyMembership.ts),
  espelha nextAdvisorIdsAfterMembership.
- loadStateDeputyOptions estendido com plainName/party/slug.
- Catálogo entregue uma vez por tabela via leadershipColumns(options)
  (columns factory, mesmo precedente de municipalityListColumns).

/simplify (1ª rodada): corrige revert otimista por delta (não pelo
snapshot inteiro), remove fallback silencioso na adição, extrai
sameIdSet compartilhado com MunicipalityListAdvisorsControl (B27), e
evita leitura+revalidação supérfluas no caminho no-op da action.

/simplify (2ª rodada, pós-rebase): troca o StateDeputyOptionsProvider
(Context) inicial pela columns factory acima — a 1ª rodada tinha
validado o Context por falta da evidência de que MunicipalityList.tsx
já resolve o mesmo problema sem Context; corrige revalidatePath para
o id concreto em vez do template dinâmico '/campanha/liderancas/[id]'
(que invalidava toda ficha a cada toggle); remove non-null assertion
movendo a guarda para dentro do setCurrent updater.

Gate verde: tsc, lint --max-warnings=0, format:check, unit 530/530,
int 406/406 (incl. os 6 de campaignLeadershipStateDeputyMembership),
build. Aikido: 0 achados nos arquivos novos/editados.
2026-07-26 19:50:15 -03:00
Francisco Ignácio Fontoura Solla
2ee9a4fe23 Merge branch 'cursor/4c694ada' 2026-07-26 19:00:16 -03:00
Francisco Ignácio Fontoura Solla
a60200cf6c B25: link do território na lista de municípios
Fecha a navegação inversa para /campanha/territorios com âncoras estáveis compartilhadas entre lista e destino.
2026-07-26 18:59:49 -03:00
Francisco Ignácio Fontoura Solla
e5dd0c3b3f Merge branch 'cursor/716686b9' 2026-07-26 18:47:45 -03:00
Francisco Ignácio Fontoura Solla
d38997686d fix(mapa): limita pan/zoom do BahiaMap ao estado
Evita que arrastar ou dar zoom-out no mapa do Início tire o usuário da Bahia por engano em campo; maxBounds + minZoom runtime alinhados ao fitBounds.
2026-07-26 18:47:06 -03:00
Francisco Ignácio Fontoura Solla
0942686433 Merge branch 'cursor/617e8b7f' 2026-07-26 18:40:58 -03:00
Francisco Ignácio Fontoura Solla
2086ed50e9 B38: sidebar recolhido em tablet no shell /campanha
Rail offcanvas com cookie, default colapsado em viewport tablet e trigger visível a partir de md; fecha o item no roadmap.
2026-07-26 18:40:31 -03:00
Francisco Ignácio Fontoura Solla
f96a2db2cb Add roadmap-items skill for batch roadmap registration.
Orchestrates roadmap-item per idea with consolidated roadmap edits (IDs, mermaid, Ordem).
2026-07-26 18:29:53 -03:00
Francisco Ignácio Fontoura Solla
97c0953a03 format: prettier no plano do B38 (sidebar recolhido em tablet) 2026-07-26 18:09:24 -03:00
Francisco Ignácio Fontoura Solla
93d10d15a7 Merge branch 'cursor/0c5cae5d'
Conflito unico em docs/roadmap.md, nas cinco superficies em que B14 aparece
(linha "Atualizado em", bullet do item, no do grafo, Paralelizaveis/Janela 1 e
Cortes seguros): main tinha B14 pendente mais os registros de hoje (B38 e o
fill-in de limitar o mapa a Bahia), a feature tinha B14 entregue sem eles.
Resolvido preservando os dois lados em todas elas.
2026-07-26 18:08:36 -03:00
Francisco Ignácio Fontoura Solla
a305188e69 docs(B38): registra sidebar recolhido em tablet + fill-in de limitar o mapa a Bahia
Registro de roadmap-item de dois pedidos de produto de 2026-07-26, commitado
como estava na arvore de trabalho do checkout principal:

- B38 sidebar recolhido em tablet (`collapsible="offcanvas"`, trigger em `md+`,
  default por faixa de viewport com o cookie `sidebar_state` vencendo apos o
  toggle) + entrada no grafo de dependencias.
- Fill-in: limitar pan/zoom do mapa da Bahia a `BAHIA_BOUNDS` (`maxBounds` com
  folga de 10%, `maxBoundsViscosity: 1` e `minZoom` calculado no
  `ResizeObserver` que ja existe), so em `BahiaMap.tsx`.
- Nota de dependencia soft do B18 para o B38: com o rail recolhido, o submenu
  de filtros salvos precisa tratar collapsed como "abrir rail".
2026-07-26 18:05:51 -03:00
Francisco Ignácio Fontoura Solla
5ace1a04b2 simplify(B14): corrige o mecanismo da fronteira, estado unsupported e duplicatas
Terceira rodada de /simplify sobre o B14, com dois enunciados errados corrigidos:

- A propriedade da fronteira compartilhada NAO vem da ordem do `.find()`, e sim
  da convencao meio-aberta do ray casting (`point.lng < crossingLng`): a aresta
  pertence ao poligono cujo interior fica a leste (ao norte, se horizontal). Tres
  comentarios diziam o contrario, e o do unit test dizia "oeste" enquanto
  afirmava `east`.
- O plano dizia que o `Promise.all` esconde o decode da malha atras do fix de
  GPS. Nao esconde: o decode roda no topo do modulo, quando o chunk executa. O
  paralelo continua certo por outro motivo (decode ~2 ms; sequencial jogaria os
  42 KB no caminho critico quando o fix vem do cache).

Simplificacoes: `unsupported` virou estado proprio, entao `failureCopy` deixa de
ser anulavel e caem duas leituras defensivas inalcancaveis; "Voce esta em " era
escrito duas vezes (frase sr-only + JSX), o que anulava justamente a invariante
que o comentario alegava; `zoneCityHrefs` virou `Partial` porque a busca falha
para os 416 municipios sem zona e o guard existia sem o tipo exigir;
`MunicipalityForIbgeIndex` era duplicata campo-a-campo de `AccessibleMunicipality`;
`kmFormatter` duplicava `oneDecimalFormatter`; `RecentlyVisitedCard` carregava
tres props que nenhum caller passa (dez condicionais mortas); e o bbox de feature
estava escrito em dois specs (agora `tests/helpers/featureBounds.ts`, compartilhado
por vitest e Playwright).

Copy: fora da Bahia sem nada por perto dizia a mesma coisa em duas frases e nunca
dizia que voce estava fora do estado. Agora o headline afirma o fato e o detalhe
reusa "Nenhum municipio da sua carteira por perto.".

Tambem limpa um achado de knip herdado do main (`MunicipalityPoliticalTrendViewModel`
exportado sem uso) e registra no plano o caminho afiado do debito de payload RSC
mais dois adiados com gatilho.
2026-07-26 18:03:44 -03:00
Francisco Ignácio Fontoura Solla
e482005ce0 B14: municipio mais proximo (atalho geo no Inicio staff)
Card "Onde estou" no dashboard staff: point-in-polygon contra a malha
municipal que o painel do mapa ja carrega, sem artefato de centroides,
sem migration, sem collection, sem server action e sem Consent -- a
posicao e lida por navigator.geolocation e casada no browser, nunca sai
do aparelho.

- src/lib/municipalityProximity.ts: haversine, ray casting, centroide
  ponderado por area e uniao discriminada
  (inScope | zoneCity | outOfScope | outsideBahia)
- src/utilities/campaignGeolocation.ts: flag de sessao, leitura guardada
  de permissao e requestCurrentPosition com falha tipada
- Salvador nao ganha ZE chutada: link para a lista filtrada, com href
  serializado no servidor (importar o serializador canonico no cliente
  custava 21 kB de First Load JS)
- fallback fora da carteira ordenado por centroide, teto de 150 km
- fix grosseiro (>10 km) mostra o resultado com ressalva de precisao

/campanha First Load JS: 265 -> 268 kB.
2026-07-26 17:33:33 -03:00
Francisco Ignácio Fontoura Solla
f63cd2cd50 Merge branch 'cursor/2f22c6f6' 2026-07-26 16:28:12 -03:00
Francisco Ignácio Fontoura Solla
ca1a2081fe docs(B33): registra débito de engenharia do /simplify (B33+)
Fecha a entrega do B33 registrando o achado do /simplify que sobrou maior
que cleanup: StateDeputyFilters é o 3º call site do mesmo scaffold de
navegação/debounce de TerritoryFilters/MunicipalityFilters. Novo fill-in
B33+ (plano escala-dry-pos-b33.md) extrai useCampaignListFilterNavigation.
Agregado SQL do facet de partido formalizado como Adiado com gatilho no
plano-pai; referência cruzada obsoleta em escala-dry-pos-e10.md corrigida
(party é texto livre, não enum fechado — o gatilho ali não disparou).
2026-07-26 16:27:37 -03:00
Francisco Ignácio Fontoura Solla
8c55daffb2 B33: ordenação e filtro no header da lista de dobradinhas
- stateDeputyListUrl.ts / stateDeputyListFilters.ts: contrato de URL/filtro
  próprio (sort/dir, filtro de Partido multi + sentinela "Sem partido"),
  espelhando municipalityListUrl.ts (paginação/resolveListUrl) e
  territoryListUrl.ts (sort keys/labels)
- stateDeputyData.ts: where/sort resolvidos no banco + facet de partido
  (loadStateDeputyPartyFacet), select estreito na query principal
- StateDeputySortableHead / StateDeputyHeaderFilter / StateDeputyFilters:
  wrappers finos sobre o chrome compartilhado (shared/CampaignSortableHead,
  CampaignHeaderFilterPopover), paridade mobile incluída
- dobradinhas/page.tsx: redirect canônico em duas fases, CampaignTable
  sempre montada com caption + empty state, resumo de ordenação aria-live
- Correção pós-entrega (design review): "Sem partido" no mobile + caption
  da tabela + copy plural do empty state
- /simplify (1ª rodada): buildStateDeputyPartyOptions compartilhado, remove
  discriminante filterParam redundante, dedupe/if mortos em
  stateDeputyListUrl.ts
- /simplify (2ª rodada, pós-rebase): remove cast/type morto em
  stateDeputyData.ts, stateDeputyColumns usa StateDeputyFilterOption em vez
  de duplicar a forma, inlinea parsePartiesParam; extrai truncatedNamesLabel
  compartilhado em campaignListUrl.ts (3º call site — municipalityListFilters
  e territoryListFilters tinham a mesma função com nomes distintos)

Gate verde (tsc, lint, format, check:cycles, 396 testes, build); Aikido
0 achados nos 7 arquivos tocados nesta rodada. Docs:
docs/plans/ordenacao-filtros-lista-dobradinhas.md, docs/roadmap.md,
.cursor/rules/projects/nucleos-eleitorais.mdc.
2026-07-26 16:21:40 -03:00
Francisco Ignácio Fontoura Solla
a04f4dc191 Merge branch 'cursor/eb00cad1' 2026-07-26 15:33:34 -03:00
Francisco Ignácio Fontoura Solla
7cafc2297b docs(B28): registra débitos do /simplify no plano-pai (adiado com gatilho)
- botão WhatsApp por linha duplicado (2 call sites) — gatilho: 3º site
- copyText vs copyMessage em SupporterShareKit — gatilho: 3º site de copy-to-clipboard
2026-07-26 15:32:57 -03:00
Francisco Ignácio Fontoura Solla
9150092e17 B28: e-mail + celular na lista de lideranças
- colunas E-mail/Celular (copy-on-click, toast, "—" vazio) + coluna de
  Ações com WhatsApp (wa.me), no padrão de /campanha/assessores (B19)
- shared/CampaignCopyableCell.tsx nasce compartilhado já no 2º call site;
  AdvisorsTable.tsx migrado no mesmo PR para consumi-lo
- whatsAppHrefForPhone promovido a lib/phone.ts
- leadershipData.ts: contactSummary (+ email), loadLeadershipDetail
  deduplicado (reusa row.email)
- simplify: remove try/catch morto em whatsAppHrefForPhone, remove casts
  redundantes em contactSummary, displayEmail retorna string | null direto
- docs: nucleos-eleitorais.mdc, roadmap.md, plano B28 marcados entregues
2026-07-26 15:30:39 -03:00
Francisco Ignácio Fontoura Solla
5ab25228e3 Merge branch 'cursor/32508717' 2026-07-26 14:06:25 -03:00
Francisco Ignácio Fontoura Solla
b3283ee061 B24: auto-save e justificativa no popover de Tendência.
Remove o botão Salvar da lista de municípios; grava via POST political-trend sem revalidar a lista, com flush coalescido e feedback na célula.
2026-07-26 14:05:36 -03:00
Francisco Ignácio Fontoura Solla
e0d0936fab Merge branch 'cursor/0578e7e1' 2026-07-26 05:13:09 -03:00
Francisco Ignácio Fontoura Solla
66feecd66b simplify(B13): corrige classe agregada, bolhas em pane proprio e registra debitos
O /simplify da escala relativa do mapa achou um bug semantico real em
computeAggregateTerritorialClass — somava o headroom dos slugs e comparava
contra a mediana de UMA unidade, classificando um grupo grande como expansao
quando ele e marginal. A mediana agora escala por slugs.length, com teste.

As bolhas sairam do centro do bounding box para o centroide real e ganharam
pane proprio (z-index 450), porque o bringToFront do hover as escondia atras
do poligono. Legenda e pintura passaram a compartilhar o mesmo predicado,
a copy dos quantis parou de prometer faixas de igual largura, e o subtitulo
do painel parou de dizer "em 2026" com 2022 selecionado (defeito do B11).

Fonte unica onde havia duas: NO_DATA_FILL, o raio das bolhas e
formatPlacementOrdinal (ex-formatMunicipalityVoteRank, agora client-safe em
electionFormat.ts).

Os quatro debitos maiores que o cleanup viraram o fill-in B13+
(docs/plans/escala-dry-pos-b13.md); a flakiness dos e2e, reproduzida em main
limpo, foi para o ledger com a causa afinada.
2026-07-26 05:12:37 -03:00
Francisco Ignácio Fontoura Solla
29f45e093a B13: escala relativa no mapa dos Municipios (quantis/LQ/posicao) + artefato TSE v3
O coropleto do Inicio era quase monocromatico porque 0-100% dos validos comprime
uma disputa cujo topo estadual e 4,71%. Tres escalas relativas ao proprio
candidato, todas em classes discretas: quantis (novo default), padrao proprio
(LQ) e posicao no municipio, ao lado de "% dos validos" (B11) e "Total (votos)".

Artefato v3: federalRankByIbgeCode com a colocacao entre todos os candidatos com
votos > 0 na geografia (2014/2018/2022), chaveado por ibgeCode porque e o que o
mapa desenha. Fica no artefato commitado, nao em query de request, que varreria
~100 mil linhas por ano para um dado imutavel. 534 -> 623 KB (orcamento 700 KB).

LQ virou 5 classes nas ancoras do E10, nao diverging: o plano pedia as duas
coisas e se contradizia. Vermelho x azul continua significando "Solla x
adversario" e segue exclusivo do compare.

computeAggregateTerritorialClass classifica um grupo de slugs como um territorio
so, somando insumos e rodando o mesmo classificador (LQ e razao; a media de
razoes nao e a razao das somas) - o E12 herda o helper. TERRITORIAL_CLASS_ANCHORS
mudou para src/lib/territorialClassAnchors.ts para nao arrastar o artefato TSE
ao bundle do cliente.

Quantis sobre o escopo de acesso do usuario, degradando para 3 classes abaixo de
10 municipios com a legenda declarando a contagem efetiva. Rank indisponivel em
2026 (dado TSE); LQ em 2026 reusa o padrao estadual de 2022.

Bolhas opcionais por validos projetados (area proporcional ao valor, cor da
classe E10, interactive: false). 417 marcadores medidos antes de otimizar - nao
derrubaram o frame, entao o corte para top-K nao foi feito.

Sem migration, sem collection, sem server action; bundle client de /campanha
byte a byte igual.
2026-07-26 04:22:56 -03:00
Francisco Ignácio Fontoura Solla
b16d2c9831 Merge branch 'cursor/70386841' 2026-07-26 03:54:15 -03:00
Francisco Ignácio Fontoura Solla
2bbb630af6 simplify(B27): fix optimistic-state clobber, unify JSON route error shell, dedupe advisor id resolution
- MunicipalityListAdvisorsControl: stop advancing lastPropsIDsRef from a
  successful delta response — it exists only to detect a genuine external
  prop change, and writing it there made a later stale re-render of the
  pre-edit content look external and clobber optimistic state back to it.
  Also reconcile with the latest confirmed server set on the error path
  once every in-flight delta settles (previously only the success path did),
  and consolidate chip/tooltip name resolution onto the shared
  advisorEntriesFromIds helper instead of a bespoke lookup with an
  '#id' placeholder.
- Extract campaignJsonMutationErrorResponse/parseCampaignJsonRequestBody
  (src/utilities/campaignJsonMutationRoute.ts) and adopt them in both
  municipios/advisors/route.ts and municipios/expected-votes/route.ts,
  removing the duplicated body-parse + error-mapping ladder.
- Municipality.ts's validateMunicipalityAdvisors now uses
  uniqueRelationshipIds instead of re-inlining the Set+map+filter dance
  the helper was extracted for.

Verified: tsc --noEmit, lint, format:check, check:cycles all clean;
full unit suite (450 tests) and the municipality/advisor int suites pass.
knip's pre-existing payload.config.ts load error is unrelated (P3).
Playwright's spawned webServer failed to boot in this sandbox (unrelated
module-resolution environment issue, reproduced even before this diff);
manual pnpm dev boots fine and the change is covered by unit/int tests.
2026-07-26 03:51:32 -03:00
Francisco Ignácio Fontoura Solla
a4b62c1a9a fix(test): drop obsolete municipalitySlug/formAction props from MunicipalityListAdvisorsControl test after B27 rebase merge 2026-07-26 03:34:11 -03:00
Francisco Ignácio Fontoura Solla
7c50e29aa7 B27: combobox + chips + auto-save no popover de Assessores da lista de municípios
Replaces the checkbox-list + "Salvar assessores" button in the
Assessors popover (/campanha/municipios) with removable chips + a
Command combobox, delta-based auto-save (one advisor add/remove per
request, never the whole array), and optimistic UI with request
sequencing to resolve out-of-order responses correctly.

- New POST /campanha/municipios/advisors JSON route (mirrors
  expected-votes/), backed by setMunicipalityAdvisorMembership(Record)
  in actions/municipality.ts (text advisory lock + transaction).
- nextAdvisorIdsAfterMembership moved to
  src/lib/municipalityAdvisorMembership.ts with unit tests.
- Shared isSameOriginRequest (src/utilities/sameOriginRequest.ts) and
  uniqueRelationshipIds (src/utilities/relationship.ts) helpers,
  de-duplicating the same-origin CSRF check and the
  Set+map(relationshipId)+filter pattern across advisor call sites.
- MunicipalityListAdvisorsControl.tsx rewritten: chips, Command
  combobox, per-item pending affordance, rollback on error, and a
  request-sequence guard so only the most recently sent delta's
  response is adopted when concurrent optimistic updates settle.
- e2e coverage for the combobox auto-save flow; setup prewarms the new
  POST-only route to avoid Playwright dev-server HMR aborting the
  first request.
- Plan and roadmap marked as delivered (B27).
2026-07-26 03:31:51 -03:00
Francisco Ignácio Fontoura Solla
4b60e9f1aa Merge branch 'cursor/46b02119' 2026-07-26 03:25:21 -03:00
Francisco Ignácio Fontoura Solla
4211a548f6 docs: registra débitos do /simplify pós-B23 no plano (paridade a11y da nota de tendência, wrapper Popover+tooltip) 2026-07-26 03:24:55 -03:00
Francisco Ignácio Fontoura Solla
d4b7aab50e B23: tooltip de conteudo nas celulas Assessores/Tendencia de /campanha/municipios
- openOnTouch/disabled em CampaignHoverTooltip para conviver com Popover
  sem roubar o tap nem sobreviver atras dele; falsy content agora renderiza
  o trigger sem wrapper (colapsa o ternario nos dois consumidores)
- formatAdvisorNamesTooltip + advisorEntriesFromIds compartilhado
  (MunicipalityAdvisorAvatarStack) para dedupe entre lista e controle
- MunicipalityListAdvisorsControl: hover mostra nomes completos (coordenador)
- MunicipalityListTrendControl: hover mostra a justificativa (politicalTrend.note,
  E4R), com whitespace-pre-wrap preservado; leitura tambem dentro do Popover em toque
- testes unit dos wrappers/no-op e do helper

/simplify (pos-rebase): corrigido HEADER_TOOLTIP_COUNT (4->10) no teste da
celula de assessores, que ficou obsoleto depois do rebase absorver o B22
(10 tooltips de header no thead staff) - o teste passava a comparar 5 vs 11
sem essa correcao; formatAdvisorNamesTooltip agora tipa ReactElement | null
em vez de ReactNode, removendo os casts 'as React.ReactElement' no teste;
comentarios de baixa informacao removidos/enxugados em CampaignHoverTooltip
e nos dois controles; docs: plano, roadmap e notebook do projeto marcados
como entregues.

Gate verde: tsc, lint (0 warnings), format, 446 unit, build anterior verde.
Aikido: 0 findings.
2026-07-26 03:21:45 -03:00
Francisco Ignácio Fontoura Solla
18cd8cb3ae Merge branch 'cursor/d4bf7d32' 2026-07-26 02:59:35 -03:00
Francisco Ignácio Fontoura Solla
fb4ba831c8 B22: explicacao por coluna no header das listas — description em CampaignTableHead/MunicipalitySortableHead, campaignConceptOneLiner como fonte unica E18, municipalityColumnDescriptions cobrindo as 11 colunas de /campanha/municipios
/simplify: extrai campaignHoverExplanationClassName/campaignHoverTooltipAlign para CampaignHoverTooltip.tsx (elimina duplicacao entre CampaignTableHead, MunicipalitySortableHead e MunicipalityGoalAccountCard), troca non-null assertion de campaignConceptOneLiner por lookup em Record, corrige copy da coluna Assessores (coordenador, nao candidato) e restaura comentario de invariante do sort de goalCoverage
2026-07-26 02:58:54 -03:00
Francisco Ignácio Fontoura Solla
292df15b3f Merge branch 'cursor/c64aa441' 2026-07-25 22:38:58 -03:00
Francisco Ignácio Fontoura Solla
520613f862 E10+ registra os debitos do /simplify do E10 como fill-in de engenharia
Quatro fases num plano so (escala-dry-pos-e10): leitura unica da tabela na lista,
memo de processo dos potenciais por municipio, TooltipProvider unico no layout e
multi-filtros do mobile saindo das definitions.

A F1 nao e achado novo: o A11 ja tinha adiado a "hidratacao unica do sort derivado"
com gatilho "3o path derivado no loader" — com classe o loader chegou ao 4o
(votos, deficit, frescor, classe), entao o adiado do A11 fica riscado e apontando
para o item novo. Reforcada tambem a nota de gatilho do disclosure em
conta-da-cadeira.md (a classe entrou acima da dl, nao como 5a metrica).

Descartados no triage, com racional no plano: formatador de percentual proprio do
TerritoryListColumns, os tres caches do classificador e o ruido do knip com
payload.config.ts (ja ledgerado P3, e a evidencia da sessao contradiz).
2026-07-25 22:38:28 -03:00
Francisco Ignácio Fontoura Solla
0ef4ddac1d E10 /simplify: remove superficie morta, unifica o parser de enum repetivel e reusa o ranking A11
- campoVotes (sem leitor) e classification.fieldHeadroom (duplicava o fator field) removidos
- formatTerritorialClassWhy passa a ser mesmo a fonte unica do slice(0,2) e do fallback
  "sem serie", eliminando o sentinela de string vazia nas duas superficies de detalhe
- classe territorial vira um so componente com layout table|card, como os vizinhos
- parseExhaustiveEnumParam em campaignListUrl cobre trend e class (o includes era morto:
  allParamValues ja deduplica)
- bloco central caminha o computeVoteRankByYear memoizado do A11 em vez de reordenar
  os 435 slugs por conta propria
- comentarios corrigidos onde afirmavam o contrario do codigo (CampaignCellTooltip e a
  regra do sem_base por superficie)
2026-07-25 22:33:16 -03:00
Francisco Ignácio Fontoura Solla
63fe4431bf E10: classificacao territorial relativa (Reduto/Expansao/Manutencao/Marginal/Sem base) sobre o artefato TSE, com coluna+sort+filtro na lista, badge no card da conta da cadeira e na capa do dossie; seam generico cellTooltip na CampaignTable e promocao do CampaignHoverTooltip para shared 2026-07-25 21:59:20 -03:00
Francisco Ignácio Fontoura Solla
ac2a189bf2 Merge branch 'cursor/af9e470e' 2026-07-25 21:50:56 -03:00