From d5da7b0adf79b97fadb6906c47ebabdb55876220 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 1 Aug 2026 04:08:04 +0000 Subject: [PATCH] =?UTF-8?q?B82=20=E2=80=94=20A=C3=A7=C3=B5es=20r=C3=A1pida?= =?UTF-8?q?s=20em=20lideran=C3=A7as=20(lista=20+=20detalhe)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Registra catálogo contextual no drawer B79 para /campanha/liderancas: lista com update-leadership; detalhe com cinco wizards e prefill de município quando N=1. Adiciona query leadershipId no wizard A4 e CampaignQuickActionContextSync na ficha. Co-authored-by: Francisco Solla --- docs/plans/acoes-rapidas-liderancas.md | 14 ++- .../campanha/(app)/acoes/[slug]/page.tsx | 5 + .../campanha/(app)/liderancas/[id]/page.tsx | 26 ++++- .../leadership/WizardLeadershipStep.tsx | 13 ++- src/lib/campaignActionRoutes.ts | 24 ++++- src/lib/campaignQuickActionLeadership.ts | 100 ++++++++++++++++++ src/lib/campaignQuickActionRegistry.ts | 6 ++ tests/unit/campaignActionRoutes.unit.spec.ts | 12 +++ tests/unit/campaignQuickAction.unit.spec.ts | 6 ++ ...campaignQuickActionLeadership.unit.spec.ts | 53 ++++++++++ 10 files changed, 247 insertions(+), 12 deletions(-) create mode 100644 src/lib/campaignQuickActionLeadership.ts create mode 100644 tests/unit/campaignQuickActionLeadership.unit.spec.ts diff --git a/docs/plans/acoes-rapidas-liderancas.md b/docs/plans/acoes-rapidas-liderancas.md index 7a3c63f4..f05f98a2 100644 --- a/docs/plans/acoes-rapidas-liderancas.md +++ b/docs/plans/acoes-rapidas-liderancas.md @@ -1,7 +1,7 @@ # Ações rápidas — Lideranças (lista + detalhe) -Status: rascunho -Atualizado em: 2026-07-30 +Status: entregue (B82 — 2026-08-01) +Atualizado em: 2026-08-01 Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item **B82**) Impeccable: B — catálogo drawer B79 em `/liderancas` e `/liderancas/[id]` Appetite: ~0,5 dia eng; prefills A4 + município(s) da liderança; sem migration @@ -38,7 +38,15 @@ Se B70 ainda não expuser query de liderança, o agente deve (1) propor o param ## Questões em aberto -- **Query `?lideranca=` no wizard A4?** **Opções:** A) sim no B70/B82 | B) só município. **Recomendação:** **A** quando B70 estiver no ar — senão degradar. +- **Query `?lideranca=` no wizard A4?** **Resolvido (as-built):** param **`leadershipId`** (inglês) em `wizardActionHref` + `WizardLeadershipStep` abre o form quando o id bate com um tile do município; sem município único, `update-leadership` degrada para `/campanha/liderancas/[id]`. + +## As-built (2026-08-01) + +- Registry: `src/lib/campaignQuickActionLeadership.ts` — lista = só `update-leadership`; detalhe = cinco wizards com prefill de município quando N=1. +- Contexto: `CampaignQuickActionContextSync` na ficha `[id]` (slug único via portfolio index). +- Wizard: `WIZARD_LEADERSHIP_ID_QUERY_KEY` + auto-abertura do form no B70. + +## Questões em aberto (histórico) ## Abordagem proposta diff --git a/src/app/(campaign)/campanha/(app)/acoes/[slug]/page.tsx b/src/app/(campaign)/campanha/(app)/acoes/[slug]/page.tsx index 9277e916..8d946a6c 100644 --- a/src/app/(campaign)/campanha/(app)/acoes/[slug]/page.tsx +++ b/src/app/(campaign)/campanha/(app)/acoes/[slug]/page.tsx @@ -13,6 +13,7 @@ import { hasWizardScenarioParam, isCampaignWizardActionSlug, parseWizardEntryActionParam, + parseWizardLeadershipIdParam, parseWizardMunicipioParam, resolveWizardSignalTypeParam, resolveWizardTrendStatusParam, @@ -126,6 +127,9 @@ export default async function CampaignActionWizardPage({ const entryAction = parseWizardEntryActionParam( resolvedSearchParams[WIZARD_ENTRY_ACTION_QUERY_KEY], ) + const initialLeadershipId = parseWizardLeadershipIdParam( + resolvedSearchParams.leadershipId, + ) const tiles = await loadWizardLeadershipTiles(payload, user, municipality.id) return ( @@ -136,6 +140,7 @@ export default async function CampaignActionWizardPage({ municipalitySlug={municipality.slug} entryAction={entryAction} initialTiles={tiles} + initialLeadershipId={initialLeadershipId} /> ) } diff --git a/src/app/(campaign)/campanha/(app)/liderancas/[id]/page.tsx b/src/app/(campaign)/campanha/(app)/liderancas/[id]/page.tsx index 615b07cc..41741b41 100644 --- a/src/app/(campaign)/campanha/(app)/liderancas/[id]/page.tsx +++ b/src/app/(campaign)/campanha/(app)/liderancas/[id]/page.tsx @@ -8,9 +8,11 @@ import { LeadershipInviteButtons } from '@/components/campaign/invite/Leadership import { LeadershipInternalForm } from '@/components/campaign/leadership/LeadershipInternalForm' import { SupportStatusBadge } from '@/components/campaign/leadership/SupportStatusBadge' import { CampaignPageShell } from '@/components/campaign/shell/CampaignPageShell' +import { CampaignQuickActionContextSync } from '@/components/campaign/shell/CampaignQuickActionContextSync' import { StateDeputyChips } from '@/components/campaign/stateDeputy/StateDeputyChips' import { Badge } from '@/components/ui/Badge' import { Button } from '@/components/ui/button' +import { resolvedPortfolioEntriesById } from '@/lib/municipalityPortfolio' import { requireCampaignPageActor } from '@/utilities/campaignPageActor' import { loadMunicipalityOptions, @@ -18,6 +20,7 @@ import { loadStateDeputyOptions, } from '@/utilities/campaignRelationOptions' import { loadLeadershipDetail } from '@/utilities/leadership/leadershipData' +import { loadMunicipalityPortfolioIndex } from '@/utilities/municipality/municipalityPortfolioIndex' import { updateLeadershipInternalFormAction } from './formActions' type LeadershipDetailPageProps = { @@ -36,14 +39,27 @@ export default async function LeadershipDetailPage({ params }: LeadershipDetailP const leadership = await loadLeadershipDetail(payload, user, Number(id)) if (!leadership) notFound() - const [municipalityOptions, organizationOptions, stateDeputyOptions] = await Promise.all([ - loadMunicipalityOptions(payload, user), - loadOrganizationOptions(payload, user), - loadStateDeputyOptions(payload, user), - ]) + const [municipalityOptions, organizationOptions, stateDeputyOptions, municipalityIndex] = + await Promise.all([ + loadMunicipalityOptions(payload, user), + loadOrganizationOptions(payload, user), + loadStateDeputyOptions(payload, user), + loadMunicipalityPortfolioIndex(), + ]) + + const municipalityById = resolvedPortfolioEntriesById(municipalityIndex) + const municipalitySlugs = leadership.municipalityIDs + .map((id) => municipalityById.get(id)?.slug) + .filter((slug): slug is string => slug !== undefined) + const singleMunicipalitySlug = + municipalitySlugs.length === 1 ? municipalitySlugs[0] : undefined return ( +