Merge pull request #74 from fsolla/cursor/pool-i17-70bc3fde-a9be

B81 — Ações rápidas — Territórios
This commit is contained in:
Francisco Solla 2026-08-01 01:20:06 -03:00 committed by GitHub
commit a638561ce9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 82 additions and 11 deletions

View file

@ -16,8 +16,7 @@ import {
type CampaignWizardActionId,
} from '@/lib/campaignActionRoutes'
import { LEADER_CONTACTS_HOME } from '@/lib/campaignPaths'
import type { CampaignRole } from '@/lib/campaignRoles'
import { isStaffCampaignRole } from '@/lib/campaignRoles'
import { isStaffCampaignRole, type CampaignRole } from '@/lib/campaignRoles'
type CampaignHomeActionId =
| 'update-votes'
@ -124,6 +123,27 @@ export type ResolvedCampaignHomeAction = CampaignHomeAction & {
href?: string
}
/** Mirrors Início `buildMunicipalityListHref({ coverage: 'sem_assessor', sort: 'votos' }, 1)`. */
export const UNCOVERED_MUNICIPALITIES_LIST_HREF =
'/campanha/municipios?coverage=sem_assessor&sort=votos' as const
/** Staff Início catalog with hrefs — shared by quick-action registries (B81+). */
const staffHomeQuickActionsByRole = new Map<CampaignRole, readonly ResolvedCampaignHomeAction[]>()
export const resolveStaffHomeQuickActions = (
role: CampaignRole,
): readonly ResolvedCampaignHomeAction[] => {
if (!isStaffCampaignRole(role)) return []
const cached = staffHomeQuickActionsByRole.get(role)
if (cached) return cached
const actions = toHomeActionButtonProps(
homeActionsForRole(role),
UNCOVERED_MUNICIPALITIES_LIST_HREF,
)
staffHomeQuickActionsByRole.set(role, actions)
return actions
}
export const toHomeActionButtonProps = (
actions: readonly CampaignHomeAction[],
uncoveredMunicipalitiesHref?: string,

View file

@ -1,4 +1,6 @@
/** Client-safe `/campanha` route paths shared by nav, gates, and home actions. */
export const CAMPAIGN_HOME = '/campanha' as const
export const CAMPAIGN_TERRITORIES_HOME = '/campanha/territorios' as const
export const LEADER_CONTACTS_HOME = '/campanha/contatos' as const

View file

@ -1,14 +1,26 @@
import type { ResolvedCampaignHomeAction } from '@/lib/campaignHomeActions'
import {
resolveStaffHomeQuickActions,
type ResolvedCampaignHomeAction,
} from '@/lib/campaignHomeActions'
import { CAMPAIGN_TERRITORIES_HOME } from '@/lib/campaignPaths'
import type { CampaignQuickActionContext } from '@/lib/campaignQuickActionContext'
import type { CampaignRole } from '@/lib/campaignRoles'
const isTerritoriesListPath = (pathname: string): boolean =>
pathname === CAMPAIGN_TERRITORIES_HOME || pathname.startsWith(`${CAMPAIGN_TERRITORIES_HOME}/`)
/**
* Pathname + role + page context → contextual quick actions.
* Catalogs per vertical land in B80–B90; the B79 chassis renders whatever
* this helper returns (empty until those items register providers).
*/
export const resolveQuickActionsForPath = (
_pathname: string,
_role: CampaignRole,
pathname: string,
role: CampaignRole,
_context: CampaignQuickActionContext,
): readonly ResolvedCampaignHomeAction[] => []
): readonly ResolvedCampaignHomeAction[] => {
if (isTerritoriesListPath(pathname)) {
return resolveStaffHomeQuickActions(role)
}
return []
}

View file

@ -1,7 +1,12 @@
import { describe, expect, it } from 'vitest'
import { homeActionsForRole, toHomeActionButtonProps } from '@/lib/campaignHomeActions'
import {
homeActionsForRole,
toHomeActionButtonProps,
UNCOVERED_MUNICIPALITIES_LIST_HREF,
} from '@/lib/campaignHomeActions'
import { LEADER_CONTACTS_HOME } from '@/lib/campaignPaths'
import { buildMunicipalityListHref } from '@/utilities/municipality/municipalityListUrl'
const staffActionIds = [
'update-votes',
@ -48,10 +53,12 @@ describe('toHomeActionButtonProps', () => {
})
it('wires uncovered-municipalities when href is provided', () => {
const href = '/campanha/municipios?coverage=sem_assessor&sort=votos'
const props = toHomeActionButtonProps(homeActionsForRole('coordinator'), href)
const props = toHomeActionButtonProps(
homeActionsForRole('coordinator'),
UNCOVERED_MUNICIPALITIES_LIST_HREF,
)
const uncovered = props.find((action) => action.id === 'uncovered-municipalities')
expect(uncovered?.href).toBe(href)
expect(uncovered?.href).toBe(UNCOVERED_MUNICIPALITIES_LIST_HREF)
const updateVotes = props.find((action) => action.id === 'update-votes')
expect(updateVotes?.href).toBe('/campanha/acoes/atualizar-votos')
const registerSignal = props.find((action) => action.id === 'register-signal')
@ -63,3 +70,11 @@ describe('toHomeActionButtonProps', () => {
expect(props.find((action) => action.id === 'uncovered-municipalities')?.href).toBeUndefined()
})
})
describe('UNCOVERED_MUNICIPALITIES_LIST_HREF', () => {
it('matches canonical municipality list builder (client/server parity)', () => {
expect(buildMunicipalityListHref({ page: 1, coverage: 'sem_assessor', sort: 'votos' }, 1)).toBe(
UNCOVERED_MUNICIPALITIES_LIST_HREF,
)
})
})

View file

@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest'
import { UNCOVERED_MUNICIPALITIES_LIST_HREF, homeActionsForRole } from '@/lib/campaignHomeActions'
import {
isCampaignActionsPath,
isCampaignHomePath,
@ -34,6 +35,7 @@ describe('campaignQuickActionMount', () => {
it('mounts for staff outside Início and acoes', () => {
expect(shouldMountQuickActionsDrawer('/campanha/municipios', 'coordinator')).toBe(true)
expect(shouldMountQuickActionsDrawer('/campanha/territorios', 'coordinator')).toBe(true)
expect(shouldMountQuickActionsDrawer('/campanha', 'coordinator')).toBe(false)
expect(shouldMountQuickActionsDrawer('/campanha/acoes/registrar-sinal', 'advisor')).toBe(false)
})
@ -45,13 +47,33 @@ describe('campaignQuickActionMount', () => {
})
describe('campaignQuickActionRegistry', () => {
it('returns empty catalog until B80+ register providers', () => {
const staffActionIds = homeActionsForRole('coordinator').map((action) => action.id)
it('returns empty catalog for unregistered paths', () => {
expect(
resolveQuickActionsForPath('/campanha/municipios/foo', 'coordinator', {
municipalitySlug: 'foo',
}),
).toEqual([])
})
it('returns staff Início catalog on territorios without municipality prefill (B81)', () => {
const actions = resolveQuickActionsForPath('/campanha/territorios', 'coordinator', {})
expect(actions.map((action) => action.id)).toEqual(staffActionIds)
expect(actions.find((action) => action.id === 'update-votes')?.href).toBe(
'/campanha/acoes/atualizar-votos',
)
expect(actions.find((action) => action.id === 'uncovered-municipalities')?.href).toBe(
UNCOVERED_MUNICIPALITIES_LIST_HREF,
)
for (const action of actions) {
expect(action.href).not.toContain('municipio=')
}
})
it('returns empty catalog on territorios for leader lockdown', () => {
expect(resolveQuickActionsForPath('/campanha/territorios', 'leader', {})).toEqual([])
})
})
describe('campaignQuickActionSnap', () => {