Commit graph

214 commits

Author SHA1 Message Date
Francisco Ignácio Fontoura Solla
cf6ae6c47c Pass 2 W2: correcoes de fronteira lib/utilities e client boundary
- helpers puros movidos utilities->lib: slug, phone, wordStartFilter, voteEstimate (9 inversoes lib->utilities zeradas)
- chaves de consent em src/lib/campaignConsentKeys.ts (client-safe); campaignInvite/campaignConsent re-exportam; onda0ConsentTexts referencia as constantes
- 'server-only' em 21 loaders Payload/cache (CLI segue com --conditions=react-server)
- ciclo de tipos supporterListFilters <-> supporterUi quebrado (SupporterListFilterInput estrutural; sqlFilters idem)
- votePledgeData dividido: votePledgeViews (puro, client-safe) + votePledgeData (loaders, server-only) - padrao contract-module
- predicados de role client-safe em src/lib/campaignRoles.ts; access/shared deriva deles (fonte unica); nav.ts para de importar o barrel campaignAccess (casts 'as CampaignUser' eliminados)
2026-07-25 14:38:41 -03:00
Francisco Ignácio Fontoura Solla
23e1f1e83d Pass 2 W1-D2: migra tripé de entidades, demandas, apoiadores, planos e assessores para o sistema de listas
- liderancas/organizacoes/dobradinhas/demandas: CampaignTable + column defs + CampaignListFooter + CampaignListEmptyState; parsers locais de first/page substituidos pelos helpers de campaignListUrl (fecha C8 F3); buildXListHref por modulo (URLs byte-identicas)
- SupporterList: tabela desktop em column defs (cards mobile intactos); apoiadores adota footer + empty state compartilhados
- planos: cards continuam (dominio de cards); adota footer + empty state
- assessores: tabela client bespoke (edit-where-you-see); pagina adota CampaignListFooter; empty state compartilhado no AdvisorsTable
- fora por decisao D5: TerritoryOverviewTable (client-sort, dataset <=27), comparacao de candidatos, LeaderContacts, preview de import
2026-07-25 14:28:01 -03:00
Francisco Ignácio Fontoura Solla
03bf54dea2 Pass 2 W1-D1: sistema de listas (column defs + CampaignTable) + split do municipalityUi + B16+
- CampaignTable/CampaignTableHead: colunas como dado (id, head island, cell, seams B17 mandatory/defaultVisible); colSpan derivado
- CampaignListFooter: contagem + paginacao compartilhadas
- MunicipalityList reescrita sobre column defs; cards mobile inalterados
- municipalityUi.ts (765/59 exports) dividido: municipalityListUrl / municipalityListFilters / municipalityLabels / municipalitySignal; contrato de URL byte-identico (pins verdes)
- B16+ absorvido: useOptimistic no HeaderFilter (onNavigate dentro da transition), hrefs de opcao via serializador canonico confiavel (1 serializacao por opcao, pin de byte-igualdade), facet de municipio como slugs rotulados no cliente (-20KB), facets no mesmo Promise.all (seed por promise), catalogo DRY, clearMunicipalityListFilters
- timeout explicito no int test de facets (2 bundles full-scope sob suite 47-way)
2026-07-25 14:13:04 -03:00
Francisco Ignácio Fontoura Solla
dc9a39f800 Pass 2 W0: pina contratos de URL e loaders das listas + truth-up TESTING/TECH-DEBT
- unit: campaignListUrl (canonicalize/clamp/redirect), supporterUi, actionPlanUi (matriz tab->where), 5 parsers de entidade
- int: campaignEntityListData smokes (escopo por role, busca por marker, shape de paginacao)
- fixtures: suporte a stateDeputy (create + cleanup)
- docs: contagens reais (45/47/7), Praca ~96 hits, conflito C8 F4 resolvido (10 ladders restantes -> W4d)
2026-07-25 13:30:39 -03:00
Francisco Ignácio Fontoura Solla
3812d81261 Pass 2: materializa tracker e planos por workstream (W0-W5, D1-D6) 2026-07-25 13:15:30 -03:00
Francisco Ignácio Fontoura Solla
c2a3d73702 Merge branch 'cursor/5274eb5f' 2026-07-25 03:44:44 -03:00
Francisco Ignácio Fontoura Solla
8430bd0a9f B16: corrige filtros que se anulavam e registra os débitos pós-simplify
A revisão paralela do /simplify achou três defeitos funcionais na entrega
dos filtros no header, todos corrigidos aqui:

- as facetas omitiam só o param multi, então "Prioritária" escondia os
  municípios não-prioritários do popover onde o próprio check mora e
  "Sem assessor" esvaziava o popover de Assessores; cada faceta passa a
  omitir o grupo de params do seu popover (int test cobre os dois casos);
- "todas as tendências" tinha duas codificações — as três marcadas
  deixavam trend= inútil na URL com o funil se declarando inativo; o
  parse agora canonicaliza o conjunto completo para ausente;
- o filtro de Território havia sumido do mobile ao virar multi.

Também devolve o "Limpar" aos popovers curtos (estava preso ao limiar da
busca) e registra o que sobrou como B16+ (escala/DRY), com o
CampaignListEmptyState compartilhado e o firstValue duplicado absorvidos
no C8, e a fragilidade do build sem o global metadata no site público.
2026-07-25 03:44:25 -03:00
Francisco Ignácio Fontoura Solla
96bf362421 B16: Filtros no header da lista de municípios
Popover de filtro por coluna no desktop (multi-seleção OR em município,
território, assessores e tendência; busca acento-insensível e "Limpar"),
mantendo os selects empilhados no mobile. Header sticky com a página como
scroller e empty state que substitui só as linhas de conteúdo, preservando
overview e filtros. Remove a coluna "Assessoria": a ordenação foi para
"Assessores" e o com/sem assessor virou opção do mesmo popover.
2026-07-25 03:13:28 -03:00
Francisco Ignácio Fontoura Solla
bbed0eccd5 Merge branch 'cursor/6400d68f' 2026-07-25 02:43:48 -03:00
Francisco Ignácio Fontoura Solla
2b4f62380e B19: registra gatilho de defer do N+1 no batch de território/ZE
Achado do /simplify pós-entrega (findByID por município ao atribuir um
território de identidade ou zona eleitoral inteira). Sem precedente para
o fix natural (payload.find com id: { in }) e mudaria semântica de erro
para IDs inexistentes; fica como gatilho no plano em vez de item de
roadmap.
2026-07-25 02:43:26 -03:00
Francisco Ignácio Fontoura Solla
01e52834e2 B19: Gerenciar assessores (/campanha/assessores)
Lista+detalhe única de edição para coordinator/candidate: nome, e-mail e
celular com auto-save debounced (edit-where-you-see-it); carteira de
municípios por chips com agregação de território de identidade,
adição/remoção com UI otimista; criação inline no topo da tabela; ação de
WhatsApp por assessor; reset de senha via e-mail (bloqueado para
placeholders @planilha.invalid). Alinha access de update/phone/atribuição
de município a isCampaignUnrestricted (coordinator + candidate).

Sem migration. Testes int (campaignAdvisorManagement) + unit (schema e
portfólio de municípios) cobrindo access, conflito de e-mail único, cap de
assessores por município e agregação por território/ZE.
2026-07-25 02:40:47 -03:00
Francisco Ignácio Fontoura Solla
4b7f2f595d Merge branch 'cursor/4c2433dd' 2026-07-25 02:30:36 -03:00
Francisco Ignácio Fontoura Solla
ef8dae7fb2 Fix stale computeGoalCoverage call left by main's E18 test file
The rebase auto-merged tests/unit/goalCoverage.unit.spec.ts against
origin/main without a conflict, but one assertion in the merged file
still called computeGoalCoverage with a raw number for the suggested
goal, from before this branch's Fase 0 (SuggestedGoalByScenario). Wrap
it in the file's own suggested() helper, matching every other call
site in the file. Caught by the pre-push tsc gate.
2026-07-25 02:30:09 -03:00
Francisco Ignácio Fontoura Solla
31fcc20ffd E9 /simplify: consolidate duplicated helpers, dead passthrough, comment drift
Cross-checked by parallel code-quality/performance/reuse reviewers against
the E9 commit. Behavior-preserving:

- laterTimestamp/resolveMunicipalityLastSignalAt were the same "latest of
  two ISO timestamps" written twice; both now delegate to a new
  latestIsoTimestamp in campaignTime.ts, which documents the fixed-width
  UTC invariant that makes the string compare sound.
- Hoisted the frescor comparator's Date.now() out of the per-comparison
  accessor so the sort key stops drifting mid-sort.
- Deleted the unread baseTotal/growthFactor/belowBase passthrough from the
  statewide goal entry point and fixed a comment citing a symbol
  (goalDecompositionBelowBase) that never existed; renamed
  computeStatewideGoalDecomposition -> computeStatewideSuggestedGoals now
  that nothing is proportionally decomposed.
- Collapsed the advisor badge's two copies (desktop table vs mobile card)
  into one AdvisorStatusBadge; they had quietly diverged on which icon
  renders for the "no advisor" state.
- Renamed the sort-menu's stale "Cobertura" label for advisor coverage to
  "Assessoria" (E8 already renamed the list column for the same reason);
  derived the sort-key set from the label record instead of a third
  parallel list.
- Consolidated three zeroed-scenario-record literals into one
  zeroByVoteEstimateScenario() factory in voteEstimate.ts.
- Routed three separate votesByYear[2022] reads through one votesInYear
  helper in municipalityPotential.ts.
- Gated the goal card's empty-state copy on !usesMesaEstimate so it stops
  telling a município with an existing mesa estimate to enter one.
- Swapped a test assertion pinned to a raw Tailwind token for a stable
  data-signal="cold" hook, and tightened the "desert" test's bound to
  check against the actual growth factor (the old bound was true for
  every slug regardless of the formula).

Left alone (roadmap decision, not cleanup): sanityCheckSuggestedGoalsByTerritory
has no production caller yet (deferred to E11/E12); comment shortened
instead of deleting the dead-but-documented code.

Verified: tsc --noEmit, pnpm lint (0 warnings), 337 unit + 366 integration
tests, pnpm build all green.
2026-07-25 02:24:18 -03:00
Francisco Ignácio Fontoura Solla
51c13153fc E9: Fila de alocação (déficit como default do staff, frescor, coluna da vergonha)
A lista de municípios vira a fila de decisão, sem rota nova, sem coluna nova
e sem migration.

Fase 0 corrige a meta sintética do E8, que repartia a meta estadual
proporcionalmente só ao teto do campo e ignorava a votação própria (meta 2.911
para Vitória da Conquista, de 5.005 votos em 2022; 813 para Campo Formoso, de
47). `deriveSuggestedGoalsByScenario` substitui `decomposeStateGoal` e ancora a
meta na votação de 2022 por cenário — pessimista = base × (1 − margin), central
= base, otimista = base × (stateGoal ÷ Σ base), com clamp. `goalCoverage` passa
a receber `SuggestedGoalByScenario`, deixando `expectedVotes[S] ??
suggestedGoal[S]` simétrico. Só descrições do global mudaram.

Fase 1 adiciona frescor e ordenações: `declaredAt`/`estimatedAt` no select do
agregado de pledges (`lastPledgeAt`), `lastSignalAt` no view model via
`resolveMunicipalityLastSignalAt` (mesmo helper para o comparador do servidor e
a célula), e as sort keys `deficit` (novo default do staff) e `frescor`,
derivadas em memória no cenário central.

Fase 2 dobra os sinais nas células existentes: "há N dias" na coluna de última
atualização (frio ≥ 21 dias, em âmbar — vermelho já é da prioridade), badge
"sem responsável" em prioridade alta sem assessor, coluna da vergonha no
detalhe da métrica de assessoria, tooltip de cobertura nomeando o cenário e
copy Praças→Municípios.

Cortes: votos em jogo → B13, LQ/captura → E10, coluna dedicada de déficit
desnecessária.
2026-07-25 02:24:17 -03:00
Francisco Ignácio Fontoura Solla
0cc91ac344 Merge branch 'cursor/c6ecf9ef' 2026-07-25 01:40:00 -03:00
Francisco Ignácio Fontoura Solla
9443a4e1f6 E18: documentação de conceitos de inteligência de campanha
Nova rota staff-only /campanha/conceitos (glossário curado em
src/lib/campaignIntelligenceConcepts.ts, revisado junto das fórmulas
que descreve em municipalityPotential.ts/goalCoverage.ts) com
descoberta por "Saiba mais" nos tooltips do card "Conta da cadeira" e
entrada fixa no rodapé da sidebar (staffSecondaryNav). Corrige defeito
E8 de formatElectionNumber (meta fracionária lida como cem mil em
pt-BR). Ajustes pós-/simplify: MetricExplanation.conceptID obrigatório,
href da sidebar desacoplado do módulo de conteúdo (client bundle).
2026-07-25 01:37:37 -03:00
Francisco Ignácio Fontoura Solla
bb6a321082 Merge branch 'cursor/5f01d520' 2026-07-25 01:28:24 -03:00
Francisco Ignácio Fontoura Solla
2682078fe4 E16: Dossiê do município (pré-agenda) with print view and budgetNotes.
Composes existing loaders into a staff-only pre-visit tab, adds G11 manual emendas notes, and unlocks CSS print for 1–2 A4 pages.
2026-07-25 01:28:03 -03:00
Francisco Ignácio Fontoura Solla
5cdd1027f4 Merge branch 'cursor/5930b0db' 2026-07-24 22:58:00 -03:00
Francisco Ignácio Fontoura Solla
3250300086 Anota gatilho de defer pós-critique no plano do E8
capture-review-debts (auto-confirm) triageou os achados restantes do
/impeccable critique de MunicipalityGoalAccountCard: os 3×P1+P2+P3
confirmados já foram fechados nesta sessão; o resto (naming, redesenho
hipotético de Captura/Share intracampo, disclosure não assentado para
futuras métricas) é cosmético ou prematuro — anotado como defer+gatilho
em "Adiado com gatilho" em vez de virar item novo no roadmap.
2026-07-24 22:57:40 -03:00
Francisco Ignácio Fontoura Solla
717d584f6d E8 follow-up: touch-friendly metric tooltips, 2014/2018 majoritarian seed, E18 docs plan
- MunicipalityHoverTooltip/MunicipalityGoalAccountCard: hover/focus tooltips on
  the 4 "Conta da cadeira" diagnostic numbers, hardened per /impeccable
  critique (touch dead-end fix via controlled open + pointerup toggle +
  outside-tap dismiss, tooltip no longer covers "Cobertura da meta", clearer
  two-tier copy, distinguishable button trigger, thicker 0% progress bar).
- seed-tse-results.mjs/electionResultsBuild/electionResultsParse: seed also
  imports presidente/governador for 2014/2018 (previously deputado federal
  only), with vote-em-transito duplicate merging so the unique constraints
  hold; artifact/formulas stay 2022-only by design.
- Registered E18 (documentacao-conceitos-campanha) roadmap item/plan for a
  future /campanha/conceitos glossary page.
2026-07-24 22:55:51 -03:00
Francisco Ignácio Fontoura Solla
5a4a9033ed E8: Conta da cadeira (metas derivadas, potencial por município, cobertura)
- campoParties.ts + committed election-aggregates artifact v2 (campo federal
  votes, federal tally, 2022 majoritarian presidente/governador)
- global campaignGoals (state goal + margin) with staff-only read /
  unrestricted-write access
- municipalityPotential.ts (teto do campo, captura, share intracampo,
  roll-off, proportional state-goal decomposition) and goalCoverage.ts
  (meta vs comprometido semantics locked this session)
- UI: dashboard + list overview 'Cobertura da meta' metric, new list column,
  detail page 'Conta da cadeira' card
- /simplify pass: dropped dead per-request TI-warning computation, O(1)
  potential lookup, deduped near-identical election loaders/tally types/
  percent formatters
2026-07-24 22:54:27 -03:00
Francisco Ignácio Fontoura Solla
7ac099be42 Register B19 plan for managing advisors in /campanha.
Gives Coordenador Geral and Candidato a path to create and maintain advisor accounts without Payload admin during Onda 0 onboarding.
2026-07-24 20:15:03 -03:00
Francisco Ignácio Fontoura Solla
86ae32ce98 Merge branch 'cursor/5176346d' 2026-07-24 19:27:35 -03:00
Francisco Ignácio Fontoura Solla
63910014ad Implement C12 campaign foundation records
Preserve pledge history, type field signals, link plan demands, and record immutable ex-ante allocation decisions.
2026-07-24 19:27:15 -03:00
Francisco Ignácio Fontoura Solla
12d7de4cf1 Register B16–B18 municipality list UX plans and mark column reorder out of scope.
Capture header filters, column visibility, and named saved filters (sidebar shortcuts) in the roadmap, and document why drag-and-drop column reorder stays deferred.
2026-07-24 19:14:01 -03:00
Francisco Ignácio Fontoura Solla
d73a644d72 Merge branch 'cursor/4ba21740' 2026-07-24 18:41:52 -03:00
Francisco Ignácio Fontoura Solla
166e1398a3 Document A11/B15 post-simplify deferrals after rebase onto B15. 2026-07-24 18:41:35 -03:00
Francisco Ignácio Fontoura Solla
3c95bc6174 A11: show municipality 2022 vote rank/share and default list sort by votos.
Dense catalog rank plus share of statewide votes on the baseline card and list column (header label 2022), with URL sort/dir and Impeccable polish.
2026-07-24 18:40:56 -03:00
Francisco Ignácio Fontoura Solla
4b457de521 Merge branch 'cursor/f24f6110' — B15 sortable municipality list headers 2026-07-24 18:27:41 -03:00
Francisco Ignácio Fontoura Solla
aa94443008 B15: sortable municipality list headers with URL sort/dir state and /simplify cleanup 2026-07-24 18:27:00 -03:00
Francisco Ignácio Fontoura Solla
4cda9ad86a Register fill-in to replace the priority badge with an icon+tooltip on the municipalities list. 2026-07-24 18:25:36 -03:00
Francisco Ignácio Fontoura Solla
6523824b3a Merge branch 'cursor/3e72ba50' — E17 tabela TI no Início 2026-07-24 17:17:48 -03:00
Francisco Ignácio Fontoura Solla
b6e5d8be57 Add comparative Identity Territory table to campaign dashboard (E17)
First regional surface in /campanha: a sortable 27-TI overview table on the
Início staff dashboard, next to the municipal map. Per-TI aggregates only
(sums and ratios of aggregates — MAUP safeguard); Metropolitano decomposed
into Salvador (19 zones) vs. demais RMS. Loader is server-only with
overrideAccess:true (regional context, not management; TI-level exposure
only); pure rollup stays client-safe for the sortable table. First slice of
E12, which extends with capture/LQ/meta coverage.
2026-07-24 17:17:31 -03:00
Francisco Ignácio Fontoura Solla
fe90cb7450 update 2026-07-24 16:13:13 -03:00
Francisco Ignácio Fontoura Solla
b64ebc13fa Record E4R fase 2 production seed in the project notebook. 2026-07-24 16:06:41 -03:00
Francisco Ignácio Fontoura Solla
a2964090e7 Merge branch 'cursor/projecao-strategy-columns' 2026-07-24 15:58:16 -03:00
Francisco Ignácio Fontoura Solla
0407f02ce2 Import projection-sheet strategy columns into municipalities (E4R fase 2)
- pnpm db:seed:projecao now also imports SITUAÇÃO -> politicalTrend,
  DOBRADINHAS -> stateDeputy + stateDeputies + dobradinhaNotes,
  LIDERANÇAS -> name-only contact + leadership (statewide slug dedupe),
  ASSESSOR RESPONSÁVEL -> campaignUser + advisors (Edizio=coordinator,
  Solla=candidate, placeholder @planilha.invalid emails),
  ENCAMINHAMENTOS -> nextSteps, OBSERVAÇÃO -> strengths/risks (curated).
  PRIORITÁRIAS overrides MAPA GERAL when non-empty; Salvador stays skipped;
  relationships are unions; re-run is idempotent.
