Register B56–B57: home campaign summary and deferred 7-day delta.
This commit is contained in:
parent
60dcbb23c8
commit
431157ed95
3 changed files with 236 additions and 6 deletions
106
docs/plans/delta-7-dias-estimativa-inicio.md
Normal file
106
docs/plans/delta-7-dias-estimativa-inicio.md
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
# Delta de 7 dias do total de estimativas (Início)
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B57 — baixa prioridade / futuro)
|
||||
Impeccable: B — encaixe de seta + Δ no bloco **B56** (quando existir trajetória)
|
||||
Appetite: ~1,5–2 dias eng quando aberto; **snapshot diário** + reader + UI; migration leve
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` / `DESIGN.md` · bloco **B56** · tema `campaign`.
|
||||
|
||||
Na implementação (quando priorizado): craft compacto → critique → polish.
|
||||
|
||||
Brief compacto:
|
||||
|
||||
- **Persona / contexto:** CG na reunião semanal — “subimos ou caímos na semana?”
|
||||
- **Job principal:** ler o sinal do Δ ao lado do total sem abrir planilha.
|
||||
- **Estratégia de cor:** Restrained; ↑/↓ com contraste acessível (não só cor — também sinal/texto).
|
||||
- **Edit where you see:** não.
|
||||
- **Anti-goals:** sparkline multi-semana em v1; Δ de cobertura no mesmo gesto (pode vir depois); inventar histórico a partir de um único load.
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
- **Vou apresentar dados?** Sim — série temporal curta (2 pontos: agora vs T−7d) agregada.
|
||||
- **Decisões desbloqueadas:** Coordenador: “a projeção statewide/carteira moveu o suficiente esta semana para mudar a pauta da reunião / abrir A1 em lote?”
|
||||
- **Forma escolhida:** seta + número absoluto do Δ (pt-BR) ao lado do total B56 — degrau pobre. **Rejeitado:** chart; dual-axis; % do Δ como único sinal sem absoluto.
|
||||
- **Profile:** 1 escalar assinado (Δ do `staffVoteTotal` central); janela fixa 7 dias civis (Bahia).
|
||||
- **Anti-goals de dado:** sem reconstruir “quase” com versions incompletas e apresentar como verdade.
|
||||
|
||||
## Contexto
|
||||
|
||||
**B56** entrega o total vivo + cobertura E8. Não existe série de `municipality.expectedVotes` nem snapshot statewide; versions em `votePledge` (C12) existem mas **não** cobrem a metade da mesa do `staffVoteTotal`, e não há reader de ponto-no-tempo. E8 já adiou “delta semanal persistido” até existir trajetória. Produto (2026-07-29): o Δ é desejável, mas **caro demais agora** — registrar com **prioridade baixa / futuro**, sem competir com chassis UX-1 (B46–B55) nem com B56.
|
||||
|
||||
## Objetivos (quando priorizado)
|
||||
|
||||
- Persistir **um ponto por dia** (civil America/Bahia) do `staffVoteTotal` central **por escopo relevante** — no mínimo: total irrestrito (CG/candidato); decisão aberta se assessor precisa de série por ator ou só vê Δ statewide (recomendação abaixo).
|
||||
- Reader: valor em T₀ (vivo, como B56) − valor persistido mais próximo de T−7d (ou “—” se <7 dias de história / buraco).
|
||||
- UI: ao lado do número do **B56**, seta ↑/↓ (ou em dash se Δ≈0) + magnitude formatada; `aria-label` com direção e valor.
|
||||
- Sem Consent; migration da collection/tabela de snapshot.
|
||||
|
||||
## Decisões travadas (para quando abrir)
|
||||
|
||||
- **Prioridade baixa até o chassis UX-1 + B56 estabilizarem.** **Rejeitado:** furar fila de B46–B48 / soft 03/08 por este Δ.
|
||||
- **Métrica do Δ = mesmo hero do B56** (`staffVoteTotal` central). **Rejeitado:** Δ só de pledges (mente sobre a projeção da mesa); Δ de cobertura no lugar do total.
|
||||
- **Persistência = snapshot diário leve**, não event-sourcing de versions. **Rejeitado agora:** versionar `municipality.expectedVotes` (caro, vazaria em todo update); reconstruir só via `_vote_pledge_v` (incompleto p/ expectedVotes).
|
||||
- **i18n:** `campaignVoteSummarySnapshot` (ou nome final), `homeSummaryDelta`; copy “nos últimos 7 dias”.
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **Escopo do snapshot para assessor?** **Opções:** A só statewide (assessor vê Δ global) | B uma linha por `campaignUser` advisor no fim do dia | C sem Δ para assessor. **Recomendação:** A em v1 (uma série; assessor já vê total da carteira no B56 — Δ statewide é contexto). _(assumido — validar ao priorizar)_
|
||||
- **Escrita do snapshot: on-read (primeiro hit do dia) vs cron/job?** **Opções:** A first staff home/quadro load do dia | B script agendado | C afterChange em expectedVotes/pledge (N writes). **Recomendação:** A — zero infra nova; idempotente por `(date, scopeKey)`. _(assumido)_
|
||||
- **Retenção?** **Opções:** 30 | 90 | ilimitado. **Recomendação:** 30 dias (cabe o 7d + folga; prune no write). _(assumido)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Live["staffVoteTotal hoje"]
|
||||
Snap["snapshot T-7d"]
|
||||
Delta["Δ = live − snap"]
|
||||
UI["seta + número no B56"]
|
||||
Live --> Delta
|
||||
Snap --> Delta --> UI
|
||||
```
|
||||
|
||||
Componentes (quando aberto):
|
||||
|
||||
- **Collection ou tabela `campaignVoteSummarySnapshot`** (admin-hidden): `day` (date), `scopeKey` (`statewide` | …), `staffVoteTotalCentral` (number), unique `(day, scopeKey)`; access admin/staff-read; write só via utility interna com `overrideAccess` justificado **ou** Local API no path de load.
|
||||
- **`recordCampaignVoteSummarySnapshotIfNeeded`**: se não há row do dia civil BA, grava o total atual do escopo.
|
||||
- **`loadCampaignHomeSummaryDelta`**: lê live + row ~T−7d; devolve `{ delta: number | null }`.
|
||||
- **UI** em `CampaignHomeSummary`: slot ao lado do total.
|
||||
- **Migration:** `pnpm migrate:create add_campaign_vote_summary_snapshot` (nome final no PR).
|
||||
|
||||
## Dependências
|
||||
|
||||
- Dura: **B56** (slot UI + semântica do total). Soft: **E8 ✓** / **C12 ✓** (contexto; C12 versions **não** substituem o snapshot).
|
||||
- Não bloqueia B46–B55.
|
||||
|
||||
## Não escopo
|
||||
|
||||
- Bloco total + cobertura → **B56**.
|
||||
- Backtest pós-eleição / calibração → **E15**.
|
||||
- Δ de cobertura / por município / sparkline → adiado com gatilho abaixo.
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
- **“Só ler versions de pledge.”** Hero B56 inclui `expectedVotes` sem versão → Δ mentiria. **Mitigação:** snapshot do escalar já agregado.
|
||||
- **Snapshot por município × dia.** Explode armazenamento e UI. **Mitigação:** só o agregado statewide (e escopos extras só com evidência).
|
||||
- **Cron em Vercel Hobby.** **Mitigação:** preferir on-read idempotente.
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
- **Δ da cobertura E8 na mesma linha.** Revisitar quando o Δ do total estiver em uso ≥2 semanas e a reunião pedir o segundo número.
|
||||
- **Sparkline 4 semanas.** Revisitar se B57 v1 for usado e pedirem tendência visual.
|
||||
- **Versionar `expectedVotes`.** Só se auditoria por município exigir trajetória (não só o Δ statewide).
|
||||
|
||||
## Referências
|
||||
|
||||
- `docs/roadmap.md` (B57)
|
||||
- [resumo-campanha-inicio.md](resumo-campanha-inicio.md) (B56)
|
||||
- [conta-da-cadeira.md](conta-da-cadeira.md) — adiado “Delta semanal persistido”
|
||||
- [registro-fundacao.md](registro-fundacao.md) (C12) — versions de pledge ≠ série do hero
|
||||
- `src/utilities/campaignDashboardData.ts` / `votePledgeViews.ts`
|
||||
- AGENTS.md — migrations; `overrideAccess` com comentário se write interno
|
||||
115
docs/plans/resumo-campanha-inicio.md
Normal file
115
docs/plans/resumo-campanha-inicio.md
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# Resumo da campanha no Início (total + cobertura)
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B56)
|
||||
Impeccable: C — bloco KPI novo no Início (não é strip do Quadro)
|
||||
Appetite: ~1 dia eng; loader slim + componente tipográfico + Progress; sem migration
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` (Clarity under pressure; Intelligence serves organization — sem hero-metric SaaS) / `DESIGN.md` (Headline / tabular-nums) · tema `campaign`.
|
||||
|
||||
Na implementação: **shape → craft → critique → polish** (classe C).
|
||||
|
||||
Brief compacto:
|
||||
|
||||
- **Persona / contexto:** CG/assessor abre o Início e quer, sem rolar, o pulso estadual (carteira, no assessor) da estimativa e quanto está lastreado.
|
||||
- **Job principal:** “quanto estamos projetando?” + “quanto disso já está comprometido por lideranças?”
|
||||
- **Estratégia de cor:** Restrained; barra de cobertura usa `Progress` existente; sem vermelho decorativo no número.
|
||||
- **Edit where you see:** não — só leitura; edição continua na lista/Quadro/wizards.
|
||||
- **Anti-goals:** saudação; `CampaignMetricStrip` em cards no Início; seta/delta 7 dias (→ **B57**); gauge/donut; % estadual absoluto; leader vendo estimativas; segunda lib de charts.
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
- **Vou apresentar dados?** Sim, superfície neste item.
|
||||
- **Decisões desbloqueadas:**
|
||||
- Coordenador: “a conta estadual (ou da carteira) ainda fecha na ordem de grandeza da reunião — ou caiu/subiu o suficiente para abrir A1 hoje?”
|
||||
- Assessor: “minha carteira está lastreada ou a cobertura está baixa demais para o total que estou projetando?”
|
||||
- **Forma escolhida:** número tipográfico grande (não enorme) + linha secundária com % + barra — degrau mais pobre da escada. **Rejeitado:** strip multi-KPI do Quadro; sparkline; mapa no bloco; chart.
|
||||
- **Profile:** 2 escalares (total absoluto + ratio 0–1); granularidade = escopo do ator (435 ou carteira); cenário fixo `central`.
|
||||
- **Anti-goals de dado:** sem % dos válidos estaduais; sem vanity de cadastros; sem inventar denominador paralelo ao E8.
|
||||
|
||||
## Contexto
|
||||
|
||||
Início staff já monta ações (**B45 ✓**); thumb-zone/busca (**B46–B55**) compõem o chrome. O Quadro (`/campanha/quadro`) já agrega `staffVoteTotalByScenario` e `goalCoverage` via `getCampaignDashboardData` — mas o Início está sem briefing numérico. Pedido de produto (2026-07-29): no **topo** do conteúdo (sem saudação), total das estimativas +, abaixo e mais discreto, cobertura E8 com barra. Delta 7 dias fica em **B57** (baixa prioridade).
|
||||
|
||||
Gate fechado 2026-07-29: hero = `staffVoteTotal` central; cobertura = E8 (`comprometido ÷ meta`); layout = topo sem saudação; mobile mantém ações na thumb-zone (**B46**).
|
||||
|
||||
## Objetivos
|
||||
|
||||
- Bloco **`CampaignHomeSummary`** (nome final no PR) no Início **staff** (`coordinator` / `candidate` / `advisor`); **não** montar para `leader`.
|
||||
- **Número principal:** `rollupMunicipalityStaffVotes(…).staffVoteTotalByScenario.central` no escopo do ator (`loadMunicipalityScope`), formatado com `formatElectionNumber`.
|
||||
- Tipografia: grande mas não enorme — alvo craft ~`text-3xl` / Headline+; `tabular-nums`; sem card chrome pesado.
|
||||
- **Cobertura (discreta):** `goalCoverage.aggregateByScenario.central` — `%` via `formatGoalCoverageRatioLabel` + `Progress` com `goalCoverageProgressPercent`; copy curta tipo “Cobertura por lideranças” (craft fecha o rótulo; verbete E18 `cobertura-da-meta` como destino de “Saiba mais” opcional).
|
||||
- Loader slim (não puxar mapa/sugestões do Quadro): reusar `rollupMunicipalityStaffVotes` + `loadMunicipalityGoalCoverageBundle` (ou fatia mínima de `getCampaignDashboardData`).
|
||||
- No modo focado da busca (**B47**): o resumo some com o resto do chrome (contrato do B47).
|
||||
- Sem migration / collection / Consent / action de escrita.
|
||||
|
||||
## Decisões travadas
|
||||
|
||||
- **Hero = staff vote total `central`.** `expectedVotes[S] ?? Σ pledges efetivos` por município, depois Σ no escopo — mesmo contrato do Quadro. **Rejeitado:** só Σ `expectedVotes` (zera municípios sem planilha); Σ meta E8 como hero (confunde “projeção da mesa” com “meta da conta”).
|
||||
- **Cobertura = E8 OMTM.** `meta = expectedVotes ?? suggestedGoal`; `comprometido = Σ pledges efetivos`; ratio = comprometido ÷ meta. **Rejeitado:** declared ÷ hero (segunda métrica); só `declaredVotes` no numerador (quebra paridade com Quadro/lista).
|
||||
- **Topo do conteúdo, sem saudação.** **Rejeitado:** linha “Olá, …” (produto 2026-07-29); resumo só na 2ª dobra (KPI some do ritual diário).
|
||||
- **Staff-only; advisor no escopo da carteira.** **Rejeitado:** número estadual irrestrito para assessor.
|
||||
- **Sem seta/delta nesta fatia.** Slot visual do delta = **B57**. **Rejeitado:** inventar Δ com `Date.now()` / último load em memória.
|
||||
- **i18n:** `CampaignHomeSummary`, `homeSummaryVotes`, `homeSummaryCoverage`; copy pt-BR.
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **Rótulo do número (“Votos estimados” vs “Projeção” vs “Total na média”)?** **Opções:** A “Votos estimados” | B “Projeção (média)” | C só o número sem label. **Recomendação:** A + cenário implícito (central) — alinha ao vocabulário da mesa; B se critique achar “estimados” ambíguo vs pledges. _(assumido)_
|
||||
- **Link “Ver no Quadro” / lista por déficit?** **Opções:** A sem link (só número) | B link discreto para `/campanha/quadro` | C link para `?sort=deficit`. **Recomendação:** A em v1; B se critique pedir âncora. _(assumido)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Page["/campanha page.tsx staff"]
|
||||
Load["loadCampaignHomeSummary<br/>scope + rollup + coverage"]
|
||||
UI["CampaignHomeSummary"]
|
||||
Page --> Load --> UI
|
||||
UI --> Total["text-3xl tabular-nums"]
|
||||
UI --> Cov["% + Progress"]
|
||||
```
|
||||
|
||||
Componentes:
|
||||
|
||||
- **`loadCampaignHomeSummary`** (`utilities/` — p.ex. `campaignHomeSummaryData.ts`, `server-only`): `getCampaignUser` / actor já na page; `loadMunicipalityScope` + `aggregateAllPledgesByMunicipality` (ou o caminho que o dashboard já usa) → `rollupMunicipalityStaffVotes` + `loadMunicipalityGoalCoverageBundle`; devolver `{ staffVoteTotalCentral, goalCoverage }` (só `central`). `overrideAccess: false` com `user`.
|
||||
- **`CampaignHomeSummary.tsx`** (`components/campaign/dashboard/`): RSC ou server-friendly; `formatElectionNumber`; `Progress`; sem import de catálogo/Bahia no client.
|
||||
- **Wire-up** em `page.tsx`: acima de ações/busca na ordem de leitura; no mobile o **B46** continua ancorando a strip embaixo — o resumo fica no fluxo superior do scroll.
|
||||
- **Migration:** Sem migration, sem collection, sem server action.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Duras: **E8 ✓** (fórmulas), **B43 ✓** / **B45 ✓** (Início staff com chrome). Soft: **B46** (ordem thumb-zone vs conteúdo); **B47** (ocultar no modo focado).
|
||||
- Desbloqueia slot visual de **B57** (delta).
|
||||
|
||||
## Não escopo
|
||||
|
||||
- Delta 7 dias / seta → **B57**.
|
||||
- Mapa, sugestões, prioritários, “Onde estou” de volta ao Início → Quadro / itens próprios.
|
||||
- Seletor de cenário no Início → fica no Quadro/lista.
|
||||
- Wizards UX-1 → fatias seguintes.
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
- **Reusar `CampaignDashboard` inteiro no Início.** Arrasta mapa/Suspense/sugestões e contradiz B43. **Mitigação:** loader slim + componente próprio.
|
||||
- **Hero-metric SaaS / card grid.** **Mitigação:** tipografia + uma barra; sem `CampaignMetricStrip` de 3–4 células.
|
||||
- **Snapshot/Δ “só um pouquinho”.** **Mitigação:** explícito em B57; aqui mostrar só o presente.
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
- **Delta 7 dias ao lado do número.** Revisitar em **B57** (prioridade baixa; produto 2026-07-29).
|
||||
- **Saudação.** Revisitar só se sessão de campo pedir nome/contexto emocional — hoje cortada.
|
||||
|
||||
## Referências
|
||||
|
||||
- `docs/roadmap.md` (Trilha B / UX-1)
|
||||
- `src/utilities/campaignDashboardData.ts` — precedente de rollup + coverage
|
||||
- `src/lib/voteEstimate.ts` / `src/utilities/votePledgeViews.ts` — `rollupMunicipalityStaffVotes`
|
||||
- `src/utilities/municipality/goalCoverage.ts` — labels + `goalCoverageProgressPercent`
|
||||
- `src/components/campaign/shared/CampaignMetricStrip.tsx` — o que **não** copiar como layout do Início
|
||||
- `src/app/(campaign)/campanha/(app)/page.tsx` — mount atual
|
||||
- `docs/plans/conta-da-cadeira.md` (E8) — semântica de cobertura
|
||||
- `PRODUCT.md` / `DESIGN.md` — anti hero-metric; tipografia
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue