Register B46–B55: home thumb-zone and global search slices.
Adds plans and roadmap wiring for action-button placement and the fatiated Início search (input through row actions).
This commit is contained in:
parent
7383eefafe
commit
6681133695
12 changed files with 811 additions and 12 deletions
File diff suppressed because one or more lines are too long
85
docs/plans/busca-global-acoes-linha.md
Normal file
85
docs/plans/busca-global-acoes-linha.md
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# Busca global — ações de linha (WhatsApp / compartilhar)
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B55 — busca global)
|
||||
Impeccable: B — trailing actions nas linhas de resultado
|
||||
Appetite: ~0,5–0,75 dia eng; botões + Web Share / wa.me; sem migration
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` (atalhos WhatsApp ok; soberania no horizonte) / `DESIGN.md` · `whatsAppHrefForPhone` (B28/B19) · tema `campaign`.
|
||||
|
||||
Na implementação: craft compacto → critique → polish.
|
||||
|
||||
Brief:
|
||||
|
||||
- **Persona:** staff achou a pessoa/pedido e quer ZAP ou share sem abrir a ficha.
|
||||
- **Job:** trailing control à direita da linha; não rouba o click da linha (stopPropagation).
|
||||
- **Anti-goals:** share que grava PII em analytics; segundo botão de copy; WA em município/TI.
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
Dados: N/A — ações de saída; texto de share = título + URL canônica do detalhe.
|
||||
|
||||
## Contexto
|
||||
|
||||
Pedido (2026-07-29):
|
||||
|
||||
- **Lideranças, assessores, dobradinhas:** ação abrir WhatsApp à direita.
|
||||
- **Demandas e atividades:** compartilhar à direita — **mobile** = `navigator.share` do device; **desktop** = delegar para WhatsApp (`wa.me` com texto/url).
|
||||
|
||||
## Objetivos
|
||||
|
||||
- Primitivo(s) `HomeSearchWhatsAppAction` / `HomeSearchShareAction` (ou um com `variant`).
|
||||
- WA: reusar `whatsAppHrefForPhone`; ocultar se null; `target`/`rel` seguros; `aria-label` com nome.
|
||||
- Share: se `navigator.share` disponível **e** viewport/pointer mobile (definir: `navigator.share` + `!matchMedia('(pointer: fine)')` ou UA — **recomendação:** feature-detect `share` primeiro; fallback WA no desktop).
|
||||
- Texto/url de share: título do hit + absolute URL do detalhe (`getCampaignInviteBaseURL` / site URL já usados na campanha).
|
||||
- `pointer-events` / `onClick` stop na ação para o `Link` da linha não navegar.
|
||||
- Sem migration / Consent novo (não é canal D3; é `wa.me` / share nativo).
|
||||
|
||||
## Decisões travadas
|
||||
|
||||
- **stopPropagation na ação.** **Rejeitado:** ação = único hit da linha (mata navegação).
|
||||
- **Desktop share → WhatsApp**, não Twitter/e-mail picker. **Rejeitado:** `mailto:` genérico.
|
||||
- **Sem Consent** — não armazena biometria nem canal; só abre app externo. **Rejeitado:** abrir Onda 0 por isso.
|
||||
- **i18n:** labels “Abrir no WhatsApp” / “Compartilhar”.
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **Dobradinha sem telefone:** esconder ícone (já em B52). Confirmado.
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Row["result row Link"] --> WA["wa.me"]
|
||||
Row --> Share["navigator.share | wa.me fallback"]
|
||||
```
|
||||
|
||||
- Integrar nos grupos **B49–B53** via prop `trailingAction` ou composição.
|
||||
- Precedente share: `SupporterShareKit.tsx`; WA: `whatsAppHrefForPhone` / `buildWhatsAppUrl` em `lib/phone.ts`.
|
||||
- Unit: share fallback; click não navega.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Dura: **B47** + pelo menos um de **B49–B53**. Soft: todos os cinco grupos.
|
||||
- Soft: B28 ✓ / B19 ✓ (`whatsAppHrefForPhone`).
|
||||
|
||||
## Não escopo
|
||||
|
||||
Bridge D3–D5. Preview de mensagem rica. Municípios/TIs.
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
**Web Share Level 2 files.** **Mitigação:** só text+url.
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
- **Copy link** ao lado do share. Revisitar se mesa pedir no critique.
|
||||
|
||||
## Referências
|
||||
|
||||
- [busca-global-resultados-liderancas.md](busca-global-resultados-liderancas.md) · assessores/atividades/dobradinhas/demandas · `src/lib/phone.ts` · share kits de apoiadores (precedente copy/`wa.me` se existir)
|
||||
- AGENTS.md — naming; sem WABA
|
||||
100
docs/plans/busca-global-inicio-input.md
Normal file
100
docs/plans/busca-global-inicio-input.md
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
# Busca global do Início — input e modo resultados
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B47 — busca global)
|
||||
Impeccable: C — superfície nova de busca no Início (expand/collapse + empty results region)
|
||||
Appetite: ~1–1,5 dia eng; ilha client + debounce + chrome de modo focado; sem provider de hits ainda
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` (Clarity under pressure; Feel the action) / `DESIGN.md` · `CampaignSearchInput` · tema `campaign`.
|
||||
|
||||
Na implementação: **shape → craft → critique → polish** (classe C).
|
||||
|
||||
Brief compacto:
|
||||
|
||||
- **Persona / contexto:** staff no Início quer achar um município/pessoa/atividade sem abrir a lista certa na sidebar.
|
||||
- **Job principal:** digitar → ver resultados; no mobile o input sobe e o resto some.
|
||||
- **Estratégia de cor:** Restrained; sem card chrome nos resultados (grupos chegam em B48+).
|
||||
- **Edit where you see:** não — navegação/descoberta.
|
||||
- **Anti-goals:** botão “Pesquisar”; command palette estilo Raycast com atalho global nesta fatia; busca na sidebar; leader com omnibox estadual _(assumido — lockdown)_.
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
Dados: N/A neste item — só chrome; hits = **B48–B53**.
|
||||
|
||||
## Contexto
|
||||
|
||||
Início blank (**B43**) + ações (**B44/B45**) + posição (**B46**). Pedido (2026-07-29): input de busca geral **sem botão**, debounce na digitação; no mobile fica **abaixo** das ações e, ao digitar, **desliza para cima**, some o resto, ficam input + resultados; no tablet/desktop o input fica **acima** das ações e a visualização de resultados é semelhante (grid = **B54**).
|
||||
|
||||
## Objetivos
|
||||
|
||||
- Ilha **`CampaignHomeSearch`** (nome final no PR) no Início **staff** (`coordinator` / `candidate` / `advisor`); **não** montar para `leader` _(assumido)_.
|
||||
- Reusar visual de `CampaignSearchInput` (ícone, `min-h-11`); **sem** submit button.
|
||||
- Debounce ~200–300 ms (**não** reusar `SEARCH_DEBOUNCE_MS = 1000` de `useCampaignListFilterNavigation` / `CampaignSearchForm` — esse valor é para filtro de lista com round-trip RSC; omnibox precisa resposta mais curta); query vazia / só espaços → sair do modo focado e limpar região de resultados.
|
||||
- **Modo focado (mobile):** ao ter query não vazia, transição: input sobe; strip de ações e demais chrome do Início ocultos; região de resultados abaixo do input.
|
||||
- **Modo focado (`md+`):** mesma semântica de “só input + resultados”, sem empurrar a strip para baixo da dobra de forma confusa — strip some enquanto há query (paridade com mobile).
|
||||
- Slot/região `aria-live` / `role="region"` para grupos (**B48+**); empty state “Nenhum resultado.” só quando query ≥ limiar e todos os grupos vazios (após ≥1 provider).
|
||||
- Endpoint ou RSC search: **não** neste item — expor contrato (`query`, `onResults`, registry de grupos) para B48+.
|
||||
- Sem migration / Consent.
|
||||
|
||||
## Decisões travadas
|
||||
|
||||
- **Debounce no cliente; sem botão Pesquisar.** **Rejeitado:** submit form; busca a cada keystroke sem debounce.
|
||||
- **Staff-only.** Leader continua lockdown (contatos). **Rejeitado:** omnibox para leader com só apoiadores nesta fatia (outro job).
|
||||
- **Expand = query não vazia**, não focus-only. **Rejeitado:** sumir a strip no focus vazio (rouba o ritual de ações).
|
||||
- **i18n:** `CampaignHomeSearch`, `homeSearchQuery`; label pt-BR “Buscar na campanha” (ou copy curta a fechar no craft).
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **Transporte dos hits: `POST` JSON vs server action vs fetch RSC?** **Opções:** A rota `campaignJsonMutationRoute`-like GET/POST search | B server action | C debounce + `router` query. **Recomendação:** A — `GET`/`POST` same-origin com body/query curta, access no loader, sem sujar a URL do Início com `?q=` (Início não é lista). _(assumido)_
|
||||
- **Limiar mínimo de caracteres?** **Opções:** 1 | 2. **Recomendação:** 2 para reduzir ruído em nomes curtos; 1 se critique pedir. _(assumido)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Input["CampaignHomeSearch input"]
|
||||
Deb["debounce"]
|
||||
Mode["focused mode UI"]
|
||||
Slot["results slot"]
|
||||
Input --> Deb --> Mode
|
||||
Deb --> Slot
|
||||
```
|
||||
|
||||
Componentes:
|
||||
|
||||
- **`CampaignHomeSearch.tsx`** (`'use client'`, `components/campaign/dashboard/`): estado local da query; debounce; classes de modo focado; renderiza `children` / registry de grupos quando B48+ plugar.
|
||||
- **Wire-up** em `page.tsx` (RSC) só para staff; passar escopo mínimo se necessário depois.
|
||||
- **Não** importar `buildMunicipalityListHref` / catálogo no client strip — hits serializados pelo server nos itens seguintes.
|
||||
- **Migration:** Sem migration. Rota JSON de busca pode nascer em **B48** com o primeiro provider (evitar endpoint vazio).
|
||||
|
||||
## Dependências
|
||||
|
||||
- Dura: **B46** (ordem relativa ações↔busca). Soft: **B45**.
|
||||
- Desbloqueia **B48–B55**.
|
||||
|
||||
## Não escopo
|
||||
|
||||
- Providers de hits → **B48–B53**.
|
||||
- Grid 2/3 colunas e cap de viewport → **B54**.
|
||||
- WhatsApp/share na linha → **B55**.
|
||||
- Páginas de detalhe novas → rotas existentes (cada grupo só `Link`).
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
- **cmdk / Command palette global com ⌘K.** **Mitigação:** input no Início só; atalho = fill-in se pedido ≥2×.
|
||||
- **Index único tipo Algolia.** **Mitigação:** loaders Payload/`wordStartFilter` por domínio.
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
- **`?q=` na URL do Início** (shareable). Revisitar se mesa pedir mandar link da busca.
|
||||
- **Busca na liderança (apoiadores).** Revisitar se leader pedir achar contato sem rolar a lista.
|
||||
|
||||
## Referências
|
||||
|
||||
- [posicao-botoes-acao-inicio-thumb-zone.md](posicao-botoes-acao-inicio-thumb-zone.md) · [fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md) (U11 busca de município nos wizards — reusar matcher)
|
||||
- `CampaignSearchInput.tsx` · `lib/wordStartFilter.ts` · `campaignJsonMutationRoute.ts` (precedente CSRF se POST)
|
||||
- `PRODUCT.md` / `DESIGN.md`
|
||||
80
docs/plans/busca-global-layout-resultados.md
Normal file
80
docs/plans/busca-global-layout-resultados.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# Busca global — layout dos resultados (grid + cap de viewport)
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B54 — busca global)
|
||||
Impeccable: B — layout responsivo da região de resultados B47
|
||||
Appetite: ~0,5–0,75 dia eng; CSS grid + política de cap; sem provider novo
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` (Clarity under pressure) / `DESIGN.md` · chrome **B47** · tema `campaign`.
|
||||
|
||||
Na implementação: craft compacto → critique → polish.
|
||||
|
||||
Brief compacto:
|
||||
|
||||
- **Persona:** staff no tablet/notebook com vários grupos; no mobile empilha.
|
||||
- **Job principal:** ver grupos lado a lado sem scroll infinito de hits.
|
||||
- **Anti-goals:** masonry irregular; virtualização prematura; cap por grupo fixo que esconde Municípios (grupo principal).
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
Dados: N/A — só layout/quantidade de linhas renderizadas; números vêm dos grupos.
|
||||
|
||||
## Contexto
|
||||
|
||||
Pedido (2026-07-29): mobile = grupos empilhados; tablet = **2 colunas**; desktop = **3 colunas**; total de resultados limitado ao **preenchimento da tela** pelo conjunto dos grupos. Conteúdo das linhas = **B48–B53**; este item só organiza e corta.
|
||||
|
||||
## Objetivos
|
||||
|
||||
- Container de resultados: `flex flex-col` (<`md`) → `md:grid md:grid-cols-2` → `lg:grid-cols-3` (breakpoints alinhados ao shell da campanha).
|
||||
- **Cap de viewport:** limitar o número total de hits renderizados (soma dos grupos) ao que cabe na área útil sob o input — medir via `ResizeObserver` / estimativa de row height, ou política simples documentada (ex. max 12–18 hits totais redistribuídos com prioridade ao grupo Municípios).
|
||||
- Grupo Municípios (**B48**) tem prioridade de slots no cap; demais grupos compartilham o resto.
|
||||
- Grupos vazios continuam não montados (regra dos providers).
|
||||
- Sem migration.
|
||||
|
||||
## Decisões travadas
|
||||
|
||||
- **Grid por viewport, não por pointer.** **Rejeitado:** 1 coluna em touch tablet paisagem.
|
||||
- **Cap no conjunto, não scroll infinito.** **Rejeitado:** “mostrar todos” com página interna.
|
||||
- **Prioridade de slots: Municípios primeiro.** **Rejeitado:** round-robin que deixa Cairu fora e enche de demandas.
|
||||
- **i18n:** `HomeSearchResultsLayout`.
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **Cap: medir DOM vs constante?** **Opções:** A ResizeObserver + row estimate | B constante por breakpoint (ex. 8/12/15). **Recomendação:** B no v1 (barato, testável); A se critique achar corte cego. _(assumido)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Slot["B47 results region"] --> Layout["HomeSearchResultsLayout"]
|
||||
Layout --> G1["Municípios"]
|
||||
Layout --> G2["outros grupos"]
|
||||
```
|
||||
|
||||
- Componente de layout envolvendo os grupos já registrados; API `allocateHitBudget(groups, budget)`.
|
||||
- **Migration:** nenhuma.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Dura: **B47**. Soft: **B48** (precisa ≥1 grupo para validar cap); melhor após 2+ grupos.
|
||||
|
||||
## Não escopo
|
||||
|
||||
Conteúdo das linhas → **B48–B53**. Ações → **B55**.
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
**Virtualização (`react-virtual`).** **Mitigação:** cap pequeno; virtualizar só se budget > 50.
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
- **“Ver todos em /municipios?q=”** por grupo. Revisitar se cap gerar reclamação na sessão.
|
||||
|
||||
## Referências
|
||||
|
||||
- [busca-global-inicio-input.md](busca-global-inicio-input.md) · [busca-global-resultados-municipios.md](busca-global-resultados-municipios.md)
|
||||
- `CampaignPageShell` breakpoints · `PRODUCT.md`
|
||||
63
docs/plans/busca-global-resultados-assessores.md
Normal file
63
docs/plans/busca-global-resultados-assessores.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# Busca global — resultados Assessores
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B50 — busca global)
|
||||
Impeccable: B — grupo de hits no slot B47
|
||||
Appetite: ~0,5 dia eng; loader + grupo; sem WA (B55)
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` / `DESIGN.md` · `/campanha/assessores` (B19) · tema `campaign`.
|
||||
|
||||
Brief: CG/candidato acha assessor pelo nome; assessor **não** precisa ver o grupo inteiro se access negar — falhar fechado com a mesma regra de `isCampaignUnrestricted` da lista (se advisor não lê assessores, grupo sempre vazio / não registrado).
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
Dados: N/A (identificação). Lista simples.
|
||||
|
||||
## Contexto
|
||||
|
||||
Grupo “Assessores”. Clique → `/campanha/assessores/[id]`. WhatsApp = **B55**.
|
||||
|
||||
## Objetivos
|
||||
|
||||
- `searchHomeAdvisors(query, user)` respeitando access B19 (unrestricted only para a vertical de assessores — se o ator não pode listar, não montar provider).
|
||||
- Título **“Assessores”**; oculto se vazio.
|
||||
- Linha: nome; secundário discreto (e-mail mascarado ou contagem de municípios — barato).
|
||||
- Sem migration.
|
||||
|
||||
## Decisões travadas
|
||||
|
||||
- **Mesmo access da lista de assessores.** **Rejeitado:** advisor buscando colegas (não é o modelo atual).
|
||||
- **Detalhe existente.** **Rejeitado:** página nova.
|
||||
- **i18n:** `HomeSearchAdvisorHit`; “Assessores”.
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **Advisor vê o grupo?** **Opções:** A nunca | B só a si. **Recomendação:** A — consistente com nav. _(assumido)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
Loader espelhando `advisorData`; grupo UI; pluga no registry B47.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Dura: **B47**. Soft: B19 ✓; **B55** para WA.
|
||||
|
||||
## Não escopo
|
||||
|
||||
WA → **B55**. Outros grupos / grid → B48–B54.
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
**Expor e-mail `@planilha.invalid`.** **Mitigação:** não mostrar e-mail inválido; preferir nome + N municípios.
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
Nenhum neste item.
|
||||
|
||||
## Referências
|
||||
|
||||
- [busca-global-inicio-input.md](busca-global-inicio-input.md) · `gerenciar-assessores.md` · `utilities/advisorData.ts` (`loadAdvisorListPageData` / `loadAdvisorDetail`) · `/campanha/assessores/[id]`
|
||||
59
docs/plans/busca-global-resultados-atividades.md
Normal file
59
docs/plans/busca-global-resultados-atividades.md
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# Busca global — resultados Atividades
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B51 — busca global)
|
||||
Impeccable: B — grupo de hits no slot B47
|
||||
Appetite: ~0,5–0,75 dia eng; loader + grupo; share = B55
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` / `DESIGN.md` · `/campanha/atividades` · tema `campaign`.
|
||||
|
||||
Brief: achar atividade por título; secundário = município + data. Clique → `/campanha/atividades/[slug]`.
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
Dados: N/A (agenda). Lista com data legível pt-BR.
|
||||
|
||||
## Objetivos
|
||||
|
||||
- `searchHomeActivities(query, user)` com access de `activity`; match no `title` (word-start).
|
||||
- Grupo **“Atividades”**; oculto se vazio.
|
||||
- Linha: título; secundário município + quando; slot para share (**B55**).
|
||||
- Sem migration / Consent.
|
||||
|
||||
## Decisões travadas
|
||||
|
||||
- **Detalhe existente por slug.** **Rejeitado:** abrir compositor de giro.
|
||||
- **Share não neste item** → **B55**.
|
||||
- **i18n:** `HomeSearchActivityHit`; “Atividades”.
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **Incluir rascunhos?** **Opções:** A sim no escopo do ator | B só próximos/realizados. **Recomendação:** A — busca é achar o que eu criei. _(assumido)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
Loader perto de activity list data; grupo UI.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Dura: **B47**. Soft: C13 ✓; **B55**.
|
||||
|
||||
## Não escopo
|
||||
|
||||
Share → **B55**. Grid → **B54**.
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
**Full-text no `resultSummary`.** **Mitigação:** só título no v1.
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
Nenhum neste item.
|
||||
|
||||
## Referências
|
||||
|
||||
- [busca-global-inicio-input.md](busca-global-inicio-input.md) · `utilities/activityPageData.ts` / `activityDetailPageData.ts` · `/campanha/atividades/[slug]`
|
||||
58
docs/plans/busca-global-resultados-demandas.md
Normal file
58
docs/plans/busca-global-resultados-demandas.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Busca global — resultados Demandas
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B53 — busca global)
|
||||
Impeccable: B — grupo de hits no slot B47
|
||||
Appetite: ~0,5 dia eng; loader + grupo; share = B55
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` / `DESIGN.md` · `/campanha/demandas` · tema `campaign`.
|
||||
|
||||
Brief: achar pedido pelo título/assunto; secundário município + status; clique → `/campanha/demandas/[slug]`.
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
Dados: N/A. Lista.
|
||||
|
||||
## Objetivos
|
||||
|
||||
- `searchHomeDemands(query, user)` com access staff de demandas; match no título (e campos de texto baratos já selecionados na lista).
|
||||
- Grupo **“Demandas”**; oculto se vazio.
|
||||
- Slot share (**B55**).
|
||||
- Sem migration / Consent.
|
||||
|
||||
## Decisões travadas
|
||||
|
||||
- **Detalhe existente.** **Rejeitado:** criar demanda a partir do empty state neste item.
|
||||
- **i18n:** `HomeSearchDemandHit`; “Demandas”.
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **Buscar descrição longa?** **Opções:** A só título | B título+resumo. **Recomendação:** A no v1. _(assumido)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
Loader demand list; grupo UI.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Dura: **B47**. Soft: **B55**.
|
||||
|
||||
## Não escopo
|
||||
|
||||
Share → **B55**. Leader nunca vê (já sem access).
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
**Filtro por status na busca.** **Mitigação:** query de nome só.
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
Nenhum neste item.
|
||||
|
||||
## Referências
|
||||
|
||||
- [busca-global-inicio-input.md](busca-global-inicio-input.md) · `utilities/campaignDemandData.ts` (`loadDemandListPageData`) · `/campanha/demandas/[slug]`
|
||||
58
docs/plans/busca-global-resultados-dobradinhas.md
Normal file
58
docs/plans/busca-global-resultados-dobradinhas.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Busca global — resultados Dobradinhas
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B52 — busca global)
|
||||
Impeccable: B — grupo de hits no slot B47
|
||||
Appetite: ~0,5 dia eng; loader + grupo; WA = B55
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` / `DESIGN.md` · `/campanha/dobradinhas` · tema `campaign`.
|
||||
|
||||
Brief: achar deputado estadual / dobradinha por nome ou partido; clique → `/campanha/dobradinhas/[slug]`.
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
Dados: N/A. Secundário = partido + contagem de municípios se barato.
|
||||
|
||||
## Objetivos
|
||||
|
||||
- `searchHomeStateDeputies(query, user)`; match nome (e partido se couber sem ruído).
|
||||
- Grupo **“Dobradinhas”**; oculto se vazio.
|
||||
- Slot trailing para WhatsApp (**B55**) quando houver telefone no modelo — se não houver telefone na entidade, B55 no-op nessa linha.
|
||||
- Sem migration.
|
||||
|
||||
## Decisões travadas
|
||||
|
||||
- **Detalhe existente.** **Rejeitado:** abrir município da dobradinha.
|
||||
- **i18n:** `HomeSearchStateDeputyHit`; “Dobradinhas”.
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **Telefone na dobradinha existe?** **Opções:** A WA só se campo existir | B omitir WA neste grupo. **Recomendação:** A — B55 esconde ícone se `whatsAppHrefForPhone` for null. _(assumido)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
Loader `stateDeputy` list; grupo UI.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Dura: **B47**. Soft: **B55**; B33 ✓.
|
||||
|
||||
## Não escopo
|
||||
|
||||
WA → **B55**.
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
**Buscar por município da dobradinha.** **Mitigação:** nome/partido só; município já está em B48.
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
Nenhum neste item.
|
||||
|
||||
## Referências
|
||||
|
||||
- [busca-global-inicio-input.md](busca-global-inicio-input.md) · `utilities/stateDeputyData.ts` (`loadStateDeputyListPageData`) · `/campanha/dobradinhas/[slug]`
|
||||
71
docs/plans/busca-global-resultados-liderancas.md
Normal file
71
docs/plans/busca-global-resultados-liderancas.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Busca global — resultados Lideranças
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B49 — busca global)
|
||||
Impeccable: B — grupo de hits no slot B47
|
||||
Appetite: ~0,5–0,75 dia eng; loader + grupo UI; sem WA (B55)
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` / `DESIGN.md` · lista `/campanha/liderancas` · tema `campaign`.
|
||||
|
||||
Na implementação: craft compacto → critique → polish.
|
||||
|
||||
Brief: staff acha uma liderança pelo nome; toque abre a ficha. Sem card. Grupo “Lideranças” só se houver hit.
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
Dados: N/A como KPI — identificação (nome + contexto curto: município principal ou contagem). Forma: lista. Sem chart.
|
||||
|
||||
## Contexto
|
||||
|
||||
Slot **B47**; grupo secundário abaixo de Municípios (**B48**). Clique → `/campanha/liderancas/[id]` (rota existente). Ação WhatsApp à direita = **B55**.
|
||||
|
||||
## Objetivos
|
||||
|
||||
- `searchHomeLeaderships(query, user)` com access existente (`overrideAccess: false`); match em `contact.name` (mesmo espírito da busca da lista / `wordStartFilter`).
|
||||
- Grupo título **“Lideranças”**; oculto se vazio.
|
||||
- Linha: nome em destaque; linha secundária discreta (ex. municípios resumidos ou setor — o que o loader já tiver barato); **sem** trailing WA neste item (reserva slot/`actions` opcional para B55).
|
||||
- Sem migration / Consent.
|
||||
|
||||
## Decisões travadas
|
||||
|
||||
- **Navega para detalhe existente.** **Rejeitado:** drawer de preview nesta fatia.
|
||||
- **Sem WhatsApp aqui** — **B55**. **Rejeitado:** copiar o ícone do B28 agora e divergir depois.
|
||||
- **i18n:** `HomeSearchLeadershipHit`; título “Lideranças”.
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **Secundário: lista de municípios vs status de apoio?** **Opções:** A municípios (curto) | B status. **Recomendação:** A — alinha ao job “quem é de onde”. _(assumido)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Q --> L["searchHomeLeaderships"] --> G["HomeSearchLeadershipGroup"] --> D["/liderancas/id"]
|
||||
```
|
||||
|
||||
- Loader perto de `leadershipData.ts` / list where; UI no dashboard/search.
|
||||
- **Migration:** nenhuma.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Dura: **B47**. Soft: **B48** (ordem visual — Municípios acima); B28 ✓ (telefone existirá para B55).
|
||||
|
||||
## Não escopo
|
||||
|
||||
- WA → **B55**. Assessores/outros grupos → **B50–B53**. Layout grid → **B54**.
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
- **Buscar por telefone.** **Mitigação:** só nome no v1; gatilho se campo pedir.
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
Nenhum neste item.
|
||||
|
||||
## Referências
|
||||
|
||||
- [busca-global-inicio-input.md](busca-global-inicio-input.md) · `utilities/leadership/leadershipData.ts` (`loadLeadershipListPageData`) · `/campanha/liderancas/[id]` · B28 (`whatsAppHrefForPhone` em `lib/phone.ts`)
|
||||
92
docs/plans/busca-global-resultados-municipios.md
Normal file
92
docs/plans/busca-global-resultados-municipios.md
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
# Busca global — resultados Municípios (+ Territórios)
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B48 — busca global)
|
||||
Impeccable: B — grupo de resultados no chrome B47; linhas sem card
|
||||
Appetite: ~1–1,5 dia eng; loader scoped + UI do grupo; 1º provider da busca
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` / `DESIGN.md` · `MunicipalityPriorityIndicator` · coluna “2022” (A11) · tema `campaign`.
|
||||
|
||||
Na implementação: craft compacto → critique → polish.
|
||||
|
||||
Brief compacto:
|
||||
|
||||
- **Persona:** staff digita “Cairu” / “Recôncavo” e quer o hit certo em 1 toque.
|
||||
- **Job principal:** listar municípios (e TIs) que casam a query, com prioridade e votos 2022 legíveis.
|
||||
- **Anti-goals:** card com borda por linha; grupo vazio visível; desalinhamento dos nomes quando um tem flag e outro não.
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
- **Vou apresentar dados?** Sim — votos 2022 (município) e agregado do território (TI) à direita.
|
||||
- **Decisões:** abrir o município/TI certo; ler magnitude relativa (mesma linguagem da coluna “2022” / rollup E17).
|
||||
- **Forma:** lista ranqueada por relevância de nome (word-start), não chart. **Rejeitado:** mapa nos resultados; % estadual absoluto.
|
||||
- **Profile:** ~até N hits por grupo (cap fino em **B54**); scoped ao access do ator.
|
||||
- **Anti-goals de dado:** sem inventar métrica nova; reusar A11 / rollup TI.
|
||||
|
||||
## Contexto
|
||||
|
||||
**B47** entrega o input e o slot. Este item é o **grupo principal** (“Municípios”), sempre no topo quando há hits. Inclui **territórios de identidade** no mesmo grupo (pedido 2026-07-29): nome do TI + valores agregados à direita. Clique → detalhe existente (`/campanha/municipios/[slug]` ou `/campanha/territorios#ti-…` via `territoryAnchor`).
|
||||
|
||||
## Objetivos
|
||||
|
||||
- Loader/server path `searchHomeMunicipalities(query, user)` (nome final) com `overrideAccess: false`; match via `matchesAtWordStart` / `normalizeSearchPhrase` (`lib/wordStartFilter.ts`).
|
||||
- Grupo com título discreto **“Municípios”**; **só renderiza se `hits.length > 0`**.
|
||||
- Linha município: flag de prioridade (`MunicipalityPriorityIndicator`) **só se `priority === 'alta'`**, com **coluna de ícone de largura fixa** para alinhar o início dos nomes; nome em destaque; território (TI) abaixo, menor/`muted`; à direita a mesma informação da coluna **“2022”** da lista (votos + contexto A11 — reusar helper de célula / `formatElectionNumber`, sem rank longo se estourar densidade — preferir o compacto da lista).
|
||||
- Linha TI: sem flag; nome do território; à direita agregado já usado em `/campanha/territorios` / `territoryOverview` (o mesmo número que a mesa já lê — tipicamente votos/rollup 2022 do E17/E12, documentar no PR qual campo).
|
||||
- Sem borda/card; padding vertical suficiente; hit target ≥ 44 px no mobile.
|
||||
- Clique = `Link` para detalhe existente — **sem** item de roadmap de “página nova”.
|
||||
- Sem migration / Consent.
|
||||
|
||||
## Decisões travadas
|
||||
|
||||
- **TIs no grupo Municípios**, não grupo separado. **Rejeitado:** “Territórios” como 7º grupo (pedido explícito).
|
||||
- **Coluna de ícone reservada** (vazia se não prioritário). **Rejeitado:** flag inline que desloca o texto.
|
||||
- **Primeiro provider nasce com a rota de busca** (se B47 adiou o endpoint). **Rejeitado:** client-only scan do catálogo sem access (assessor veria fora da carteira).
|
||||
- **i18n:** `HomeSearchMunicipalityHit`, título pt-BR “Municípios”.
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **Ordenação: word-start primeiro vs votos 2022 desc?** **Opções:** A relevância de nome | B votos. **Recomendação:** A (busca é achar pelo nome); empate → votos desc. _(assumido)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Q["query B47"] --> L["searchHomeMunicipalities"]
|
||||
L --> G["HomeSearchMunicipalityGroup"]
|
||||
G --> Mun["/municipios/slug"]
|
||||
G --> TI["/territorios#ti-"]
|
||||
```
|
||||
|
||||
Componentes:
|
||||
|
||||
- **Loader** em `utilities/` (domínio municipality ou `dashboard/` se multi — preferir municipality + territory helpers existentes: `loadTerritoryOverview` / rollup puro).
|
||||
- **`HomeSearchMunicipalityGroup.tsx`**: título + lista de linhas; registra-se no slot B47.
|
||||
- Reuso: `MunicipalityPriorityIndicator`, `mapKey`/catalog names, `TerritoryLink`/`territoryAnchor` se couber.
|
||||
- **Migration:** Sem migration.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Dura: **B47**. Soft: A11 ✓, E17/E12 ✓, B20 ✓ (priority), B25 ✓ (âncora TI).
|
||||
|
||||
## Não escopo
|
||||
|
||||
- Outros grupos → **B49–B53**. Grid/cap → **B54**. Ações WA → **B55** (não aplica a município/TI).
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
- **Fuzzy/typo tolerance.** **Mitigação:** word-start como listas.
|
||||
- **Salvador ZE vs cidade.** **Mitigação:** hits são as 19 unidades operacionais + TIs; não agregar Salvador num hit único.
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
- **Hit “sem responsável” / deficit na linha.** Revisitar se a busca virar fila E9.
|
||||
|
||||
## Referências
|
||||
|
||||
- [busca-global-inicio-input.md](busca-global-inicio-input.md) · `MunicipalityList.tsx` (`VotePositionReadout` / coluna `votos` → header “2022”) · `MunicipalityPriorityIndicator.tsx` · `lib/municipalityVoteRank.ts` · `lib/wordStartFilter.ts` · `utilities/territory/loadTerritoryOverview.ts` · `utilities/territory/territoryOverview.ts` (`TerritoryOverviewRow.votesByYear` / `pctPropriaVotacao`) · `lib/territoryAnchor.ts` · `municipalityListUrl.ts` (lista usa `name.contains` — busca home preferir word-start no índice/loader próprio)
|
||||
- AGENTS.md — `overrideAccess: false`; access advisor
|
||||
97
docs/plans/posicao-botoes-acao-inicio-thumb-zone.md
Normal file
97
docs/plans/posicao-botoes-acao-inicio-thumb-zone.md
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
# Posição dos botões de ação do Início (thumb zone)
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B46 — chassis UX-1)
|
||||
Impeccable: B — encaixe de layout no Início sob tema `campaign` (strip B44/B45 já montada)
|
||||
Appetite: ~0,5 dia eng; CSS/layout do shell do Início; sem migration
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` (Clarity under pressure; Feel the action; alvos de toque no campo) / `DESIGN.md` · primitivo **B44** + catálogo **B45** · tema `campaign`.
|
||||
|
||||
Na implementação: craft compacto → critique → polish.
|
||||
|
||||
Brief compacto:
|
||||
|
||||
- **Persona / contexto:** CG/assessor no celular (polegar na metade inferior); no notebook a faixa de ações é a primeira coisa acima da dobra.
|
||||
- **Job principal:** alcançar a strip de ações sem alongar o polegar no mobile; no `md+` manter as ações no topo do conteúdo.
|
||||
- **Estratégia de cor:** herda B44 (Restrained).
|
||||
- **Edit where you see:** não — chrome de launcher.
|
||||
- **Anti-goals:** fixed overlay que tapa a sidebar Sheet; segundo FAB flutuante; “sticky” que empurra o conteúdo sem reserva de espaço; misturar viewport (`md`) com política de pointer (aqui é **viewport**: thumb zone ≠ fine/coarse).
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
Dados: N/A — posicionamento de chrome; sem KPI.
|
||||
|
||||
## Contexto
|
||||
|
||||
Com **B43** o Início está blank; **B44/B45** colocam a strip de ações. Sem regra de zona, a strip tende a nascer no fluxo normal do `CampaignPageShell` (topo do conteúdo) — bom no desktop, ruim no celular (polegar longe). Pedido de produto (2026-07-29): botões **próximos do limite inferior** no mobile e **próximos do limite superior** em tablet/desktop. A busca global (**B47+**) se ancora nesta composição (abaixo da strip no mobile; acima no `md+`).
|
||||
|
||||
## Objetivos
|
||||
|
||||
- Layout do Início staff (e leader, se a strip B45 existir) com:
|
||||
- **< `md`:** strip de ações ancorada na região inferior do viewport útil (acima da bottom nav, se houver), com safe-area.
|
||||
- **`md+`:** strip no topo do conteúdo do Início (abaixo do header/inset usual).
|
||||
- Reservar espaço para o input de busca (**B47**) na ordem relativa: mobile = ações → busca; `md+` = busca → ações.
|
||||
- Sem migration / collection / Consent / action.
|
||||
|
||||
## Decisões travadas
|
||||
|
||||
- **Breakpoint = viewport (`md`), não pointer.** Thumb zone é anatomia de mão + tela pequena; tablet com mouse ainda quer ações no topo. **Rejeitado:** `pointer-coarse` → bottom (um touch laptop colocaria a strip embaixo no desktop widescreen).
|
||||
- **Âncora acima da bottom nav** no mobile (não cobrir `getCampaignBottomNav` / `CampaignBottomNav` — já usa `pb-[env(safe-area-inset-bottom)]`). **Rejeitado:** `fixed bottom-0` por cima da barra.
|
||||
- **Sem FAB circular solto** — continua a strip B44. **Rejeitado:** botão único flutuante “Ações”.
|
||||
- **i18n:** classes/ids em inglês (`homeActionDock`, …); copy intacta do B45.
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **`position: sticky` vs flex column com `mt-auto` no mobile?** **Opções:** A sticky na base do scroller | B coluna full-height com strip no fim. **Recomendação:** B se o Início continuar sem scroll de conteúdo útil (blank + strip + busca); A se a 2ª dobra voltar cedo. _(assumido — medir no PR com o shell real)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Page["/campanha page.tsx"]
|
||||
Shell["CampaignPageShell / home layout"]
|
||||
SearchSlot["slot busca B47"]
|
||||
Strip["CampaignHomeActionStrip B44/B45"]
|
||||
Page --> Shell
|
||||
Shell -->|"md+: SearchSlot → Strip"| SearchSlot
|
||||
Shell -->|"lt md: Strip → SearchSlot"| Strip
|
||||
```
|
||||
|
||||
Componentes:
|
||||
|
||||
- **Layout no Início** (`src/app/(campaign)/campanha/(app)/page.tsx` + wrapper em `components/campaign/dashboard/` se o markup crescer): ordenação responsiva (`flex-col-reverse` / `md:flex-col` ou dois slots CSS `order-*`).
|
||||
- **Safe-area + offset da bottom nav** — medir altura real em `CampaignBottomNav.tsx` (fixed + safe-area); não hardcodar sem checar o shell.
|
||||
- **Migration:** Sem migration, sem collection, sem server action.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Dura: **B45** (strip montada no Início — senão não há o quê ancorar). Soft: **B44** ✓ (primitivo).
|
||||
- Soft: **B47** consome a ordem relativa (não bloqueia B46).
|
||||
|
||||
## Não escopo
|
||||
|
||||
- Input de busca e expand → **B47**.
|
||||
- Resultados / grid → **B48–B54**.
|
||||
- Wizards das ações → fatias UX-1 seguintes.
|
||||
- 2ª dobra (mapa/KPI) no Início → adiado do B43.
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
- **Redesenhar `CampaignPageShell` global.** **Mitigação:** wrapper só do Início.
|
||||
- **`dvh` / teclado virtual.** **Mitigação:** não fixar altura frágil; preferir fluxo + padding-bottom.
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
- **Reposicionar 2ª dobra sob a busca no `md+`.** Revisitar quando B45+B47 estáveis e CG pedir briefing no mesmo viewport (soft 03/08).
|
||||
|
||||
## Referências
|
||||
|
||||
- `docs/roadmap.md` (B46) · [botao-acao-inicio-strip.md](botao-acao-inicio-strip.md) · [catalogo-acoes-inicio-por-persona.md](catalogo-acoes-inicio-por-persona.md) · [fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md)
|
||||
- `src/app/(campaign)/campanha/(app)/page.tsx` · `CampaignPageShell` · `CampaignBottomNav.tsx` · `nav.ts` (`getCampaignBottomNav`)
|
||||
- `CampaignHomeActionStrip.tsx` / `CampaignHomeActionButton.tsx` (B44 ✓)
|
||||
- `PRODUCT.md` / `DESIGN.md`
|
||||
- AGENTS.md — Feel the action; naming
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue