mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
Fill-in: move estimate scenario into municipality list filters.
Cenário lives in MunicipalityFilters with provider wrapping filters and results; roadmap and plan marked delivered.
This commit is contained in:
parent
a166956276
commit
d04f23399f
8 changed files with 89 additions and 45 deletions
|
|
@ -7,6 +7,7 @@ alwaysApply: false
|
|||
|
||||
## Status
|
||||
|
||||
- **Fill-in Cenário junto aos filtros entregue em código (2026-07-29):** em `/campanha/municipios` (staff), o seletor **Cenário** (`VoteEstimateScenarioField`) saiu do header do `MunicipalityListOverview` e entrou na barra slim de `MunicipalityFilters` (desktop: antes de Salvar/Limpar; mobile: após filtros staff, antes de Ordenar). `MunicipalityEstimateScenarioProvider` passou a envolver filtros + resultados; cenário continua estado local (fora da URL e do **Limpar**). Hint `VOTE_ESTIMATE_SCENARIO_FILTERS_HINT`. Sem migration/action/Consent. Plano: [`cenario-junto-filtros-municipios.md`](../../../docs/plans/cenario-junto-filtros-municipios.md).
|
||||
- **B46 entregue em código (2026-07-29):** thumb-zone do Início (UX-1 chassis) — `CampaignHomeLayout` ancora `CampaignHomeActions` na metade inferior do viewport em mobile (`min-h-full` no shell + spacer `flex-1` acima da strip; clearance existente `pb-24` + bottom nav fixa, sem `position: fixed` na strip) e no topo em `md+` (`md:min-h-0`). Slots `data-slot="home-actions"` e opcional `searchSlot` → `data-slot="home-search"` (ordem documento mobile ações→busca; desktop busca→ações via `order-1`/`order-2`/`order-3`) para **B47**. Wire em `campanha/(app)/page.tsx`. Unit `campaignHomeLayout.unit.spec.ts`; e2e `campaignHomeActions` verde. Sem migration. Plano: [`posicao-botoes-acao-inicio-thumb-zone.md`](../../../docs/plans/posicao-botoes-acao-inicio-thumb-zone.md). Próximo: **B47** busca global.
|
||||
- **B45 entregue em código (2026-07-29):** catálogo de ações do Início por persona (UX-1 chassis) — `src/lib/campaignHomeActions.ts` (`homeActionsForRole`, `toHomeActionButtonProps`) + ilha client `CampaignHomeActions` (ícones Lucide no client; RSC passa `role` + `uncoveredMunicipalitiesHref` de `buildMunicipalityListHref`). Staff (coordinator/candidate/advisor): 6 ações; `uncovered-municipalities` → `?coverage=sem_assessor&sort=votos`; demais inertes (`cursor-default`). Leader: 2 ações; `my-contacts` → `/campanha/contatos`. Seção "O que você quer fazer?" acima da strip B44. Unit `campaignHomeActions.unit.spec.ts`; e2e `campaignHomeActions.e2e.spec.ts`. Sem migration. Plano: [`catalogo-acoes-inicio-por-persona.md`](../../../docs/plans/catalogo-acoes-inicio-por-persona.md).
|
||||
- **B46–B55 registrados no roadmap (2026-07-29):** ~~thumb-zone do Início (**B46**)~~ entregue; busca global fatiada (**B47** input → **B48** Municípios/TIs → **B49–B53** outros grupos → **B54** grid/cap → **B55** WA/share). Cadeia: B45 ✓ → B46 ✓ → B47 → …. Planos em `docs/plans/posicao-botoes-*` e `busca-global-*`. Assumido: staff-only na busca; clique = rotas de detalhe existentes; TIs no grupo Municípios.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
# Seletor de Cenário junto aos filtros de Municípios
|
||||
|
||||
Status: rascunho — **nota Pass 2 W1 (2026-07-25):** o cenário continua client-side e NUNCA entra na serialização de URL da lista; mover para a URL é exatamente o escopo deste fill-in
|
||||
Atualizado em: 2026-07-24
|
||||
Status: entregue (2026-07-29)
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Fill-ins)
|
||||
Impeccable: B — encaixe em `MunicipalityFilters` + `MunicipalityListOverview` (`/campanha/municipios`); sem rota nova
|
||||
Appetite: ~0,25–0,5 dia eng; relocação de controle + ajuste do provider; sem migration
|
||||
Responsável: —
|
||||
|
||||
- **Revisão 2026-07-29 (implementação):** pós-B16 ✓ o Cenário pousou na **barra slim** (desktop: entre resumo e Salvar/Limpar; mobile: após filtros staff, antes de Ordenar). `MunicipalityEstimateScenarioProvider` envolve filtros + `CampaignListResults` em staff. Hint `VOTE_ESTIMATE_SCENARIO_FILTERS_HINT`. Testes do shell de filtros montam o provider (mesmo contrato que a página). Caminhos reais: `src/components/campaign/municipality/*`, `src/components/campaign/votePledge/VoteEstimateScenarioField.tsx`.
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` (princípios 2 — clareza sob pressão — e 8 — Feel the action) / `DESIGN.md` (register `product`; Field Desk) · tema `data-theme='campaign'` · shells `MunicipalityFilters`, `VoteEstimateScenarioField`, `MunicipalityEstimateScenarioProvider`.
|
||||
|
|
@ -48,12 +50,12 @@ Polish anterior (notebook 2026-07-23) já tinha movido o seletor do mapa → ove
|
|||
- **Fill-in com plano próprio (não reabrir A10; não absorver só em R6; não item de trilha B).** Relocação barata, paralelizável, cortável; A10 já entregue a semântica. (2026-07-24, classificação roadmap-item.) **Rejeitado:** fase informal de A10 (mistura entrega fechada com polish de layout); só R6 (atrasa quick win no critique largo); ID B novo (infla grafo por ~½ dia).
|
||||
- **Provider envolve filtros + `CampaignListResults` (staff).** Necessário para o seletor viver em `MunicipalityFilters`. Custo: troca de cenário re-renderiza a árvore dos filtros — aceitável (controle local, sem navegação). **Rejeitado:** prop-drill `scenario`/`setScenario` sem provider; dois providers; manter seletor no overview _e_ nos filtros (duplicata).
|
||||
- **Cenário continua fora da URL e fora de `buildMunicipalityFiltersKey` / Limpar.** Alinhado à decisão A10 (Ano/Escala locais; só `compare` na URL do mapa). **Rejeitado:** `?estimate=` neste item; Limpar resetar cenário (mistura recorte URL com lente de leitura).
|
||||
- **Hint:** adaptar copy do overview (hoje `VOTE_ESTIMATE_SCENARIO_OVERVIEW_HINT`) para o contexto dos filtros (“Troca o total da visão geral e os votos na lista…”); mapa no Início permanece com o hint próprio. **Rejeitado:** remover o `CampaignInfoHint`.
|
||||
- **Hint:** `VOTE_ESTIMATE_SCENARIO_FILTERS_HINT` no controle da barra de filtros; mapa no Início permanece com `VOTE_ESTIMATE_SCENARIO_MAP_HINT`.
|
||||
- **i18n e naming** (AGENTS.md): identificadores existentes (`VoteEstimateScenarioField`, `MunicipalityEstimateScenarioProvider`); strings “Cenário” / Pessimista / Média / Otimista.
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **Posição exata na fileira (antes ou depois dos selects de recorte)?** **Opções:** A) após Prioridade (fim dos staff filters, antes de Limpar) | B) logo após a busca | C) fileira própria abaixo. **Recomendação:** **A** — Cenário é lente de leitura do resultado, não critério de where; fica com os controles staff sem competir com busca/TI. _(assumido — validar no craft se a fileira quebrar em mobile)_
|
||||
- **Posição exata na fileira (antes ou depois dos selects de recorte)?** **Resolvido 2026-07-29:** desktop = barra slim B16 (antes de Salvar/Limpar); mobile = após assessores, antes de Ordenar.
|
||||
- **Mapa do Início: espelhar “Cenário nos filtros do dashboard”?** **Opções:** A) neste item | B) fora / R6 se critique pedir. **Recomendação:** **B** — pedido é a página de municípios; mapa já agrupa Cenário com Ano/Escala.
|
||||
|
||||
## Abordagem proposta
|
||||
|
|
@ -101,6 +103,12 @@ Depth check: reusa field + context + filters existentes; sem hook/shared shell n
|
|||
## Adiado com gatilho
|
||||
|
||||
- **Mesma colocação mental no dashboard (mapa).** Revisitar quando: R6 critique o Início ou produto pedir “um só lugar para Cenário” entre mapa e lista.
|
||||
- **Cenário fora do `<form role="search">`.** Revisitar quando: auditoria a11y/semântica da barra B16 apontar conflito busca × cenário (fill-in mantém padrão B16).
|
||||
- **Mount único do seletor (breakpoint).** Revisitar quando: medição mostrar custo dos dois `VoteEstimateScenarioField` (desktop/mobile) ou regressão de foco/Popover.
|
||||
|
||||
## Já resolvido no simplify (não reabrir)
|
||||
|
||||
- Provider obrigatório no controle de Cenário + `MunicipalityEstimateScenarioProvider` nos testes do shell de filtros (`campaignListFilterNavigation.unit.spec.ts`).
|
||||
|
||||
## Referências
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Roadmap — Teqo
|
||||
|
||||
Atualizado em: 2026-07-29 — B46 ✓ (thumb-zone ações no Início UX-1); B58 registrado (polimento strip); B56–B57 registrados; B47–B55 busca global fatiada.
|
||||
Atualizado em: 2026-07-29 — fill-in Cenário junto aos filtros (municípios) ✓; B46 ✓ (thumb-zone ações no Início UX-1); B58 registrado (polimento strip); B56–B57 registrados; B47–B55 busca global fatiada.
|
||||
Registro canônico dos **próximos** planos e débitos. Histórico de entregas: resumo abaixo + planos em [`docs/plans/`](plans/) + notebook [`.cursor/rules/projects/nucleos-eleitorais.mdc`](../.cursor/rules/projects/nucleos-eleitorais.mdc).
|
||||
|
||||
## Âncoras do calendário eleitoral 2026 (Res. TSE 23.760/2026)
|
||||
|
|
@ -157,7 +157,7 @@ O discovery literatura→persona→entrevista ([relatório aprovado](research/re
|
|||
- ~~**Dimir o resultado no compositor de giro**~~ — **✓ (entregue 2026-07-28)** em `/campanha/atividades/giros`: `CampaignListPendingBoundary` + `CampaignListResults` dimam a proposta (`data-pending`/`aria-busy`/`aria-live`), `TourRegionPicker` usa `useCampaignListTransition` (spinner do select permanece); `CalendarPhaseNote` moveu para dentro de Results; unit `tourRegionPending.unit.spec.ts` · sem migration · [plano](plans/feedback-pendente-compositor-giro.md)
|
||||
- ~~**Ícone de prioridade na lista**~~ ✓ (entregue 2026-07-28) em `/campanha/municipios` — Badge “Prioritária” trocado por Flag + `CampaignHoverTooltip` “Município prioritário” na coluna do nome (tabela + card mobile) · ~0,25d · Impeccable B · [plano](plans/icone-prioridade-lista-municipios.md)
|
||||
- ~~**Selecionados no topo nos filtros do header**~~ — **✓ (entregue 2026-07-28)** opções já marcadas sobem ao topo nos Popovers multi-seleção do sistema de listas (municípios, lideranças, territórios, dobradinhas) e nos `CampaignMobileMultiFilterField`; helper puro `orderFilterOptionsSelectedFirst` (`src/lib/listFilterOptions.ts`); snapshot congelado enquanto o Popover está aberto; hairline entre grupos (oculto durante busca); single-select gateado por `row.checkbox` · Impeccable B · [plano](plans/selecionados-no-topo-filtros-header.md)
|
||||
- **Cenário junto aos filtros** em `/campanha/municipios` — mover `VoteEstimateScenarioField` para a fileira/barra slim de `MunicipalityFilters` (com **B16**, a barra deixa de carregar os selects de coluna); overview só consome o contexto · ~0,25–0,5d · Impeccable B · soft: B16 · [plano](plans/cenario-junto-filtros-municipios.md)
|
||||
- ~~**Cenário junto aos filtros**~~ — **✓ (entregue 2026-07-29)** `VoteEstimateScenarioField` na barra slim de `MunicipalityFilters` (desktop/mobile); overview só consome contexto; provider envolve filtros + resultados · ~0,25–0,5d · Impeccable B · [plano](plans/cenario-junto-filtros-municipios.md)
|
||||
- ~~**B16+** escala/DRY pós-B16~~ — **absorvido pelo Pass 2 W1-D1 (2026-07-25)**: `useOptimistic`, facet por slugs, hrefs via serializador canônico, facets no mesmo `Promise.all` · [plano](plans/escala-dry-pos-b16.md)
|
||||
- **E10+** escala/DRY pós-E10 — quatro fases do `/simplify` da classificação territorial: **F1** a lista lê `municipality` 2× por request (`listQuery` + `loadMunicipalityScope`, mesmo `where`, select superset) — **fecha o "Adiado com gatilho" do A11**, cujo gatilho ("3º path derivado no loader") disparou com `classe` sendo o 4º (`votos`, `deficit`, `frescor`); **F2** `computeAllMunicipalityPotentials()` recomputa 435 linhas puras do artefato por request (memo de processo, como `bahiaElectionAggregates`/`municipalityVoteRank`); **F3** um `TooltipProvider` por tooltip (~30 na lista) anula o agrupamento de delay do Radix — hoist para o layout de `(app)`; **F4** os 4 multi-filtros do mobile saem da mesma `municipalityFilterDefinitions` que os single-select vizinhos já usam · ~0,75–1d eng · Impeccable A · sem migration · cortável · [plano](plans/escala-dry-pos-e10.md)
|
||||
- **B13+** escala/DRY pós-B13 — quatro fases do `/simplify` da escala relativa no mapa, as duas primeiras de **coerência** e não de custo: **F1** a razão do LQ é escrita duas vezes (painel cliente `:210` × classificador `server-only` `:108`, separação **deliberada** — o classificador arrasta o artefato de 600 KB) sem nenhum teste travando a concordância, que é a rede de segurança do **E15**; **F2** no readout do mapa a posição vale para a cidade inteira enquanto a classe é calculada sobre o escopo do usuário, então dois assessores veem classes diferentes para o mesmo polígono de Salvador — declarar a geografia, sem mudar o cálculo; **F3** `pnpm build:election-aggregates` varre a mesma fatia estadual do TSE 3× por ano (~225k docs extras) porque `loadFederalVotesByCityZoneAndCandidate` só precisa de `party` no `select` para ser superconjunto estrito dos outros dois; **F4** `competitiveRankByYear` soma ~57 dos ~63,6 KB que o B13 pôs no payload RSC, pagos mesmo por quem nunca abre a escala — tupla `[rank, candidates]` corta ~metade e responde ao "profiling antes de agir" que o ledger pediu em 2026-07-23 · ~0,5–0,75d eng · Impeccable A · sem migration · cortável · [plano](plans/escala-dry-pos-b13.md)
|
||||
|
|
@ -391,7 +391,7 @@ flowchart TD
|
|||
B8F2 -.rank por ZE = débito aberto.-> B14n
|
||||
```
|
||||
|
||||
Paralelizáveis agora: **B58** (polimento strip de ações); **B47** (próximo do chassis UX-1 — desbloqueia B48–B55); **B56** (resumo Início — soft B47, não bloqueia busca); após **B47**, **B49–B53** em paralelo entre si (soft ordem visual sob B48). **B57** não é paralelizável agora (depende de B56 e está de propósito no futuro). Demais já listados: ~~**B43**~~ ✓ (chassis UX-1 — entregue 2026-07-29), ~~**B42**~~ ✓ (polimento mobile da mesma lista — entregue 2026-07-27), **B21** (página dos TIs; sem dep dura — E17 ✓/B15 ✓/B16 ✓ já entregues; E12 pousa nela depois; **B25** é o único item que espera B21 — o link do território na lista só existe depois do destino), ~~**B17**~~ ✓ (seletor de colunas; soft B15 ✓/B16 ✓ — pousa no header de coluna atual), ~~**B18**~~ ✓ (filtros salvos + submenu Municípios; soft B16 ✓/B17 — botão na barra), **B22** (explicação por coluna no header; sem dep dura — B15 ✓/B16 ✓/E18 ✓), **B23** (tooltip nas células; a mecânica genérica e a promoção do primitivo já saíram no E10 ✓ — restam os consumidores Assessores/Tendência e a convivência com o Popover), ~~**B24**~~ ✓ (auto-save + justificativa no popover de Tendência; entregue 2026-07-26), **B26** (registrar sinal na célula "Último sinal"; sem dep dura — E9 ✓/C12 ✓ já entregues; soft B24/B23 só pela coerência de contrato na mesma linha), **B27** (combobox + chips + auto-save no popover de Assessores; sem dep dura — B19 ✓ já deu o delta+lock e o padrão de chips; soft B24 pelo endpoint e B23 pelo gatilho), **B28** (e-mail + celular na lista de lideranças; sem dep dura — B19 ✓ é só o padrão soft de copy/WhatsApp; `phone` já está no view model), **B29** (sort + filtro no header da lista de lideranças; sem dep dura — B15 ✓/B16 ✓ deram o contrato e o popover; soft B21 e B28 só pela ordem de chegada: quem entrar depois herda o head compartilhado / encaixa as colunas do outro), fill-ins (renomear coluna Cobertura → quick win de densidade no header; remover coluna Tipo — quick win de densidade; remover lista de zonas do Início — pós-B8 F2 ✓; ícone de prioridade na lista; Cenário junto aos filtros — encaixa na barra slim de B16 ✓ —; selecionados no topo nos filtros do header — helper puro que B21 reusa —; O0+, RS+). **B30** (convite WhatsApp na lista de lideranças; sem dep dura — reusa integralmente a action/collection existentes; soft B28 só pelo ícone na mesma linha, qualquer um pode ir primeiro — **entregue 2026-07-26**), **B31** (coluna de dobradinhas na lista de lideranças; sem dep dura — o dado já está no view model e o catálogo já tem loader; soft B27 pela peça de célula, que só vira extração compartilhada no 3º call site, e B28/B29/B30 pela ordem de chegada na mesma tabela), ~~**B32**~~ ✓ (editar Status de apoio na célula da lista de lideranças; entregue 2026-07-26), **B33** (sort + filtro no header da lista de dobradinhas; sem dep dura — B15 ✓/B16 ✓ deram o contrato; soft B29/B21 só pela ordem de chegada na mesma corrida de extração do chrome compartilhado, quem entrar primeiro paga a peça e o outro só consome), **B34** (chips editáveis de municípios na lista de lideranças; sem dep dura — B19 ✓ já deu o delta+lock, o padrão de chips e o loader do catálogo a promover; soft B27/B31 pela mesma peça de célula editável — quem chegar depois herda o combobox/Popover em vez de reinventar —, e B28/B30/B32 só pela mesma tabela; _decisão a validar com produto_: Popover por célula em vez do toggle "Editar" pedido literalmente, mesmo precedente do B31/B32), **B37** (chips editáveis de municípios na lista de dobradinhas; sem dep dura — B19 ✓/B34 já dão a lib a promover e o desenho do Popover+combobox; soft B34 só pela ordem de chegada em quem faz a promoção da lib, B31 pela mesma família de pedido em outra tabela, B33 pela mesma tabela; _mesma decisão a validar com produto_ do B31/B32/B34: Popover por célula, não toggle). **B36** (coluna de lideranças na lista de dobradinhas; sem dep dura — reusa integralmente a mutação/schema/lock que o B31 desenha para a mesma relação, do lado oposto; soft B31 pela corrida de extração da célula/action compartilhadas, B19 ✓ pelo padrão de origem, B33 pela mesma tabela), **C13** (renomear Plano de Ação → Atividade; sem dep dura — não toca nenhuma das listas em curso (B26–B37 são municípios/lideranças/dobradinhas), então roda em paralelo a elas; a única ordem que importa é **antes de E13**, que constrói dentro da vertical renomeada, e antes do congelamento ~20/09 por carregar migration; PR mecânico e isolado, não misturar com feature). **D3** só após smoke + folga e só se o fluxo sede-digita (C12 ✓) não absorver os deltas do ZAP (não compete com E9).
|
||||
Paralelizáveis agora: **B58** (polimento strip de ações); **B47** (próximo do chassis UX-1 — desbloqueia B48–B55); **B56** (resumo Início — soft B47, não bloqueia busca); após **B47**, **B49–B53** em paralelo entre si (soft ordem visual sob B48). **B57** não é paralelizável agora (depende de B56 e está de propósito no futuro). Demais já listados: ~~**B43**~~ ✓ (chassis UX-1 — entregue 2026-07-29), ~~**B42**~~ ✓ (polimento mobile da mesma lista — entregue 2026-07-27), **B21** (página dos TIs; sem dep dura — E17 ✓/B15 ✓/B16 ✓ já entregues; E12 pousa nela depois; **B25** é o único item que espera B21 — o link do território na lista só existe depois do destino), ~~**B17**~~ ✓ (seletor de colunas; soft B15 ✓/B16 ✓ — pousa no header de coluna atual), ~~**B18**~~ ✓ (filtros salvos + submenu Municípios; soft B16 ✓/B17 — botão na barra), **B22** (explicação por coluna no header; sem dep dura — B15 ✓/B16 ✓/E18 ✓), **B23** (tooltip nas células; a mecânica genérica e a promoção do primitivo já saíram no E10 ✓ — restam os consumidores Assessores/Tendência e a convivência com o Popover), ~~**B24**~~ ✓ (auto-save + justificativa no popover de Tendência; entregue 2026-07-26), **B26** (registrar sinal na célula "Último sinal"; sem dep dura — E9 ✓/C12 ✓ já entregues; soft B24/B23 só pela coerência de contrato na mesma linha), **B27** (combobox + chips + auto-save no popover de Assessores; sem dep dura — B19 ✓ já deu o delta+lock e o padrão de chips; soft B24 pelo endpoint e B23 pelo gatilho), **B28** (e-mail + celular na lista de lideranças; sem dep dura — B19 ✓ é só o padrão soft de copy/WhatsApp; `phone` já está no view model), **B29** (sort + filtro no header da lista de lideranças; sem dep dura — B15 ✓/B16 ✓ deram o contrato e o popover; soft B21 e B28 só pela ordem de chegada: quem entrar depois herda o head compartilhado / encaixa as colunas do outro), fill-ins (renomear coluna Cobertura → quick win de densidade no header; ~~remover coluna Tipo~~ ✓; ~~remover lista de zonas do Início~~ ✓; ~~ícone de prioridade na lista~~ ✓; ~~Cenário junto aos filtros~~ ✓ — encaixa na barra slim de B16 ✓; ~~selecionados no topo nos filtros do header~~ ✓ — helper puro que B21 reusa —; O0+, RS+). **B30** (convite WhatsApp na lista de lideranças; sem dep dura — reusa integralmente a action/collection existentes; soft B28 só pelo ícone na mesma linha, qualquer um pode ir primeiro — **entregue 2026-07-26**), **B31** (coluna de dobradinhas na lista de lideranças; sem dep dura — o dado já está no view model e o catálogo já tem loader; soft B27 pela peça de célula, que só vira extração compartilhada no 3º call site, e B28/B29/B30 pela ordem de chegada na mesma tabela), ~~**B32**~~ ✓ (editar Status de apoio na célula da lista de lideranças; entregue 2026-07-26), **B33** (sort + filtro no header da lista de dobradinhas; sem dep dura — B15 ✓/B16 ✓ deram o contrato; soft B29/B21 só pela ordem de chegada na mesma corrida de extração do chrome compartilhado, quem entrar primeiro paga a peça e o outro só consome), **B34** (chips editáveis de municípios na lista de lideranças; sem dep dura — B19 ✓ já deu o delta+lock, o padrão de chips e o loader do catálogo a promover; soft B27/B31 pela mesma peça de célula editável — quem chegar depois herda o combobox/Popover em vez de reinventar —, e B28/B30/B32 só pela mesma tabela; _decisão a validar com produto_: Popover por célula em vez do toggle "Editar" pedido literalmente, mesmo precedente do B31/B32), **B37** (chips editáveis de municípios na lista de dobradinhas; sem dep dura — B19 ✓/B34 já dão a lib a promover e o desenho do Popover+combobox; soft B34 só pela ordem de chegada em quem faz a promoção da lib, B31 pela mesma família de pedido em outra tabela, B33 pela mesma tabela; _mesma decisão a validar com produto_ do B31/B32/B34: Popover por célula, não toggle). **B36** (coluna de lideranças na lista de dobradinhas; sem dep dura — reusa integralmente a mutação/schema/lock que o B31 desenha para a mesma relação, do lado oposto; soft B31 pela corrida de extração da célula/action compartilhadas, B19 ✓ pelo padrão de origem, B33 pela mesma tabela), **C13** (renomear Plano de Ação → Atividade; sem dep dura — não toca nenhuma das listas em curso (B26–B37 são municípios/lideranças/dobradinhas), então roda em paralelo a elas; a única ordem que importa é **antes de E13**, que constrói dentro da vertical renomeada, e antes do congelamento ~20/09 por carregar migration; PR mecânico e isolado, não misturar com feature). **D3** só após smoke + folga e só se o fluxo sede-digita (C12 ✓) não absorver os deltas do ZAP (não compete com E9).
|
||||
|
||||
### Sequência por janela (só pendentes)
|
||||
|
||||
|
|
|
|||
|
|
@ -146,19 +146,21 @@ export default async function MunicipalitiesPage({ searchParams }: Municipalitie
|
|||
</>
|
||||
)
|
||||
|
||||
// Shared transition: the filters navigate, the results dim ("Feel the action").
|
||||
// The estimate-scenario provider wraps ONLY the results — a scenario switch
|
||||
// re-renders the overview/list cells, never the filter controls.
|
||||
// Shared transition: filters navigate, results dim. Staff provider wraps filters + results.
|
||||
const listRegion = (
|
||||
<>
|
||||
{filters}
|
||||
<CampaignListResults>{listBody}</CampaignListResults>
|
||||
</>
|
||||
)
|
||||
|
||||
const main = (
|
||||
<CampaignListPendingBoundary>
|
||||
{filters}
|
||||
<CampaignListResults>
|
||||
{isStaffView ? (
|
||||
<MunicipalityEstimateScenarioProvider>{listBody}</MunicipalityEstimateScenarioProvider>
|
||||
) : (
|
||||
listBody
|
||||
)}
|
||||
</CampaignListResults>
|
||||
{isStaffView ? (
|
||||
<MunicipalityEstimateScenarioProvider>{listRegion}</MunicipalityEstimateScenarioProvider>
|
||||
) : (
|
||||
listRegion
|
||||
)}
|
||||
</CampaignListPendingBoundary>
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
'use client'
|
||||
|
||||
import { useMunicipalityEstimateScenario } from '@/components/campaign/municipality/MunicipalityEstimateScenarioContext'
|
||||
import { SaveMunicipalityFilterControl } from '@/components/campaign/municipality/SaveMunicipalityFilterControl'
|
||||
import { CampaignMobileMultiFilterField } from '@/components/campaign/shared/CampaignMobileMultiFilterField'
|
||||
import { CampaignSearchInput } from '@/components/campaign/shared/CampaignSearchInput'
|
||||
import { useCampaignListFilterNavigation } from '@/components/campaign/shared/useCampaignListFilterNavigation'
|
||||
import {
|
||||
VOTE_ESTIMATE_SCENARIO_FILTERS_HINT,
|
||||
VoteEstimateScenarioField,
|
||||
} from '@/components/campaign/votePledge/VoteEstimateScenarioField'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Field, FieldLabel } from '@/components/ui/field'
|
||||
import { NativeSelect, NativeSelectOption } from '@/components/ui/native-select'
|
||||
|
|
@ -25,6 +30,25 @@ import {
|
|||
type MunicipalityListState,
|
||||
} from '@/utilities/municipality/municipalityListUrl'
|
||||
|
||||
const MunicipalityFilterEstimateScenario = ({
|
||||
className,
|
||||
id,
|
||||
}: {
|
||||
className?: string
|
||||
id: string
|
||||
}) => {
|
||||
const { scenario, setScenario } = useMunicipalityEstimateScenario()
|
||||
return (
|
||||
<VoteEstimateScenarioField
|
||||
id={id}
|
||||
value={scenario}
|
||||
onChange={setScenario}
|
||||
hint={VOTE_ESTIMATE_SCENARIO_FILTERS_HINT}
|
||||
className={className}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
type MunicipalityFiltersProps = {
|
||||
state: MunicipalityListState
|
||||
showStaffFilters: boolean
|
||||
|
|
@ -79,6 +103,12 @@ export const MunicipalityFilters = ({
|
|||
{activeFiltersSummary}
|
||||
</p>
|
||||
) : null}
|
||||
{showStaffFilters ? (
|
||||
<MunicipalityFilterEstimateScenario
|
||||
id="municipality-filter-estimate-scenario"
|
||||
className="hidden shrink-0 md:block md:self-end"
|
||||
/>
|
||||
) : null}
|
||||
<SaveMunicipalityFilterControl state={state} />
|
||||
{hasActiveFilters ? (
|
||||
<Button
|
||||
|
|
@ -207,6 +237,12 @@ export const MunicipalityFilters = ({
|
|||
}
|
||||
/>
|
||||
) : null}
|
||||
{showStaffFilters ? (
|
||||
<MunicipalityFilterEstimateScenario
|
||||
id="municipality-filter-estimate-scenario-mobile"
|
||||
className="md:hidden"
|
||||
/>
|
||||
) : null}
|
||||
<Field>
|
||||
<FieldLabel htmlFor="municipality-sort">Ordenar</FieldLabel>
|
||||
<NativeSelect
|
||||
|
|
|
|||
|
|
@ -7,10 +7,6 @@ import { useMunicipalityEstimateScenario } from '@/components/campaign/municipal
|
|||
import { CampaignTransitionAnchor } from '@/components/campaign/shared/CampaignListPending'
|
||||
import { CampaignMetricStrip } from '@/components/campaign/shared/CampaignMetricStrip'
|
||||
import { campaignPrioritySurfaceClassName } from '@/components/campaign/shell/CampaignPageShell'
|
||||
import {
|
||||
VOTE_ESTIMATE_SCENARIO_OVERVIEW_HINT,
|
||||
VoteEstimateScenarioField,
|
||||
} from '@/components/campaign/votePledge/VoteEstimateScenarioField'
|
||||
import { VoteEstimateScenarioStrip } from '@/components/campaign/votePledge/VoteEstimateScenarioStrip'
|
||||
import { formatElectionNumber } from '@/lib/electionFormat'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
|
@ -74,7 +70,7 @@ export const MunicipalityListOverview = ({
|
|||
view: MunicipalityListOverviewData
|
||||
shameHref: string | null
|
||||
}) => {
|
||||
const { scenario, setScenario } = useMunicipalityEstimateScenario()
|
||||
const { scenario } = useMunicipalityEstimateScenario()
|
||||
const [isFlashing, setIsFlashing] = useState(false)
|
||||
const skipFlashRef = useRef(true)
|
||||
const flashTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
|
||||
|
|
@ -134,14 +130,6 @@ export const MunicipalityListOverview = ({
|
|||
isFlashing ? 'ring-2 ring-primary/35' : undefined,
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col gap-2 border-b border-foreground/10 px-4 py-2 sm:flex-row sm:items-end sm:justify-between">
|
||||
<VoteEstimateScenarioField
|
||||
id="municipality-overview-estimate-scenario"
|
||||
value={scenario}
|
||||
onChange={setScenario}
|
||||
hint={VOTE_ESTIMATE_SCENARIO_OVERVIEW_HINT}
|
||||
/>
|
||||
</div>
|
||||
<CampaignMetricStrip
|
||||
className="rounded-none ring-0"
|
||||
metrics={[
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import {
|
|||
type VoteEstimateScenario,
|
||||
} from '@/lib/voteEstimate'
|
||||
|
||||
export const VOTE_ESTIMATE_SCENARIO_OVERVIEW_HINT =
|
||||
'Troca o total desta visão geral e os votos na lista. No mapa, a cor só muda com Ano 2026.'
|
||||
export const VOTE_ESTIMATE_SCENARIO_FILTERS_HINT =
|
||||
'Troca o total da visão geral e os votos na lista. No mapa do Início, a cor só muda com Ano 2026.'
|
||||
|
||||
export const VOTE_ESTIMATE_SCENARIO_MAP_HINT = 'Pinta o mapa e os totais da lista abaixo.'
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ vi.mock('next/navigation', async (importActual) => ({
|
|||
useRouter: () => ({ replace: routerState.replace }),
|
||||
}))
|
||||
|
||||
import { MunicipalityEstimateScenarioProvider } from '@/components/campaign/municipality/MunicipalityEstimateScenarioContext'
|
||||
import { MunicipalityFilters } from '@/components/campaign/municipality/MunicipalityFilters'
|
||||
import { TerritoryFilters } from '@/components/campaign/municipality/TerritoryFilters'
|
||||
import { CampaignListPendingBoundary } from '@/components/campaign/shared/CampaignListPending'
|
||||
|
|
@ -85,19 +86,27 @@ const filterCases: FilterCase[] = [
|
|||
bareHref: '/campanha/municipios',
|
||||
sortValue: 'name|asc',
|
||||
element: (currentQuery) =>
|
||||
createElement(MunicipalityFilters, {
|
||||
state: { page: 1, ...(currentQuery ? { q: currentQuery } : {}) },
|
||||
showStaffFilters: true,
|
||||
regionFilterOptions: [],
|
||||
advisorFilterOptions: [],
|
||||
}),
|
||||
createElement(
|
||||
MunicipalityEstimateScenarioProvider,
|
||||
null,
|
||||
createElement(MunicipalityFilters, {
|
||||
state: { page: 1, ...(currentQuery ? { q: currentQuery } : {}) },
|
||||
showStaffFilters: true,
|
||||
regionFilterOptions: [],
|
||||
advisorFilterOptions: [],
|
||||
}),
|
||||
),
|
||||
elementWithSort: () =>
|
||||
createElement(MunicipalityFilters, {
|
||||
state: { page: 1, sort: 'name', dir: 'asc' },
|
||||
showStaffFilters: true,
|
||||
regionFilterOptions: [],
|
||||
advisorFilterOptions: [],
|
||||
}),
|
||||
createElement(
|
||||
MunicipalityEstimateScenarioProvider,
|
||||
null,
|
||||
createElement(MunicipalityFilters, {
|
||||
state: { page: 1, sort: 'name', dir: 'asc' },
|
||||
showStaffFilters: true,
|
||||
regionFilterOptions: [],
|
||||
advisorFilterOptions: [],
|
||||
}),
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue