diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index b4f19f99..fadd0757 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -311,8 +311,8 @@ jobs: node-version: 24 cache: pnpm - run: pnpm install --frozen-lockfile - - name: Install Playwright chromium - run: pnpm exec playwright install --with-deps chromium + - name: Install Playwright Chromium and focused WebKit + run: pnpm exec playwright install --with-deps chromium webkit - run: pnpm migrate - run: pnpm db:seed:minimal - name: Build for e2e diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33da2960..f0db6ab3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -133,8 +133,8 @@ jobs: node-version: 24 cache: pnpm - run: pnpm install --frozen-lockfile - - name: Install Playwright chromium - run: pnpm exec playwright install --with-deps chromium + - name: Install Playwright Chromium and focused WebKit + run: pnpm exec playwright install --with-deps chromium webkit - run: pnpm migrate - run: pnpm db:seed:minimal - name: Build for e2e diff --git a/docs/CHANGELOG-AGENTS.md b/docs/CHANGELOG-AGENTS.md index 8ba0d7f8..3fc1a2ba 100644 --- a/docs/CHANGELOG-AGENTS.md +++ b/docs/CHANGELOG-AGENTS.md @@ -4,6 +4,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente --- +**Recently resolved (2026-08-05):** **B158** (Issue #364) — `/campanha/municipios` troca cards↔tabela e revela colunas pela largura do painel de conteúdo com named container queries (48–84rem), sem rolagem horizontal; Território virou sublinha de Município, Tendência/Sinal preservam um único editor na tabela quando ocultos manualmente, e `campaign_columns=municipios:__none__` diferencia “todas visíveis” dos defaults. E2E mede cada limiar em Chromium e mantém um caso iPad-sized no projeto WebKit focado. Sem migration ou mudança de RBAC. Intenção [`colunas-responsivas-municipios.md`](docs/plans/colunas-responsivas-municipios.md); impl [`colunas-responsivas-municipios-impl.md`](docs/plans/colunas-responsivas-municipios-impl.md). + **Recently resolved (2026-08-05):** **B159** (Issue #374) — as colunas Assessores, Lideranças e Dobradinhas de `/campanha/municipios` agora compartilham `MunicipalityRelationEditor` (`CampaignCellEditOverlay` + Command + chips + optimistic reconcile) e `MunicipalityRelationAvatarStack`; Dobradinhas deixa `RelationChipCell` só nesta superfície, preserva busca por partido e links para a ficha, e Lideranças cria inline somente com nome (`Contact.phone = null`, sem stub; telefone real depois na ficha). Sem migration, rota nova ou mudança de access. Intenção [`generalizar-colunas-relacao-municipios.md`](docs/plans/generalizar-colunas-relacao-municipios.md); impl [`generalizar-colunas-relacao-municipios-impl.md`](docs/plans/generalizar-colunas-relacao-municipios-impl.md). **Recently resolved (2026-08-04):** **B155** (Issue #359) — coluna de lideranças na lista de municípios (espelho de Assessores): coluna "Lideranças" após "Assessores" (staff; leader fora), chips com nome do contato, popover/sheet com Command + delta otimista (seq/pending/rollback) e criação inline (mini-form Nome + Celular). **Sem migration**: leitura por query reversa de `leadership.municipalities` (batch no bundle, `loadMunicipalityLeadershipSummaries`) e escrita reusando B34 (`setLeadershipMunicipalitiesMembershipRecord`) + wizard create (`createLeadershipWizardRecord`) — fonte única de verdade, consistência automática com `/campanha/liderancas`. Rota nova `POST /campanha/municipios/leaderships` (`campaignJsonMutationRoute`, union toggle|create, safe-messages allowlist); provider bridge espelho do B154. Regra travada com o humano: **célula → rota, form → server action** (débito de migração das 3 anomalias — sinal da lista e colunas B31/B34 de `/campanha/liderancas`). Intenção [`liderancas-coluna-municipios.md`](docs/plans/liderancas-coluna-municipios.md); impl [`liderancas-coluna-municipios-impl.md`](docs/plans/liderancas-coluna-municipios-impl.md). diff --git a/playwright.config.ts b/playwright.config.ts index 91fe8301..289838bd 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -75,6 +75,12 @@ export default defineConfig({ dependencies: ['setup'], use: { ...devices['Desktop Chrome'], channel: 'chromium' }, }, + { + name: 'municipality-responsive-webkit', + testMatch: /campaignMunicipalityResponsiveColumns\.e2e\.spec\.ts/, + dependencies: ['setup'], + use: { ...devices['iPad Pro 11'] }, + }, { name: 'frontend', testMatch: /frontend\.e2e\.spec\.ts/, diff --git a/src/components/campaign/municipality/MunicipalityList.tsx b/src/components/campaign/municipality/MunicipalityList.tsx index 023ebabb..3b9a4a61 100644 --- a/src/components/campaign/municipality/MunicipalityList.tsx +++ b/src/components/campaign/municipality/MunicipalityList.tsx @@ -1,4 +1,4 @@ -import { SearchXIcon } from 'lucide-react' +import { ActivityIcon, CircleAlertIcon, SearchXIcon } from 'lucide-react' import Link from 'next/link' import { @@ -69,6 +69,10 @@ import { resolveMunicipalityListSort, type MunicipalityListState, } from '@/utilities/municipality/municipalityListUrl' +import { + isMunicipalitySignalCold, + municipalitySignalAgeInDays, +} from '@/utilities/municipality/municipalitySignal' import type { EligibleAdvisorOption, EligibleLeadershipOption, @@ -155,8 +159,48 @@ const advisorEntries = ( * Desktop table columns (Pass 2 W1). `id` is the picker key, cookie key, and B22 * description key — typed instead of widening to `string`. */ +type MunicipalityTableColumnId = MunicipalityListColumnId | 'actions' + type MunicipalityColumn = CampaignTableColumn & { - id: MunicipalityListColumnId + id: MunicipalityTableColumnId +} + +const responsiveColumnClassName = { + advisors: 'hidden @min-[54rem]/municipality-list:table-cell', + leaderships: 'hidden @min-[66rem]/municipality-list:table-cell', + stateDeputies: 'hidden @min-[72rem]/municipality-list:table-cell', + goalCoverage: 'hidden @min-[78rem]/municipality-list:table-cell', +} as const satisfies Partial> + +const MunicipalityListSignalTrigger = ({ + lastSignalAt, + presentation, +}: { + lastSignalAt: string | null + presentation: 'compact' | 'adaptive' +}) => { + const isCold = isMunicipalitySignalCold(municipalitySignalAgeInDays(lastSignalAt)) + const Icon = isCold ? CircleAlertIcon : ActivityIcon + const compact = ( +