teqo-backup/src/lib/campaignQuickActionLeadership.ts
Francisco Solla 212a1f2423 feat(campaign): C87 — atualização unificada (texto + polaridade + urgente)
Substitui o modelo de MunicipalityUpdate (kind/semanal/urgente/nota/sinal + signalType)
por um modelo unificado: body obrigatório + polarity (boa/neutra/ruim) + urgent + adversarySignal.

- Schema: polarity enum obrigatório, urgent boolean default false, adversarySignal boolean
- Wizard: novo WizardUpdateBodyStep com campo de texto + polaridade + urgente
- Lista: controle unificado de atualização com polaridade inline
- Feed: exibe polaridade como badge colorido + indicador de urgência
- V2 status strip: atualização rápida de polaridade
- E11: queries atualizadas para polarity + adversarySignal
- Migração: 20260807_030000_unify_municipality_update (backfill polarity=neutra, urgent de kind, adversarySignal de kind+signalType)
- Testes: 18/18 integração C87, 593/593 total, 1399/1399 unitários
- Fix: testes de state deputy usavam nomes fixos que colidiam entre execuções
2026-08-07 05:04:41 -03:00

114 lines
3.4 KiB
TypeScript

import {
CAMPAIGN_WIZARD_ACTION_SLUGS,
appendWizardReturnPath,
campaignActionEntryHref,
wizardActionHref,
type CampaignWizardActionId,
} from '@/lib/campaignActionRoutes'
import {
homeActionsForRole,
type CampaignHomeAction,
type ResolvedCampaignHomeAction,
} from '@/lib/campaignHomeActions'
import type { CampaignQuickActionContext } from '@/lib/campaignQuickActionContext'
import { isListPath } from '@/lib/campaignQuickActionPaths'
import type { CampaignRole } from '@/lib/campaignRoles'
import { isStaffCampaignRole } from '@/lib/campaignRoles'
const LEADERSHIP_LIST_PATH = '/campanha/liderancas' as const
const LEADERSHIP_DETAIL_WIZARD_IDS: readonly CampaignWizardActionId[] = [
'update-votes',
'register-update',
'change-trend',
'update-leadership',
'register-demand',
]
const isLeadershipListPath = (pathname: string): boolean =>
isListPath(pathname, LEADERSHIP_LIST_PATH)
const parseLeadershipDetailId = (pathname: string): number | undefined => {
const match = pathname.match(/^\/campanha\/liderancas\/(\d+)$/)
if (!match?.[1]) return undefined
return Number(match[1])
}
const resolveLeadershipListActions = (
role: CampaignRole,
returnPath: string,
): readonly ResolvedCampaignHomeAction[] => {
const actions = homeActionsForRole(role).filter((action) => action.id === 'update-leadership')
return actions.map((action) => ({
...action,
href: appendWizardReturnPath(campaignActionEntryHref('update-leadership'), returnPath),
}))
}
const hrefForLeadershipDetailAction = (
action: CampaignHomeAction,
context: CampaignQuickActionContext,
returnPath: string,
): string => {
const wizardSlug = CAMPAIGN_WIZARD_ACTION_SLUGS[action.id as CampaignWizardActionId]
const { municipalitySlug, leadershipId } = context
if (action.id === 'update-leadership') {
if (municipalitySlug && leadershipId !== undefined) {
return wizardActionHref(wizardSlug, municipalitySlug, { leadershipId, returnPath })
}
if (leadershipId !== undefined) {
return `${LEADERSHIP_LIST_PATH}/${leadershipId}`
}
return appendWizardReturnPath(campaignActionEntryHref('update-leadership'), returnPath)
}
if (municipalitySlug) {
return wizardActionHref(wizardSlug, municipalitySlug, { returnPath })
}
return appendWizardReturnPath(
campaignActionEntryHref(action.id as CampaignWizardActionId),
returnPath,
)
}
const resolveLeadershipDetailActions = (
role: CampaignRole,
context: CampaignQuickActionContext,
returnPath: string,
): readonly ResolvedCampaignHomeAction[] => {
const actions = homeActionsForRole(role).filter((action) =>
LEADERSHIP_DETAIL_WIZARD_IDS.includes(action.id as CampaignWizardActionId),
)
return actions.map((action) => ({
...action,
href: hrefForLeadershipDetailAction(action, context, returnPath),
}))
}
export const resolveLeadershipQuickActions = (
pathname: string,
role: CampaignRole,
context: CampaignQuickActionContext,
): readonly ResolvedCampaignHomeAction[] | null => {
if (!isStaffCampaignRole(role)) return null
if (isLeadershipListPath(pathname)) {
return resolveLeadershipListActions(role, pathname)
}
const leadershipId = parseLeadershipDetailId(pathname)
if (leadershipId !== undefined) {
return resolveLeadershipDetailActions(
role,
{
...context,
leadershipId: context.leadershipId ?? leadershipId,
},
pathname,
)
}
return null
}