mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
docs: register B73–B75 (bottom nav, strip gap v2, wizard mobile header).
This commit is contained in:
parent
5b27608aac
commit
fbcdd3a4fa
4 changed files with 367 additions and 8 deletions
86
docs/plans/espacamento-strip-acoes-inicio-v2.md
Normal file
86
docs/plans/espacamento-strip-acoes-inicio-v2.md
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# Apertar mais o espaçamento da strip de ações do Início
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-30
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B — **B74**; chassis UX-1 / pós-B72)
|
||||
Impeccable: B — uma linha de layout em `CampaignHomeActionStrip`
|
||||
Appetite: ~0,25d eng; token Tailwind; sem migration
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` / `DESIGN.md` · tema `campaign`.
|
||||
|
||||
Na implementação: craft compacto → critique → polish (6 ícones staff no iPhone estreito + pan B67 ✓; labels de duas linhas em `w-[4.75rem]`).
|
||||
|
||||
Brief compacto:
|
||||
|
||||
- **Persona:** assessor/CG na faixa horizontal do Início.
|
||||
- **Job principal:** caber mais ações por viewport sem colisão de rótulo.
|
||||
- **Estratégia de cor:** inalterada.
|
||||
- **Edit where you see:** não.
|
||||
- **Anti-goals:** encolher o círculo abaixo de 44 px; mexer no pan (B67); reabrir labels do B58.
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
Dados: N/A — atalhos, sem métricas.
|
||||
|
||||
## Contexto
|
||||
|
||||
**B72 ✓** baixou `gap-6` → `gap-4` em `CampaignHomeActionStrip` (`<ul className="… gap-4 …">`). Feedback de produto (2026-07-30): ainda **grande demais** — a strip continua esparça no mobile.
|
||||
|
||||
B72 havia listado `gap-3` como opção A e escolhido `gap-4` por cautela com labels de duas linhas; o campo agora pede o próximo degrau.
|
||||
|
||||
## Objetivos
|
||||
|
||||
- Reduzir o `gap` horizontal da lista (recomendação: `gap-4` → **`gap-3`**; critique pode ir a `gap-2` se ainda parecer largo e os rótulos não colidirem).
|
||||
- Manter: `snap-x`, pan touch + drag fine (B67), scrollbar oculta, `w-[4.75rem]` / círculo 48 px (B58).
|
||||
- Sem migration, collection, server action ou mudança de catálogo.
|
||||
|
||||
## Decisões travadas
|
||||
|
||||
- **Só `gap`, não largura do botão.** **Rejeitado:** `w` menor ou círculo <44 px (SC 2.5.5 / B72).
|
||||
- **Um valor único em coarse e fine.** **Rejeitado:** `gap-2 md:gap-4` (inconsistência sem evidência).
|
||||
- **Item novo (B74), não reabrir B72 entregue.** O as-built de B72 fica histórico; este é o ajuste fino pedido após uso. **Rejeitado:** editar só o plano B72 sem ID (some do grafo UX-1).
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **`gap-3` ou `gap-2`?** **Opções:** A `gap-3` (12 px) | B `gap-2` (8 px). **Recomendação:** **A** no craft; critique sobe para B se o viewport de 6 ações staff ainda exigir pan excessivo e o truncamento de label continuar legível. _(assumido — validar no craft)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Strip["CampaignHomeActionStrip<br/>ul gap-3"]
|
||||
Btn["CampaignHomeActionButton × N"]
|
||||
Strip --> Btn
|
||||
```
|
||||
|
||||
Componentes:
|
||||
|
||||
- **`src/components/campaign/dashboard/CampaignHomeActionStrip.tsx`**: `gap-4` → `gap-3` (ou `gap-2` pós-critique).
|
||||
- **Migration:** Sem migration.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Dura: **B72 ✓** (ponto de partida). Soft: B67 ✓ (pan continua após o aperto).
|
||||
|
||||
## Não escopo
|
||||
|
||||
- Chrome do wizard mobile → **B75**. Remoção da bottom nav → **B73**. Rótulos/animação → B58 ✓.
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
- **Comprimir o botão “porque o gap sozinho não basta”.** Mitigação: só gap neste item; se ainda faltar viewport, novo pedido de produto (não misturar).
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
Nenhum neste item.
|
||||
|
||||
## Referências
|
||||
|
||||
- `src/components/campaign/dashboard/CampaignHomeActionStrip.tsx`
|
||||
- [espacamento-strip-acoes-inicio.md](espacamento-strip-acoes-inicio.md) (B72 ✓)
|
||||
- [restaurar-pan-strip-acoes-inicio.md](restaurar-pan-strip-acoes-inicio.md) (B67 ✓)
|
||||
|
||||
Qualidade de decisão: 5/5
|
||||
135
docs/plans/header-mobile-wizard-campanha.md
Normal file
135
docs/plans/header-mobile-wizard-campanha.md
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
# Header mobile do wizard (chrome no Mandate Red)
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-30
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B — **B75**; chassis UX-1 / emenda a B59)
|
||||
Impeccable: C — chrome novo no shell `(app)` durante `/campanha/acoes/*`; brief abaixo
|
||||
Appetite: ~1–1,25d eng; provider + header condicional + shell sem chrome duplicado no mobile; sem migration
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` (Clarity under pressure; Feel the action) / `DESIGN.md` (Mandate Red mobile top bar) · tema `data-theme='campaign'`.
|
||||
|
||||
Na implementação (`implement-roadmap-item`): shape → craft → critique → polish.
|
||||
|
||||
Brief compacto:
|
||||
|
||||
- **Persona / contexto:** CG/assessor no celular entre Zaps; entrou numa ação do Início; o header vermelho do app (sidebar + “Jorge Solla”) **compete** com Voltar + título do passo que hoje moram no conteúdo (`CampaignWizardShell`).
|
||||
- **Job principal:** durante o wizard, o header Mandate Red vira **navegação do fluxo** (sair / voltar / pular encadeado + título da ação + município), sem segundo sticky no scroll.
|
||||
- **Estratégia de cor:** Restrained — **mesmo** `bg-primary` / `text-primary-foreground` do header mobile atual; ícones/links herdam contraste do header (não inventar barra branca no topo).
|
||||
- **Edit where you see:** não — chrome de fluxo.
|
||||
- **Anti-goals:** dois headers sticky (app + shell); sidebar acessível no meio do ritual; X e “Pular” ambíguos; desktop ganhando chrome vermelho; Sheet full-screen no lugar de rota (B59).
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
Dados: N/A — chrome; conteúdo numérico nas etapas B60–B64 / B70.
|
||||
|
||||
## Contexto
|
||||
|
||||
**B59 ✓** entregou `CampaignWizardShell` com:
|
||||
|
||||
- sticky **branco** no topo do conteúdo: Voltar (`md:hidden`) | caption município | `trailingAction`;
|
||||
- `h1` = pergunta do passo no `<main>`.
|
||||
|
||||
O layout `(app)` **sempre** renderiza acima disso o header vermelho mobile (`SidebarTrigger` + “Jorge Solla” / “Campanha · Bahia”) em [`layout.tsx`](../../src/app/(campaign)/campanha/(app)/layout.tsx). Em `/campanha/acoes/<slug>` o usuário vê **dois** chrome empilhados — o vermelho é irrelevante ao ritual e come viewport.
|
||||
|
||||
Pedido de produto (2026-07-30):
|
||||
|
||||
| Estado | Esquerda | Centro (título / subtítulo) | Direita |
|
||||
| ------ | -------- | --------------------------- | ------- |
|
||||
| 1ª etapa do fluxo | — | **fluxo** (ex. “Ajustar votos”); subtítulo município **só depois** de escolhido | **X** (sair do wizard → Início / origem) |
|
||||
| 2ª+ etapa | **Voltar** | fluxo + município (se houver) | **X** |
|
||||
| Fluxo **encadeado** (ex. sinal depois de votos no A1) | Voltar (se não for 1ª do subfluxo) | título do **subfluxo** atual | **“Pular <fluxo>”** no lugar do X |
|
||||
|
||||
A pergunta do passo (“Em qual município?”, grid de tipos…) permanece no **conteúdo** (`h1`), não no header.
|
||||
|
||||
Skip de B63/B64/B70 (“Pular registro de sinal…”) **migra** para este slot direito no mobile quando a origem for encadeada; no entry standalone o direito é X.
|
||||
|
||||
## Objetivos
|
||||
|
||||
- Em viewport **< `md`**, rotas `/campanha/acoes/*`: substituir o header app (sidebar + marca) por **header de wizard** Mandate Red com a tabela acima.
|
||||
- Em **`md+`**: header slim atual (só `SidebarTrigger`) **inalterado**; shell pode manter caption de município discreta **ou** só no conteúdo — sem Voltar duplicado (browser). Recomendação: no desktop, sem barra vermelha de wizard; `h1` + caption opcional no conteúdo como hoje.
|
||||
- Remover do `CampaignWizardShell` o sticky branco de Voltar/município/`trailingAction` **no mobile** (evita chrome duplo); no desktop, `trailingAction` de skip pode ficar no conteúdo até o wiring A1 — ou sumir se só mobile importa (recomendação: skip só no header mobile; desktop = link textual no conteúdo se B63+ precisar).
|
||||
- Provider/contrato tipado para o shell **publicar** chrome (`flowTitle`, `municipalityLabel?`, `stepKind: 'entry' | 'continue'`, `previousHref?`, `dismissHref`, `skip?: { label, href }`).
|
||||
- a11y: X com nome “Fechar” / “Sair da ação”; Voltar com verbo visível; “Pular …” contém o nome do fluxo; foco no `h1` do passo preservado (B59).
|
||||
- Guardrails: sem migration / Consent / action de escrita; print:hidden; staff gate de `acoes/layout` intacto.
|
||||
|
||||
## Decisões travadas
|
||||
|
||||
- **Arquitetura: chrome condicional no layout `(app)` + context publicado pelo shell (não um segundo header solto, não portal ad-hoc).** Extrair o header mobile atual para um componente client (`CampaignMobileTopBar`) que, se houver chrome de wizard ativo, renderiza o modo wizard; senão o modo app. `CampaignWizardChromeProvider` envolve só `acoes/layout.tsx` (ou o `(app)` inteiro com default `null`). O shell **registra** o chrome via `useCampaignWizardChromeSet` / efeito; cleanup no unmount. **Rejeitado:** A) layout route group paralelo `(wizard)` fora de `(app)` (duplica auth/sidebar/Toaster/PWA; caro de manter); B) portal `createPortal` no `document` sem contrato (frágil com RSC/scroll); C) só CSS `hidden` no header app sem substituir (some a affordance de sair); D) Sheet full-screen (B59 já rejeitou).
|
||||
- **Título do header = nome da ação/fluxo (catálogo B45), não a pergunta do passo.** Fonte: produto 2026-07-30. **Rejeitado:** repetir `stepTitle` no header (some hierarquia; B59 já separou pergunta × contexto).
|
||||
- **Subtítulo = município só após escolha (B60).** Ausente na 1ª etapa de busca. **Rejeitado:** slug cru; “Município” genérico.
|
||||
- **1ª etapa: só X à direita (sem Voltar).** X → `dismissHref` (default Início). **Rejeitado:** Voltar na 1ª (= mesmo que X, confunde); hamburger + X.
|
||||
- **2ª+: Voltar à esquerda + X à direita.** Voltar = `previousHref` (passo anterior). **Rejeitado:** só gesture edge; esconder X após a 1ª (usuário precisa abortar no meio).
|
||||
- **Encadeado: X vira “Pular <fluxo>”.** Quando `skip` está definido, o slot direito é o skip (não X+Pular). Fonte: produto 2026-07-30 + planos B63/B64/B70. **Rejeitado:** X e Pular juntos (dois escapes); Pular no conteúdo e X no header (split mental).
|
||||
- **Sidebar inacessível durante o wizard mobile** (trigger some com o chrome app). Sair = X ou concluir. **Rejeitado:** manter SidebarTrigger no canto (reabre domínio-primeiro no meio do ritual — alinhado a B73).
|
||||
- **i18n:** ids `CampaignWizardChrome`, `flowTitle`, `dismissHref`, `skipLabel`; copy pt-BR (“Pular registro de sinal”, nomes do catálogo).
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **Desktop: caption município no conteúdo ou só no header inexistente?** **Opções:** A caption muted acima do `h1` no `md+` | B só contexto na URL/query. **Recomendação:** **A** — paridade com B59 desktop; header vermelho wizard é mobile-only. _(assumido)_
|
||||
- **`stepKind` derivado de `previousHref` vs prop explícito?** **Opções:** A prop `isEntryStep` | B `previousHref == null` ⇒ entry. **Recomendação:** **A** — Voltar e dismiss não são o mesmo href na 1ª (dismiss=Início; não há previous); entry ≠ “sem previous”. _(assumido)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
AppLayout["(app)/layout.tsx"] --> TopBar["CampaignMobileTopBar"]
|
||||
TopBar -->|chrome null| AppMode["SidebarTrigger + marca"]
|
||||
TopBar -->|chrome set| WizMode["Voltar? + títulos + X|Pular"]
|
||||
AcoesLayout["acoes/layout Provider"] --> Shell["CampaignWizardShell"]
|
||||
Shell -->|setChrome| Ctx["CampaignWizardChromeContext"]
|
||||
Ctx --> TopBar
|
||||
Shell --> Main["h1 stepTitle + children"]
|
||||
```
|
||||
|
||||
Componentes:
|
||||
|
||||
- **`CampaignWizardChromeContext`** (`src/components/campaign/shell/` ou `shared/`): tipo `CampaignWizardChromeState | null`; provider; `useSetCampaignWizardChrome(state)` com sync em `useEffect` + clear on unmount (evitar chrome órfão na navegação Início←ações).
|
||||
- **`CampaignMobileTopBar`**: client; lê context; modo app vs wizard; tokens `bg-primary` / foreground; `min-h-14`; safe-area top; `print:hidden`; `md:hidden` no modo app **e** no modo wizard (desktop inalterado).
|
||||
- **`(app)/layout.tsx`**: trocar o `<header className="… bg-primary … md:hidden">` inline por `<CampaignMobileTopBar />` (provider precisa ancestrar o top bar — **subir** o provider para `(app)/layout` com default `null`, ou nestar top bar dentro de um client shell que também envolve `children`). Preferência: **client island `CampaignAppChrome`** wrapping header+children slot pattern só o necessário; evitar marcar o layout RSC inteiro como client.
|
||||
- Padrão prático: `SidebarInset` recebe `<CampaignWizardChromeProvider>` no `(app)/layout` (provider leve) + `CampaignMobileTopBar` + `{children}` — provider no layout RSC via componente client que só passa children.
|
||||
- **`acoes/layout.tsx`**: sem mudança de gate; opcional `data-wizard-route` se útil a testes.
|
||||
- **`CampaignWizardShell`**: props novas `flowTitle`, `isEntryStep`, `dismissHref`, `skip?: { label, href }`; no mobile **não** renderiza sticky branco; chama setChrome; `h1` + children permanecem; `md+` pode mostrar caption município se `municipalityLabel`.
|
||||
- **Copy de fluxo:** reusar labels de `campaignHomeActions.ts` (ex. “Ajustar votos”) via mapa `slug → flowTitle` em `campaignWizardCopy.ts` ou `campaignActionRoutes.ts` — uma fonte, não string solta por página.
|
||||
- **Testes:** unit do top bar (entry / continue / skip); atualizar `campaignWizardShell.unit.spec.tsx` (back some do shell no mobile — testar via context ou `data-slot` no top bar); e2e smoke: abrir ação → header sem sidebar → X volta Início; 2ª etapa → Voltar.
|
||||
- **Migration:** Sem migration, sem collection, sem server action.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Dura: **B59 ✓** (shell + rotas `/campanha/acoes`). Soft: **B60 ✓** (município no subtítulo); **B45 ✓** (labels de fluxo); planos B63/B64/B70 (passam a alimentar `skip` no header em vez de `trailingAction` no sticky branco). Soft **B73** (remove bottom nav — não bloqueia).
|
||||
- Desbloqueia polish mobile dos wizards B61/B63/B64/B70 (sem este item, cada um herda chrome quebrado).
|
||||
|
||||
## Não escopo
|
||||
|
||||
- Orquestração completa A1 “Quer também…?” (wiring de cadeia) — este item só **hospeda** o chrome quando a cadeia existir; a máquina de passos continua nos planos A1/B63+.
|
||||
- Remoção da bottom nav → **B73**. Gap da strip → **B74**.
|
||||
- Redesign do header desktop slim.
|
||||
- Persistência de rascunho (Adiado B59).
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
- **Route group `(wizard)` fora de `(app)`.** Explode auth/PWA/sidebar. **Mitigação:** context no inset.
|
||||
- **State machine global de wizard.** **Mitigação:** chrome burro; hrefs por etapa.
|
||||
- **Animar troca app↔wizard header.** **Mitigação:** swap imediato; motion só se critique pedir e com `prefers-reduced-motion`.
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
- **Skip no desktop no mesmo visual do header.** Revisitar se B63+ shippar e a mesa reclamar que “Pular” sumiu no notebook (hoje browser + conteúdo bastam).
|
||||
- **Deep-link “voltar ao Zap” com dismiss ≠ Início.** Revisitar se entry points fora do Início (E11 → wizard) pedirem `dismissHref` dinâmico — o prop já existe; falta produto.
|
||||
|
||||
## Referências
|
||||
|
||||
- `src/app/(campaign)/campanha/(app)/layout.tsx` — header vermelho mobile
|
||||
- `src/components/campaign/shared/CampaignWizardShell.tsx` — sticky atual a retirar no mobile
|
||||
- `src/app/(campaign)/campanha/(app)/acoes/layout.tsx` — gate staff
|
||||
- `src/lib/campaignHomeActions.ts` — labels de fluxo
|
||||
- `src/lib/campaignWizardCopy.ts` / `campaignActionRoutes.ts`
|
||||
- [chassis-wizard-campanha.md](chassis-wizard-campanha.md) (B59 ✓) — emenda chrome mobile
|
||||
- [wizard-registro-sinal.md](wizard-registro-sinal.md) / [wizard-mudar-tendencia.md](wizard-mudar-tendencia.md) / [wizard-atualizar-lideranca.md](wizard-atualizar-lideranca.md) — skip encadeado
|
||||
- [fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md)
|
||||
- `PRODUCT.md` / `DESIGN.md` — Mandate Red / Field Desk
|
||||
- AGENTS.md — naming; sem Consent novo
|
||||
|
||||
Qualidade de decisão: 4/5
|
||||
119
docs/plans/remover-bottom-nav-mobile.md
Normal file
119
docs/plans/remover-bottom-nav-mobile.md
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
# Remover bottom navigation bar no mobile
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B — **B73**; chassis UX-1)
|
||||
Impeccable: B — encaixe no shell `(app)`; remove chrome, não cria rota nova
|
||||
Appetite: ~0,25–0,5d eng; remover componente + ajustar padding do scroll + atualizar testes; sem migration
|
||||
Responsável: —
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` (register `product` — clareza sob pressão; UX-1 ação-primeiro) / `DESIGN.md` (hybrid mobile top bar Mandate Red + sidebar Sheet) · tema `data-theme='campaign'`.
|
||||
|
||||
Na implementação (`implement-roadmap-item`): craft compacto → critique → polish (só chrome do shell).
|
||||
|
||||
Brief compacto:
|
||||
|
||||
- **Persona / contexto:** CG/assessor no celular — o ritual diário passa pelo Início (ações + busca); listas densas são visita ocasional, não navegação por domínio.
|
||||
- **Job principal:** recuperar altura útil do viewport e deixar um único caminho de navegação secundária (sidebar via `SidebarTrigger` no header vermelho).
|
||||
- **Estratégia de cor:** Restrained — remove barra fixa; header mobile existente permanece.
|
||||
- **Edit where you see:** não — só chrome.
|
||||
- **Anti-goals:** segunda barra de atalhos; reinventar drawer fora do `Sidebar`; mover destinos para tabs no Início; exigir sidebar aberta por padrão no mobile.
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
Dados: N/A — remoção de chrome de navegação; nenhuma métrica/série/mapa.
|
||||
|
||||
## Contexto
|
||||
|
||||
O shell de `/campanha` monta hoje **duas** superfícies de navegação no mobile (<`md`):
|
||||
|
||||
1. **Header vermelho** com `SidebarTrigger` → `CampaignSidebar` em Sheet (`collapsible="offcanvas"` no `CampaignSidebar`, B38 ✓).
|
||||
2. **`CampaignBottomNav`** fixa (`src/components/campaign/shell/CampaignBottomNav.tsx`) com até 5 destinos derivados de `getCampaignBottomNav` em `nav.ts` — subconjunto de `staffNav`/`leaderNav` (exclui Quadro, Territórios, Apoiadores, Assessores).
|
||||
|
||||
O scroll global reserva espaço com `pb-24` em `src/app/(campaign)/campanha/(app)/layout.tsx` para não cobrir conteúdo com a bottom bar. O chassis UX-1 (**B43–B47 ✓**, **B65 ✓**) ancorou busca + strip de ações **acima** dessa barra.
|
||||
|
||||
**Decisão de produto (2026-07-29):** o paradigma deixou de ser “navegar por domínio” (Início / Municípios / Lideranças / … na barra inferior) e passou a ser **intenção** (ações do Início, busca, wizards). A bottom bar só consome ~48–56 px + safe-area sem servir ao ritual diário. A navegação ocasional para listas de alto volume permanece no **sidebar** (fechado por padrão no mobile via `CampaignSidebarViewportDefault` quando não há cookie `sidebar_state`).
|
||||
|
||||
Evidência alinhada: [fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md) — anti-goal “depender de sidebar para o ritual diário”; a barra inferior era o espelho mobile do modelo domínio-primeiro.
|
||||
|
||||
## Objetivos
|
||||
|
||||
- Em viewport **< `md`**: **não renderizar** `CampaignBottomNav`; navegação secundária só via `SidebarTrigger` + Sheet.
|
||||
- Remover o padding inferior artificial (`pb-24`) do `campaign-content-scroll` no mobile — substituir por padding normal (`p-4` / `pb-4` ou equivalente) com safe-area só onde necessário (toasts/PWA, não nav fantasma).
|
||||
- Revalidar o dock do Início (**B65 ✓**): com a barra sumida, o grupo strip+busca pode usar o limite inferior real do scrollport; medir se o spacer `flex-1` ainda precisa de ajuste.
|
||||
- Sidebar mobile continua **fechada por padrão** (`CampaignSidebarViewportDefault` + cookie) — sem mudar o default tablet/desktop (B38 ✓).
|
||||
- Apagar código morto: `CampaignBottomNav.tsx`, `getCampaignBottomNav`, exports órfãos; `pnpm exec knip` limpo.
|
||||
- Guardrails: sem migration, sem collection, sem Consent, sem server action; `print:hidden` preservado; leader/staff `getCampaignNav` intactos.
|
||||
|
||||
## Decisões travadas
|
||||
|
||||
- **Remover a bottom bar por completo — não esvaziar nem reduzir ícones.** O produto não quer atalhos de domínio no mobile; busca + ações cobrem o caminho frequente. **Rejeitado:** manter 2–3 ícones (ainda compete com thumb zone); mover Municípios/Lideranças para tabs no Início (segundo sistema de nav); PWA shortcut bar nativa (fora de escopo).
|
||||
- **Sidebar como única navegação estrutural no mobile.** O `SidebarTrigger` no header vermelho já existe; não adicionar segundo gatilho flutuante. **Rejeitado:** FAB “Menu”; gesto edge-only sem affordance; abrir sidebar por padrão no mobile (contradiz B38 + campo com uma mão).
|
||||
- **Apagar `getCampaignBottomNav` com o componente.** Não deixar helper “para o dia em que redesenharmos a barra” — git history é o arquivo. Testes que citavam a barra passam a assertar só `getCampaignNav` + `getCampaignSecondaryNav`. **Rejeitado:** deprecar com `@deprecated` (knip/CI bloqueia export morto).
|
||||
- **Ajustar `pb-24` no mesmo PR.** O padding existia só pela altura da barra; mantê-lo após remoção devolve gap morto e contradiz B65. **Rejeitado:** `pb-24` “por segurança” sem medir (o dock do Início pode precisar de `pb` menor local, não global).
|
||||
- **i18n e naming** seguem o AGENTS.md: identificadores em inglês (`CampaignBottomNav` deletado; layout classes em inglês); copy visível em pt-BR no sidebar (`aria-label` do trigger já existe).
|
||||
|
||||
## Questões em aberto
|
||||
|
||||
- **Reduzir `pb` global ou só revalidar o Início?** **Opções:** A) trocar `pb-24` → `pb-4` no scroll global | B) manter `pb-24` nas outras rotas e só no Início usar dock | C) `pb` proporcional a safe-area apenas. **Recomendação:** **A** no layout global — a barra era a única razão do 24; listas longas ganham área útil; se alguma rota com FAB/toast colidir, corrigir pontualmente. _(assumido — validar no craft com device real)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
layout["(app)/layout.tsx"] --> scroll["campaign-content-scroll"]
|
||||
layout --> header["header mobile + SidebarTrigger"]
|
||||
layout --> sidebar["CampaignSidebar Sheet"]
|
||||
layout -.remove.-> bottom["CampaignBottomNav DELETED"]
|
||||
scroll --> pages["rotas /campanha/*"]
|
||||
home["Início B65 dock"] --> scroll
|
||||
```
|
||||
|
||||
Componentes:
|
||||
|
||||
- **`src/app/(campaign)/campanha/(app)/layout.tsx`**: remover import/render de `CampaignBottomNav`; ajustar `className` do scroll (`pb-24` → `pb-4` ou `p-4` simétrico no mobile); confirmar que `SidebarTrigger` no header mobile permanece.
|
||||
- **Deletar `src/components/campaign/shell/CampaignBottomNav.tsx`**.
|
||||
- **`src/components/campaign/shell/nav.ts`**: remover `getCampaignBottomNav` e o comentário do cap de 5 itens; manter `getCampaignNav` / `getCampaignSecondaryNav` / `MUNICIPALITY_NAV_HREF`.
|
||||
- **`src/components/campaign/shell/CampaignSidebarViewportDefault.tsx`**: sem mudança de comportamento (já fecha sidebar <1024 sem cookie) — só verificar que mobile continua com default fechado.
|
||||
- **Início (`CampaignHomeLayout` / `CampaignHomeStaffChrome`)**: smoke visual após remoção da barra — o dock pode colar mais baixo; ajustar spacer se sobrar gap >~8 px (herança do B65).
|
||||
- **`tests/unit/campaignIntelligenceConcepts.unit.spec.ts`**: teste “stays out of … mobile bottom bar” → assertar que `CAMPAIGN_CONCEPTS_PATH` não está em `getCampaignNav` (bottom bar deixa de existir).
|
||||
- **E2E smoke**: navegar via sidebar no mobile viewport — abrir Sheet, ir a Municípios, voltar (nenhum `nav[aria-label="Navegação principal"]` no DOM).
|
||||
- **Sem migration, sem collection, sem server action.**
|
||||
|
||||
## Dependências
|
||||
|
||||
- **B43 ✓** (Início ação-primeiro) — decisão de paradigma; sem ela a barra ainda faria sentido como atalho de domínio.
|
||||
- **B65 ✓** (soft) — o dock foi desenhado acima da bottom nav; este item **revisita** o padding/âncora, não bloqueia o merge.
|
||||
- **B38 ✓** (soft) — sidebar Sheet + default fechado no mobile/tablet sem cookie.
|
||||
- Nenhuma dependência dura de item pendente.
|
||||
|
||||
## Não escopo
|
||||
|
||||
- Redesenhar itens do `staffNav` / reordenar sidebar — só remover a projeção mobile de 5 ícones.
|
||||
- Atalhos de domínio no Início (busca B48+ e ações B45 ✓ já cobrem).
|
||||
- Mudar bottom nav em design-refs HTML legados (`docs/design-refs/latest/*.html` com `pb-24` estático) — artefatos UX Pilot, não runtime.
|
||||
- PWA install banner / `InstallPwaToast` — inalterados.
|
||||
|
||||
## Rabbit holes
|
||||
|
||||
- **Reintroduzir navegação por gestos sem affordance.** Se alguém “só documentar” que o usuário deve arrastar: sem `SidebarTrigger` visível, a descoberta cai. **Mitigação:** manter trigger no header; e2e cobre o caminho.
|
||||
- **Padding inferior em cascata.** Copiar `pb-24` para o dock do Início “por precaução” recria o bug que este item remove. **Mitigação:** um único `pb` no scroll global; ajuste local só com medição.
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
- **Atalho “recentes” na sidebar** (visitados) para compensar menos um toque na bottom bar. Revisitar quando: métrica de sessão mostrar >3 aberturas de sidebar só para repetir os mesmos 2 destinos — hoje busca + ações cobrem.
|
||||
|
||||
## Referências
|
||||
|
||||
- `docs/roadmap.md` (UX-1, B73)
|
||||
- `src/app/(campaign)/campanha/(app)/layout.tsx` — montagem do shell e `pb-24`
|
||||
- `src/components/campaign/shell/CampaignBottomNav.tsx` — alvo de remoção
|
||||
- `src/components/campaign/shell/nav.ts` — `getCampaignBottomNav`
|
||||
- `src/components/campaign/shell/CampaignSidebarViewportDefault.tsx` — default fechado mobile
|
||||
- `docs/plans/ancorar-busca-acoes-inicio-mobile.md` — decisões que citavam a bottom nav (contexto histórico B65)
|
||||
- `docs/plans/fluxos-acao-primeiro-inicio.md` — anti-goal sidebar no ritual diário
|
||||
- `docs/plans/sidebar-recolhido-tablet.md` — B38, mobile Sheet inalterado
|
||||
- `PRODUCT.md` — princípios 2 e 3 (clareza sob pressão; edit where you see)
|
||||
- AGENTS.md — Campaign auth shell; knip mata órfãos no mesmo PR
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue