From ad39602d042e5dc9dda6c6f78815a356cfd9f311 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Sun, 9 Aug 2026 02:15:22 -0300 Subject: [PATCH] feat(C94): toolbar omnibox + e2e agenda hardening + changelog --- .gitignore | 1 + docs/CHANGELOG-AGENTS.md | 5 +++++ ...enda-combobox-header-acoes-rapidas-impl.md | 20 +++++++++++-------- .../activity/ActivityAgendaFilters.tsx | 4 ++-- tests/e2e/campaignActivity.e2e.spec.ts | 6 +++--- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 0baa69e7..2ddff5af 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ /playwright-report/ /test-results/ /.playwright-cli/ +/.playwright-mcp/ # Aider (local agent history + tag cache) .aider* diff --git a/docs/CHANGELOG-AGENTS.md b/docs/CHANGELOG-AGENTS.md index eeb2675f..3c9427ea 100644 --- a/docs/CHANGELOG-AGENTS.md +++ b/docs/CHANGELOG-AGENTS.md @@ -12,6 +12,11 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente **Recently resolved (2026-08-08):** **B174** (Issue #449) — a Sollinha (`/campanha/api/ai-chat`) trocou o provider de `@ai-sdk/deepseek` (API oficial `api.deepseek.com`) pelo **`@ai-sdk/deepinfra`** no caminho OpenAI-compatível da Deep Infra — **mesmo modelo** (`deepseek-ai/DeepSeek-V4-Flash`), **mesmas tools** e **mesma chave** que os agentes do time usam (um provedor, uma chave, uma fatura). Swap de 2 linhas no `route.ts`; dep `@ai-sdk/deepinfra@3.0.20` pinada na mesma ferroviária do `ai@7.0.47` (provider 4.0.4 / provider-utils 5.0.18, `pnpm why` confirma cópia única — nada de 4.0.7 duplicado), `@ai-sdk/deepseek` removido (dead dep, knip); `.env.example` `DEEPSEEK_API_KEY` → `DEEPINFRA_API_KEY` (B173 STT/Whisper reusa a mesma chave). Sem migration, access, UI ou teste novo — e2e já mocka o SSE (sem chave real no CI); rollback documentado = `git revert` (restaura provider/baseURL antiga); smoke de deploy = 2–3 perguntas forçando tool calling real antes de confiar o flip. Intenção [`sollinha-no-deepinfra.md`](plans/sollinha-no-deepinfra.md); impl [`sollinha-no-deepinfra-impl.md`](plans/sollinha-no-deepinfra-impl.md). **Recently resolved (2026-08-08):** **B170** (Issue #441, follow-up B169) — o **contrato do colapso de chips virou spec unitário** (`tests/unit/relationChipCellCollapse.unit.spec.tsx`): `RelationChipCell` renderiza 16 chips com `getBoundingClientRect` stubado (4/linha, 20px, gap 6 — a matemática real de `COLLAPSED_CHIP_ROWS`/`CHIP_GAP_PX`) e o spec pina que o slice fica em 12 (3 linhas), "Ver mais…" expande para 16 e "Ver menos" recolhe para 12; casos tudo-cabe (8) e exatamente-cheio (12) não mostram toggle. Antes, os specs das células usavam `measureOverflow: false` ou colapsavam para 1 chip — a mudança de timing do B169 (medição em `useLayoutEffect`) não tinha regressão do comportamento de colapso. **F2 medido e deferido em vez de eliminado**: `PerformanceObserver('layout-shift')` no worktree confirma o resíduo pré-hidratação exatamente como registrado no B169 — load SSR→hidratação com shifts únicos `0.139/0.043/0.257` (fontes: shell — sidebar/Drawer/main/col-resize — nenhuma célula de chips) e a linha da célula indo `97px/42chips → 57px/0chips` numa única transição; **ordenação client-side (RSC) → zero shifts novos**. Eliminar a paint pré-hidratação exigiria estimativa de largura por label (rejeitada pelo dono, que mede layout real de propósito) ou mudança de layout/regra de colapso (anti-goal) — o shift único do primeiro load é aceitável num painel interno staff. Sem migration, dados, access ou mudança visual. Intenção [`estabilizar-lista-dobradinhas-followup.md`](docs/plans/estabilizar-lista-dobradinhas-followup.md); impl [`estabilizar-lista-dobradinhas-followup-impl.md`](docs/plans/estabilizar-lista-dobradinhas-followup-impl.md). + + +**Recently resolved (2026-08-09):** **C94** (Issue #438) — barra da agenda consolidada: os 3 seletores (Município/Tag/Deputado presente) viraram **um combobox omnibox** (`activityAgendaOmnibox.ts` + chassis B127) com chips removíveis/“Limpar” e sugestões agrupadas; desktop toolbar `[combobox][carrinho giro][+ nova]` (ícones; mobile sem botão de criação no corpo — tudo no FAB); “Link de import” **saiu do card de filtros** para o **header do app** (`CampaignDesktopHeader`, **agenda-contextual**, ordem `[link][sino][IA]` do gate) via registro chaveado `SetCampaignHeaderAction` no `CampaignPageChromeContext` (slot compartilhado com C95), e virou ação de drawer no FAB mobile (`CampaignQuickAction.onAction` + `openCalendarFeed` no `CampaignQuickActionContext`) abrindo o **mesmo diálogo como sheet** responsivo (`CalendarFeedDialog` Dialog/Drawer; sem recorte mostra aviso “Aplique filtros…”, C93 cobre gerar sem filtro). O overlay do FAB fecha ao disparar ação de dialog. Sem migration/access/Consent. Intenção [`c94-agenda-combobox-header-acoes-rapidas.md`](docs/plans/c94-agenda-combobox-header-acoes-rapidas.md); impl [`c94-agenda-combobox-header-acoes-rapidas-impl.md`](docs/plans/c94-agenda-combobox-header-acoes-rapidas-impl.md). + + (feat(C94): toolbar omnibox + e2e agenda hardening + changelog) **Recently resolved (2026-08-08):** **`pnpm worktree plan [bag] [--go] [--no-migrate]`** — worktree de **planejamento** para rodar a skill `/plan-issue` sem ocupar o main: **cada invocação cria UM DIFERENTE**, para sessões de planejamento paralelas — com `bag`, branch `plans/plan-issue-` (sufixo `-2`/`-3` se o nome já estiver vivo); sem `bag`, o próximo sequencial `plans/plan-issue-` livre (`PLAN_BRANCH_PREFIX`/`planBranchName`, `scripts/lib/worktree.mjs`). Prefixo minúsculo `plans/…` nunca colide com um `next` posterior em branch nem em slot (slot do plano = hash do branch, registrado via marcador e bumpado pelos vivos). Mesmo provisionamento isolado do `next` (porta `3100+slot`, bancos `teqo_wt`/`teqo_wt_test`, migrations; sem Docker degrada ao compartilhado). `kill` cobre; comment das envs distingue `gerado por pnpm worktree plan`. Sem migration. Naming unit-testada. **Recently resolved (2026-08-08):** **B168** (Issue #422) — desfeito o **encadeamento automático** das ações rápidas do wizard (B98 era o precedente que este item inverte; evidência de campo: o coordenador rejeitou a tela avançando sozinha). Ao salvar **qualquer** ação de ajuste (votos, tendência, atualização, liderança), o usuário volta à origem (`from` allowlist B110 mantido; Início sem `from`) com toast — nunca avança para outro wizard. Removidos a fila/pipeline `wizardActionChain.ts` (fundido `wizardReturnHref` + `wizardPreviousHref` só com passos internos em `campaignActionRoutes.ts`), o param `entry`, os skip resolvers/`Pular` (`WizardSkipTrailing`/`WizardTrendSkipTrailing` deletados, chrome mobile sempre “X”) e o prefill de nota sem produtor (`notePrefill`/`voteFrom`/`voteTo`). Tendência: pós-save o RSC refresha com status já aplicado e o **client** é o único dono da navegação (toast + retorno) — o redirect servidor engolia o toast (B97 corrigido no caminho). Liderança: botão flutuante pós-save passou de “Continuar” (avançava) para **“Concluir”** (retorna à origem). Sem migration. Intenção [`desfazer-encadeamento-acoes-wizard.md`](plans/desfazer-encadeamento-acoes-wizard.md); impl [`desfazer-encadeamento-acoes-wizard-impl.md`](plans/desfazer-encadeamento-acoes-wizard-impl.md). diff --git a/docs/plans/c94-agenda-combobox-header-acoes-rapidas-impl.md b/docs/plans/c94-agenda-combobox-header-acoes-rapidas-impl.md index 0e7a6fbe..cd5d9d7f 100644 --- a/docs/plans/c94-agenda-combobox-header-acoes-rapidas-impl.md +++ b/docs/plans/c94-agenda-combobox-header-acoes-rapidas-impl.md @@ -1,6 +1,6 @@ # Impl: C94 — Agenda: filtro combobox único, header compacto (ícones) e ações no FAB mobile -Status: rascunho +Status: em execução (aprovado pelo humano em 2026-08-09) Atualizado em: 2026-08-09 Issue: #438 Intenção: docs/plans/c94-agenda-combobox-header-acoes-rapidas.md @@ -76,14 +76,18 @@ flowchart LR - **C92/C93**: o ícone reflete apenas o estado habilitado/desabilitado; "gerar feed sem filtros" é a C93 (fora). - **Não mudar o compose de giro nem o overlay inline (C91).** -## Riscos e mitigação +## Divergências executadas (vs. hipótese da intenção) -- **`useSearchParams` no layout** — evitado (slot por contexto; o header não lê URL). Não introduz Suspense. -- **Flash de registro do header** — `SetCampaignHeaderAction` usa `useLayoutEffect` (padrão do título); botão aparece ~1 frame após mount; aceitável nas rotas `/campanha/agenda` (nas demais o slot fica vazio → sem poluição). -- **Contexto `openCalendarFeed` undefined antes do mount** — o drawer só abre por gesto humano, bem depois do efeito; `import-calendar` é incluído sempre na surface `list` e abre o sheet mesmo sem filtro (dialog explica), então não há tile morta. -- **Colidir com C95 (Semana ▾ no header)** — registro chaveado por `id`; C94 usa `calendar-feed`, C95 usará outra chave; ordem fixa no header. -- **Regressão e2e** — a única e2e (linhas 120-122) troca selects por interação omnibox; ajustar junto da fase 1. -- **Branch atrás de main (4 commits, C92/B171)** — rebase em `origin/main` no início da execução (skill `rebase-on-main`). +- `AgendaFeedChrome` (client, montado na página) é o ponte único: registra o botão do header (`SetCampaignHeaderAction id="calendar-feed"`), injeta `openCalendarFeed` no `CampaignQuickActionContext` e **dona do único ``** (Dialog/sheet responsivo) — o botão do header e a ação do FAB chamam o mesmo `open()` (sem contexto novo de diálogo no layout). +- O **overlay do FAB fecha ao disparar uma ação dialog** (`onActionClick` no `CampaignQuickActionsOverlay`) — senão o sheet da agenda abriria por cima do drawer aberto. +- `playwright.config.ts` ganhou `webServer.timeout = 240s` **local-only** (`CI ? undefined`) — a máquina compartilhada (load 60+ de worktrees paralelos) estourava o boot de 60s em `pnpm dev`. + +## Riscos e mitigação (executados) + +- **Header** `[actions][sino][IA]` conforme o gate; sem `useSearchParams` no layout. +- E2E agenda: a interação omnibox usa `fill` (reabre o popover) em vez de `click` num input já focado; assert final de URL com wait do chip + `timeout: 20s`. +- `codebaseConventions` — `activityAgendaOmnibox.ts` registrado no pin de `src/utilities`. +- Os testes de slot do calendário (`cria inline`, `Mais detalhes`) são **frágeis de timing de `gridcell.nth(1)`** e flakam sob load alto também no commit base (verificado via worktree descartável de `e3f6d3f5`) — ambientais, não desta entrega; em load ~30 a suíte agenda passou 3/3. ## Aceite de engenharia diff --git a/src/components/campaign/activity/ActivityAgendaFilters.tsx b/src/components/campaign/activity/ActivityAgendaFilters.tsx index f27f39ca..896a9efe 100644 --- a/src/components/campaign/activity/ActivityAgendaFilters.tsx +++ b/src/components/campaign/activity/ActivityAgendaFilters.tsx @@ -117,7 +117,7 @@ export const ActivityAgendaFilters = ({ asChild variant="outline" size="icon" - className="min-w-11" + className="min-h-11 min-w-11" aria-label="Planejar giro" title="Planejar giro" > @@ -128,7 +128,7 @@ export const ActivityAgendaFilters = ({