- parseSituationCell + splitNameCell (noise/collective/action filters,
  honorific protection, parenthetical extraction) with unit tests.
- Contact.phone optional (migration 20260724_175500_contact_phone_optional);
  phone invariant hooks skip phone-less contacts; invite flows guard.
- candidate role now eligible for municipality/action-plan advisors
  (eligibleCampaignStaffWhere) per product decision.
2026-07-24 15:58:06 -03:00
Francisco Ignácio Fontoura Solla
c2a1570b0c Merge branch 'cursor/ddbd380e' 2026-07-24 14:11:48 -03:00
Francisco Ignácio Fontoura Solla
df0acc6500 Register B15 column-header sort on the municipalities list. 2026-07-24 14:11:34 -03:00
Francisco Ignácio Fontoura Solla
39cbce8d79 Merge branch 'cursor/63b4db6b' 2026-07-24 14:08:40 -03:00
Francisco Ignácio Fontoura Solla
583a6099c2 Register fill-in to move the Cenário selector next to municipality list filters. 2026-07-24 14:08:28 -03:00
Francisco Ignácio Fontoura Solla
c07969f4a7 Remove duplicated voteGoals; seed projection sheet into expectedVotes.
Meta Bom/Regular/Mínimo duplicated the estimate scenarios — keep a single municipality series (pessimista/média/otimista), backfill then drop the old columns, and map the spreadsheet onto expectedVotes.
2026-07-24 14:04:44 -03:00
Francisco Ignácio Fontoura Solla
d14ed67801 Ship E4R: always-overwrite seed of projection spreadsheet into voteGoals and priority.
One-shot CLI (`pnpm db:seed:projecao`) matches MAPA GERAL to municipalities, skips Salvador, and re-seeds when a newer sheet arrives; dry-run + docs/roadmap updated.
2026-07-24 13:13:46 -03:00
Francisco Ignácio Fontoura Solla
cc0087fd5f Merge branch 'feat/b14-municipio-mais-proximo' 2026-07-24 12:45:28 -03:00
Francisco Ignácio Fontoura Solla
2be41d4f59 Register B14 nearest-municipality geolocation shortcut on the campaign home.
Adds the roadmap item and plan for a staff-only, client-side proximity CTA with a once-per-session location prompt.
2026-07-24 12:45:18 -03:00
Francisco Ignácio Fontoura Solla
e468f4192e Sync roadmap and intel plans with the 2026-07-23 field session; add E4R/A11/E16/E17 plans 2026-07-24 02:54:28 -03:00
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
Francisco Ignácio Fontoura Solla
8712ae9181 Merge branch 'feat/engineering-standards-hardening' 2026-07-24 01:08:54 -03:00