From df48f61cd007a35f0f78a58f24edf051875ee910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Ign=C3=A1cio=20Fontoura=20Solla?= Date: Sat, 1 Aug 2026 14:50:04 -0300 Subject: [PATCH] =?UTF-8?q?docs:=20planos=20B101=E2=80=93B105=20=E2=80=94?= =?UTF-8?q?=20polish=20strip/drawer,=20crash=20busca,=20Pular,=20Sugest?= =?UTF-8?q?=C3=B5es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Registra o lote pós-B96/B99/B100 (Issues #128–#132; file-miss #133). --- .../plans/bottom-drawer-handle-scroll-peek.md | 137 ++++++++++++++++++ docs/plans/crash-busca-bottom-drawer.md | 117 +++++++++++++++ docs/plans/remover-titulo-sugestoes-busca.md | 100 +++++++++++++ docs/plans/strip-acoes-bleed-hit-area.md | 109 ++++++++++++++ docs/plans/wizard-pular-curto-encadeadas.md | 103 +++++++++++++ 5 files changed, 566 insertions(+) create mode 100644 docs/plans/bottom-drawer-handle-scroll-peek.md create mode 100644 docs/plans/crash-busca-bottom-drawer.md create mode 100644 docs/plans/remover-titulo-sugestoes-busca.md create mode 100644 docs/plans/strip-acoes-bleed-hit-area.md create mode 100644 docs/plans/wizard-pular-curto-encadeadas.md diff --git a/docs/plans/bottom-drawer-handle-scroll-peek.md b/docs/plans/bottom-drawer-handle-scroll-peek.md new file mode 100644 index 00000000..e94ee97d --- /dev/null +++ b/docs/plans/bottom-drawer-handle-scroll-peek.md @@ -0,0 +1,137 @@ +# Bottom drawer — handle, gesto, scroll, peek discreto, labels 2 linhas + +Status: registrado +Atualizado em: 2026-08-01 +Issue: #132 +Priority: P1 +Model: cursor-grok-4.5-medium +Impeccable: C — chrome `CampaignQuickActionsDrawer` (comportamento + geometria) +Appetite: ~1–1,5d eng; snap/gesto/scroll + peek busca + altura labels; sem migration +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` (thumb zone; Feel the action) / `DESIGN.md` · B79/B91/B100 · Base UI Drawer snap · tema `campaign`. + +Na implementação (`work-issue`): craft compacto → critique → polish (viewport ~390; detalhe município; scroll real). + +Brief compacto: + +- **Persona / contexto:** CG/assessor no celular fora do Início; uma mão; alterna ler a página e disparar ação/busca. +- **Job principal:** (1) handle **no topo** do sheet; (2) puxar para baixo fecha/recolhe; (3) scroll página ↓ fecha, ↑ abre; (4) collapsed ainda mostra busca **sem placeholder**; (5) títulos das ações com **2 linhas** visíveis. +- **Estratégia de cor:** Restrained. +- **Edit where you see:** não — launchers. +- **Anti-goals:** scrim modal; FAB; bottom nav; redesenhar catálogos B80–B90; absorver crash da busca (→ **B102** primeiro ou em paralelo sem bloquear geometria). + +### Wireframe (texto) + +**Dock (aberto):** + +```text +┌─ página ───────────────────────────────────────┐ +│ conteúdo │ +├─ drawer ───────────────────────────────────────┤ +│ ═ handle (TOPO) │ +│ [○][○][○]… labels em 2 linhas visíveis │ +│ ┌ Buscar na campanha (placeholder ok) ───────┐ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ +``` + +**Collapsed (fechado / após scroll ↓):** + +```text +┌─ página (mais conteúdo) ───────────────────────┐ +│ … │ +├────────────────────────────────────────────────┤ +│ ═ handle │ +│ ┌ (input sem placeholder — discreto) ────────┐ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ +``` + +**Gesto:** drag handle/sheet ↓ → collapsed; scroll content ↓ → collapsed; scroll content ↑ → dock. + +## Dados → decisão → apresentação + +Dados: N/A — chrome; hits = B48+ via provider existente. + +## Contexto + +**B100 ✓** (#114) definiu dock no load + collapse no scroll **para baixo** + reopen **só** tap/pull (explicitamente **sem** reopen no scroll-up). Follow-up `a056616d` reordenou o DOM para o handle ficar **em baixo** (“snap shows bottom band”) — produto agora rejeita: handle deve voltar ao **topo**; puxar ↓ deve recolher; scroll ↑ deve **abrir**. Collapsed deve manter a barra de busca visível **sem placeholder**. Labels `line-clamp-2` existem no botão, mas dock `12rem` + `overflow-hidden` + handle embaixo cortam a 2ª linha. + +## Objetivos + +- **Handle no topo** do `DrawerContent` (reverter ordem do `a056616d`); banda visível do snap mostra handle → ações → busca. +- **Swipe/drag para baixo** no drawer recolhe para collapsed (Base UI `swipeDirection="down"` + snap; validar que não fica “só tap”). +- **Scroll do `campaign-content-scroll`:** ↓ além do limiar → collapsed; ↑ (delta ou `scrollTop` caindo / direção) → dock. Não reabrir se busca `uiFocused` _(assumido)_. +- **Collapsed:** renderiza input de busca **visível**, `placeholder=""` (ou omitido); sem strip de ações (ou strip hidden). Tap no input / focus → dock + focus. +- **Altura do dock / overflow:** 2ª linha dos labels visível (subir `QUICK_ACTIONS_SNAP_DOCK` e/ou `overflow-y-auto` na região de ações; tirar clip que come `line-clamp-2`). +- Load / navegação: estado inicial = dock (B100). +- Pins: unit direção scroll; e2e mobile handle no topo + collapse swipe se estável. +- Sem migration / Consent. +- Tracer: load dock → scroll ↓ collapsed (busca sem placeholder) → scroll ↑ dock → labels 2 linhas no viewport. + +## Decisões travadas + +- **Supersede B100 no reopen por scroll-up e no collapsed “só handle”.** Fonte: produto 2026-08-01. **Rejeitado:** manter “reopen só tap” (B100); collapsed só handle sem busca. +- **Handle no topo — reverter o DOM order do `a056616d`.** Se a geometria do snap exigir `padding-bottom` Base UI, aplicar **sem** mover o handle para baixo. **Rejeitado:** handle embaixo como “fix” de banda visível. +- **Collapsed = handle + busca discreta (sem placeholder); ações hidden.** **Rejeitado:** collapsed = só handle (B100); esconder busca no collapsed. +- **Scroll-up reabre** com limiar de direção (delta ↑ ≥ N px) ou `scrollTop` caindo abaixo do limiar — craft escolhe o mais estável; pin no unit. **Rejeitado:** hysteresis multi-eixo complexa neste item. +- **i18n:** ids `dock`/`collapsed`; aria pt-BR; placeholder vazio no collapsed (label sr-only permanece). + +## Questões em aberto + +- **Limiar scroll-up para abrir?** **Opções:** A) espelhar 24 px de delta ↑ | B) `scrollTop < 24` (só perto do topo). **Recomendação:** **A** (simétrico ao collapse). _(assumido)_ +- **Focus no input collapsed:** expande para dock automaticamente? **Opções:** A) sim | B) só digitar. **Recomendação:** **A**. _(assumido)_ + +## Abordagem proposta + +```mermaid +flowchart TB + Scroll["campaign-content-scroll"] -->|delta down| Collapsed + Scroll -->|delta up| Dock + Handle["handle TOP"] -->|swipe down| Collapsed + Collapsed --> SearchPeek["search no placeholder"] + Dock --> Strip["actions 2-line labels"] + Dock --> SearchFull["search + placeholder"] +``` + +Componentes: + +- **`CampaignQuickActionsDrawer.tsx`**: ordem handle → context; collapsed mostra `CampaignHomeSearch` (ou slot) sem placeholder; dock mostra strip + search completa; overflow/altura. +- **`CampaignQuickActionsHost.tsx` / snap context:** listener de scroll com direção (↑/↓); respeitar `uiFocused`. +- **`campaignQuickActionSnap.ts`:** recalibrar `QUICK_ACTIONS_SNAP_COLLAPSED` / `DOCK` para caber handle+busca (collapsed) e strip 2 linhas+busca (dock). +- **`CampaignHomeSearch` / `CampaignSearchInput`:** prop opcional `placeholder` / `discreet` para collapsed. +- **E2e / unit** atualizar expectativas B100 (scroll-up agora reabre; collapsed tem busca). +- **Migration:** Sem migration. + +## Dependências + +- Soft: B100 ✓, B91 ✓. Soft: **B101** (bleed/gap da strip — pode pousar junto). Soft: **B102** (crash) — se o crash bloquear teste manual da busca no drawer, priorizar B102. +- Nenhuma dura de Issue aberta. + +## Não escopo + +- Crash focus/type → **B102**. +- Gap/padding dos botões → **B101**. +- Título “Sugestões” → **B103**. +- Catálogos de ações por rota. + +## Rabbit holes + +- **Terceiro snap “expanded” para resultados.** Mitigação: Adiado; resultados rolam no dock ou a página. +- **`position: fixed` caseiro no lugar do Base UI.** Mitigação: só se swipe+snap forem irrecuperáveis após handle no topo + padding-bottom correto. +- **Persistir snap em sessionStorage.** Mitigação: fora; reset no pathname (B100). + +## Adiado com gatilho + +- Animação spring custom além do Base UI. Revisitar se critique pedir motion. +- Peek de 1 ícone de ação no collapsed. Revisitar se a mesa achar busca-só pobre. + +## Referências + +- GitHub Issue #132 (B105) +- `CampaignQuickActionsDrawer.tsx` · `CampaignQuickActionsHost.tsx` · `campaignQuickActionSnap.ts` · `Drawer.tsx` · `docs/plans/bottom-drawer-peek-acoes-busca.md` (B100 — supersedido em parte) +- Commit `a056616d` (handle bottom — reverter ordem) +- `PRODUCT.md` / `DESIGN.md` — Field Desk chrome diff --git a/docs/plans/crash-busca-bottom-drawer.md b/docs/plans/crash-busca-bottom-drawer.md new file mode 100644 index 00000000..240b0472 --- /dev/null +++ b/docs/plans/crash-busca-bottom-drawer.md @@ -0,0 +1,117 @@ +# Crash — busca no bottom drawer (focus / digitação) + +Status: registrado +Atualizado em: 2026-08-01 +Issue: #129 (file-miss #133) +Priority: P0 +Model: cursor-grok-4.5-medium +Impeccable: B — `CampaignQuickActionsDrawer` + `CampaignGlobalSearchBody` +Appetite: ~0,5–1d eng; root-cause + fix + pin focus/type; sem migration +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` (Feel the action) · B91/B100 drawer search · tema `campaign`. + +Na implementação: reproduzir → fix mínimo → pin (unit + e2e mobile se estável). Sem craft visual além do necessário para o ritual de busca voltar. + +Brief: + +- **Persona:** staff no detalhe de município (mobile); toca “Buscar na campanha” no drawer. +- **Job principal:** focar e digitar **sem** a página cair; ver suggest/hits. +- **Anti-goals:** redesenhar o drawer (→ **B105**); silenciar o throw com try/catch cosmético. + +### Wireframe (texto) + +```text +┌─ /campanha/municipios/foo (mobile) ────────────┐ +│ conteúdo │ +├─ drawer dock ──────────────────────────────────┤ +│ ═ handle │ +│ [ações…] │ +│ ┌ Buscar na campanha ──── focus/type OK ─────┐ │ +│ │ Sugestões / hits (sem crash) │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ +``` + +## Dados → decisão → apresentação + +Dados: N/A neste item (hits já existem via B48/B68/B91); só estabilidade do chrome. + +## Contexto + +Fora do Início, o drawer (**B79–B91**, **B100 ✓**) embute `CampaignGlobalSearchBody` sob `CampaignGlobalSearchProvider` em `CampaignAppScrollChrome`. Produto (2026-08-01): **ao clicar ou começar a escrever na barra de busca, a página crasha**. + +Diagnóstico preliminar (código em `main`): + +1. Unit do drawer (`campaignQuickActionsDrawer.unit.spec.tsx`) cobre `change` → POST `mode: 'search'`, **não** `focus` → `setInputFocused(true)` → `mode: 'suggest'`. +2. `useHomeSearchResultsState` entra em `suggestMode` quando `uiFocused && !query.isActive` e monta os grupos B48 dentro do snap (`overflow-hidden`, dock `12rem`). +3. `CampaignContentScrollWithPeek` chama `useHomeSearch()` — correto só dentro do provider; árvore atual amarra provider + peek. Hipótese secundária: interação Base UI Drawer non-modal + focus/teclado / nested `Drawer` dos action buttons. +4. Runtime Vercel (server) **não** mostra o digest do crash (erro de client). + +**Miss de agente:** B100 / follow-up `a056616d` shipparam sem pin de focus+type no drawer. Registrar via `pnpm agent:file-miss` (`kind:defect` ou `agent-miss`) **neste delivery**, depois o fix. + +## Objetivos + +- Reproduzir o crash (mobile real ou Playwright viewport mobile em `/campanha/municipios/[slug]`). +- Eliminar a causa raiz (não engolir o erro). +- Pins: unit **focus** dispara suggest sem throw; unit/e2e **type** mantém search; regressão no caminho provider/`uiFocused`. +- Guardrails: sem migration; leader lockdown; Início intacto. +- Tracer: focus no input do drawer → região de resultados monta; digitar “ca” → POST search; zero uncaught. + +## Decisões travadas + +- **Issue própria P0 + file-miss, não absorver em B105.** Crash bloqueia o ritual. **Rejeitado:** “conserta no polish do drawer”. +- **Fix mínimo no caminho focus→suggest→render; não redesenhar snap.** Geometria/gesto → **B105**. **Rejeitado:** reescrever busca só para o drawer. +- **File-miss obrigatório** com causa + PR culpado quando conhecido. **Rejeitado:** só Issue de feature sem harvest. +- **i18n:** ids existentes; sem copy nova. + +## Questões em aberto + +- **Causa raiz exata antes do claim?** **Opções:** A) fechar no `work-issue` com repro primeiro | B) bipartir plan/exec. **Recomendação:** **A** — appetite cabe; hypothese já estreita. _(assumido)_ + +## Abordagem proposta + +```mermaid +flowchart LR + Focus["input onFocus"] --> Ui["uiFocused"] + Ui --> Suggest["POST mode:suggest"] + Suggest --> Groups["HomeSearch*Group"] + Groups -->|crash?| Fix["fix root"] + Type["onChange"] --> Search["POST mode:search"] +``` + +Componentes: + +- **Repro:** Playwright mobile ou unit com `fireEvent.focus` + wait suggest; ler stack. +- **Provável superfície:** `CampaignStaffGlobalSearch` / `HomeSearchResultsLayout` / `Drawer` + focus; eventualmente provider tree se achar caminho sem `HomeSearchProvider`. +- **Pins em** `tests/unit/campaignQuickActionsDrawer.unit.spec.tsx` (+ e2e `campaignMunicipalities` se estável). +- **Migration:** Sem migration. +- **File-miss:** `pnpm agent:file-miss -- --title "…" --kind defect` com body apontando B100/`a056616d` e ausência de pin focus. + +## Dependências + +- Soft: B91 ✓, B100 ✓. Nenhuma dura. **B105** pode pousar depois (gesto/handle); não bloqueia o fix. + +## Não escopo + +- Handle no topo / scroll abre / placeholder collapsed → **B105**. +- Remover título “Sugestões” → **B103** (pode coincidir visualmente no empty, mas é copy). +- Strip bleed → **B101**. + +## Rabbit holes + +- **Trocar Base UI Drawer por sheet caseiro.** Mitigação: só se a root cause for o primitivo e não houver escape hatch documentado. +- **Duplicar `CampaignHomeSearch` “safe” só para drawer.** Mitigação: consertar o shared. + +## Adiado com gatilho + +- 3º snap alto só para resultados. Revisitar se, após o fix, critique de **B105** mostrar hits cortados no dock. + +## Referências + +- GitHub Issue #129 (B102) · file-miss #133 +- `CampaignQuickActionsDrawer.tsx` · `CampaignAppScrollChrome.tsx` · `CampaignQuickActionsHost.tsx` · `HomeSearchResultsContext.tsx` · `CampaignStaffGlobalSearch.tsx` +- `tests/unit/campaignQuickActionsDrawer.unit.spec.tsx` +- AGENTS.md — Feel the action; client boundary diff --git a/docs/plans/remover-titulo-sugestoes-busca.md b/docs/plans/remover-titulo-sugestoes-busca.md new file mode 100644 index 00000000..dc385fe5 --- /dev/null +++ b/docs/plans/remover-titulo-sugestoes-busca.md @@ -0,0 +1,100 @@ +# Remover título “Sugestões” no empty state da busca + +Status: registrado +Atualizado em: 2026-08-01 +Issue: #130 +Priority: P2 +Model: composer-2.5 +Impeccable: B — `HomeSearchMunicipalityGroup` (modo suggest) +Appetite: ~0,25d eng; copy/CSS; sem migration +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` (Clarity under pressure) · B68 suggest · tema `campaign`. + +Na implementação: craft mínimo → critique → polish (Início focus vazio). + +Brief: + +- **Persona:** staff foca a busca no Início; já vê a lista curada. +- **Job principal:** ir aos hits sem cabeçalho decorativo. +- **Anti-goals:** remover o grupo “Municípios” no modo search; esconder `aria-label` acessível. + +### Wireframe (texto) + +```text +Antes (focus, query vazia): + Sugestões ← h2 + linha/espaço + ──────────────── + Cairu … + … + +Depois: + Cairu … + … +``` + +## Dados → decisão → apresentação + +Dados: N/A — só chrome do empty; ranking B68 intacto. + +## Contexto + +**B68 ✓** mostra seção com `sectionTitle = resultKind === 'suggest' ? 'Sugestões' : 'Municípios'` e `h2` via `HOME_SEARCH_GROUP_HEADING_CLASS` em `HomeSearchMunicipalityGroup`. Produto (2026-08-01): no empty/suggest do Início, o título **“Sugestões”** e a linha/espaço abaixo **não adicionam valor** — só ocupam thumb zone. + +## Objetivos + +- No `resultKind === 'suggest'`: **não renderizar** o `h2` “Sugestões” (nem regra visual equivalente). +- Manter `aria-label` no `
` (ou no list) para leitores de tela — “Sugestões” pode ficar só no accessible name. +- Modo `search`: título **“Municípios”** permanece _(assumido — pedido só citou suggest)_. +- Atualizar unit `homeSearchUi` / group spec se pinarem o heading. +- Sem migration. + +## Decisões travadas + +- **Só sumir o heading visual no suggest; hits B68 ficam.** **Rejeitado:** matar o empty state inteiro; renomear para outro título. +- **A11y: nome acessível sem `h2` visível.** **Rejeitado:** section muda sem nome. +- **i18n:** string “Sugestões” some da UI visível; id `sectionTitle` / `aria-label` ok. + +## Questões em aberto + +- **Modo search também perde “Municípios”?** **Opções:** A) não — só suggest | B) sim, todos os grupos sem h2. **Recomendação:** **A** (pedido explícito). _(assumido)_ + +## Abordagem proposta + +```mermaid +flowchart LR + Group["HomeSearchMunicipalityGroup"] --> Kind{"resultKind"} + Kind -->|suggest| NoH2["section aria-label only"] + Kind -->|search| H2["h2 Municípios"] +``` + +Componentes: + +- **`HomeSearchMunicipalityGroup.tsx`**: condicional no `h2`; `aria-label` sempre. +- **Tests** que assertam o texto “Sugestões” visível → ajustar. +- **Migration:** Sem migration. + +## Dependências + +- Soft: B68 ✓. Nenhuma dura. + +## Não escopo + +- Outros group headings (Lideranças, …) no modo search. +- Crash drawer → **B102**. + +## Rabbit holes + +- **Restyling de todos os group headings da busca.** Mitigação: um `if` no suggest. + +## Adiado com gatilho + +- Remover headings em todos os grupos no mobile. Revisitar se critique da busca pedir lista “flat”. + +## Referências + +- GitHub Issue #130 (B103) +- `HomeSearchMunicipalityGroup.tsx` · `src/lib/homeSearchUi.ts` · `docs/plans/sugestoes-busca-vazia-inicio.md` (B68) +- `PRODUCT.md` — Clarity under pressure diff --git a/docs/plans/strip-acoes-bleed-hit-area.md b/docs/plans/strip-acoes-bleed-hit-area.md new file mode 100644 index 00000000..d164f778 --- /dev/null +++ b/docs/plans/strip-acoes-bleed-hit-area.md @@ -0,0 +1,109 @@ +# Strip de ações — bleed sem faixa branca + hit area via padding + +Status: registrado +Atualizado em: 2026-08-01 +Issue: #128 +Priority: P1 +Model: composer-2.5 +Impeccable: B — `CampaignHomeActionStrip` / `CampaignHomeActionButton` (Início + drawer) +Appetite: ~0,5d eng; CSS + pin de classes; sem migration +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` / `DESIGN.md` · B44/B67/B99 strip · tema `campaign`. + +Na implementação (`work-issue`): craft compacto → critique → polish (iPhone estreito; Início **e** drawer). + +Brief: + +- **Persona / contexto:** assessor/CG no mobile; polegar na strip. +- **Job principal:** strip visualmente edge-to-edge (sem “trilho” branco lateral), primeiro botão **não** colado na borda, espaçamento entre ações apertado com área clicável generosa. +- **Estratégia de cor:** Restrained. +- **Edit where you see:** não — launchers. +- **Anti-goals:** encolher hit <44 px; quebrar pan B67; `p-0` global na página; segundo componente de strip. + +### Wireframe (texto) + +```text +┌─ viewport ─────────────────────────────────────┐ +│████ strip full-bleed (bg até a borda) ████████│ +│ ┌ px interno ┐ ○ ○ ○ ○ →→ │ +│ │ │ labels │ +│ └────────────┘ (gap ul = 0; espaço = padding │ +│ horizontal do botão) │ +└────────────────────────────────────────────────┘ + Mesmo contrato no Início e no drawer (B105). +``` + +## Dados → decisão → apresentação + +Dados: N/A — chrome de launcher. + +## Contexto + +**B99 ✓** (#107) entregou `-mx-4` no slot `home-actions` + `gap-2` no `