mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
Merge pull request #120 from fsolla/cursor/pool-i106-d9499b1d-9029
B98 — Encadear ajustes após a ação principal do wizard
This commit is contained in:
commit
f20fca0506
19 changed files with 397 additions and 82 deletions
|
|
@ -1,6 +1,6 @@
|
|||
# Encadear ajustes após a ação principal do wizard
|
||||
|
||||
Status: registrado
|
||||
Status: em implementação
|
||||
Atualizado em: 2026-08-01
|
||||
Issue: #106
|
||||
Priority: P1
|
||||
|
|
@ -47,7 +47,7 @@ Dados: N/A como métrica nova — encadeia writes já existentes (votos, tendên
|
|||
|
||||
O rascunho UX-1 ([fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md)) pediu continuidade no fim de A1 (“Quer também…?: sinal / tendência / liderança”). Os wizards standalone existem (votos, sinal, tendência, liderança), com `entryAction` query **já** usada para skip (sinal/liderança) e prefill de nota de tendência — mas **ninguém navega** de um fluxo ao próximo após o save.
|
||||
|
||||
`WizardExpectedVotesStep` tem fase `saved` com placeholder: _“Próximo passo deste fluxo (sinal, tendência ou resumo) em breve.”_ — gancho explícito.
|
||||
`WizardExpectedVotesStep` tinha fase `saved` com placeholder — B98 substitui por redirect imediato ao próximo elo.
|
||||
|
||||
Pedido (2026-08-01): ao iniciar uma ação → município → **ajuste principal** → **encadear** outros ajustes que façam sentido (ex. votos → tendência).
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ Pedido (2026-08-01): ao iniciar uma ação → município → **ajuste principal
|
|||
|
||||
## Questões em aberto
|
||||
|
||||
- **Tela “Votos atualizados” intermediária some?** **Opções:** A pular direto à 1ª encadeada | B 1 toque “Continuar” na fase saved. **Recomendação:** **A** — menos atrito; Feel the action. _(assumido — validar no craft)_
|
||||
- _(nenhuma — Option A da tela intermediária de votos fechada no craft 2026-08-01)_
|
||||
|
||||
## Abordagem proposta
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ Componentes:
|
|||
|
||||
## Dependências
|
||||
|
||||
- Duras: **B96** (Pular nas encadeadas), **B97** (Salvar tendência não loopa). Soft: B61/B63/B64/B70 ✓, placeholder de votos.
|
||||
- Duras: **B96** (Pular nas encadeadas) ✓ done/in-prod (#104), **B97** (Salvar tendência não loopa) ✓ done/in-prod (#105). Soft: B61/B63/B64/B70 ✓.
|
||||
- Soft: OPS11 só para ver em prod.
|
||||
|
||||
## Não escopo
|
||||
|
|
@ -123,11 +123,19 @@ Componentes:
|
|||
- **Tela de resumo antes do fim.** Revisitar se CG reportar “não sei o que gravei”.
|
||||
- **Cadeia a partir de sugestão E11.** Quando E11 deep-linkar wizards com município pré-escolhido.
|
||||
|
||||
## Freshness audit (2026-08-01)
|
||||
|
||||
- Arquivos do plano existem; `entryAction` / skip resolvers / href builders batem com o código.
|
||||
- B96 (#104) e B97 (#105) `done`+`in-prod`.
|
||||
- Skip ainda apontava para Início — B98 redefine para o próximo elo.
|
||||
- Votos não recebia `entryAction` — wiring adicionado na page do wizard.
|
||||
- **Tela intermediária de votos:** Option A (redirect direto) confirmada no craft.
|
||||
|
||||
## Referências
|
||||
|
||||
- [fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md)
|
||||
- `src/components/campaign/shared/WizardExpectedVotesStep.tsx` (fase `saved` + placeholder)
|
||||
- `src/lib/campaignActionRoutes.ts` · `campaignWizardCopy.ts`
|
||||
- `src/components/campaign/shared/WizardExpectedVotesStep.tsx`
|
||||
- `src/lib/campaignActionRoutes.ts` · `campaignWizardCopy.ts` · `wizardActionChain.ts`
|
||||
- [wizard-header-x-vs-pular.md](wizard-header-x-vs-pular.md) (B96) · [bug-salvar-tendencia-fecha-wizard.md](bug-salvar-tendencia-fecha-wizard.md) (B97)
|
||||
- `PRODUCT.md` / `DESIGN.md`
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import {
|
|||
resolveWizardTrendNoteDestination,
|
||||
} from '@/lib/politicalTrendWizardUi'
|
||||
import { toVoteEstimateScenarioViewModel } from '@/lib/voteEstimate'
|
||||
import { isWizardChainActionId, wizardChainContinueHref } from '@/lib/wizardActionChain'
|
||||
import config from '@/payload.config'
|
||||
import { requireCampaignPageActor } from '@/utilities/campaignPageActor'
|
||||
import { loadWizardLeadershipTiles } from '@/utilities/leadership/leadershipData'
|
||||
|
|
@ -129,6 +130,10 @@ export default async function CampaignActionWizardPage({
|
|||
redirect(wizardActionHref(slug, municipalitySlug))
|
||||
}
|
||||
|
||||
const entryAction = parseWizardEntryActionParam(
|
||||
resolvedSearchParams[WIZARD_ENTRY_ACTION_QUERY_KEY],
|
||||
)
|
||||
|
||||
return (
|
||||
<WizardExpectedVotesStep
|
||||
actionSlug={slug}
|
||||
|
|
@ -136,6 +141,7 @@ export default async function CampaignActionWizardPage({
|
|||
municipalityName={municipality.name}
|
||||
municipalitySlug={municipality.slug}
|
||||
initialExpectedVotes={toVoteEstimateScenarioViewModel(municipality.expectedVotes)}
|
||||
entryAction={entryAction}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -192,6 +198,10 @@ export default async function CampaignActionWizardPage({
|
|||
const noteDestination = resolveWizardTrendNoteDestination(trendStatus, currentStatus)
|
||||
|
||||
if (noteDestination === 'home') {
|
||||
// Chained session: advance the queue (B98). Standalone stale deep-link: Início (B97).
|
||||
if (isWizardChainActionId(entryAction)) {
|
||||
redirect(wizardChainContinueHref(entryAction, 'change-trend', municipalitySlug))
|
||||
}
|
||||
redirect(CAMPAIGN_HOME)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,13 +32,13 @@ import {
|
|||
WIZARD_LEADERSHIP_FORM_EDIT_TITLE,
|
||||
WIZARD_LEADERSHIP_GRID_TITLE,
|
||||
WIZARD_LEADERSHIP_SAVED_TOAST,
|
||||
WIZARD_LEADERSHIP_SKIP_LABEL,
|
||||
wizardFlowTitleForSlug,
|
||||
} from '@/lib/campaignWizardCopy'
|
||||
import { truncateNameAtWordBoundary } from '@/lib/leadershipNameTruncate'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { resolveWizardChainEntry, wizardChainContinueHref } from '@/lib/wizardActionChain'
|
||||
import {
|
||||
showLeadershipWizardSkip,
|
||||
resolveWizardLeadershipSkip,
|
||||
type WizardLeadershipTileViewModel,
|
||||
} from '@/lib/wizardLeadershipContract'
|
||||
|
||||
|
|
@ -84,11 +84,12 @@ export const WizardLeadershipStep = ({
|
|||
const [dirty, setDirty] = useState(false)
|
||||
const [infoTile, setInfoTile] = useState<WizardLeadershipTileViewModel | null>(null)
|
||||
|
||||
const showSkip = showLeadershipWizardSkip(entryAction)
|
||||
|
||||
const skipConfig = showSkip
|
||||
? { label: WIZARD_LEADERSHIP_SKIP_LABEL, href: CAMPAIGN_HOME }
|
||||
: undefined
|
||||
const skipConfig = resolveWizardLeadershipSkip(entryAction, municipalitySlug)
|
||||
const chainContinueHref = wizardChainContinueHref(
|
||||
resolveWizardChainEntry(entryAction, 'update-leadership'),
|
||||
'update-leadership',
|
||||
municipalitySlug,
|
||||
)
|
||||
|
||||
const stepTitle =
|
||||
mode.kind === 'grid'
|
||||
|
|
@ -113,7 +114,7 @@ export const WizardLeadershipStep = ({
|
|||
|
||||
const handleContinue = () => {
|
||||
startContinueTransition(() => {
|
||||
router.push(CAMPAIGN_HOME)
|
||||
router.replace(chainContinueHref)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ import { Textarea } from '@/components/ui/textarea'
|
|||
import type { CampaignWizardActionId } from '@/lib/campaignActionRoutes'
|
||||
import { wizardSignalHref } from '@/lib/campaignActionRoutes'
|
||||
import { recordLastActedMunicipality } from '@/lib/campaignLastActedMunicipality'
|
||||
import { CAMPAIGN_HOME } from '@/lib/campaignPaths'
|
||||
import { wizardFlowTitleForSlug } from '@/lib/campaignWizardCopy'
|
||||
import type { MunicipalitySignalType } from '@/lib/schemas/municipalityUpdate'
|
||||
import { municipalitySignalTypeLabels } from '@/lib/schemas/municipalityUpdate'
|
||||
import { resolveWizardChainEntry, wizardChainContinueHref } from '@/lib/wizardActionChain'
|
||||
import {
|
||||
resolveWizardSignalSkip,
|
||||
WIZARD_SIGNAL_BODY_STEP_TITLE_PREFIX,
|
||||
|
|
@ -46,12 +46,13 @@ export const WizardSignalBodyStep = ({
|
|||
createMunicipalityListSignalFormAction,
|
||||
{},
|
||||
)
|
||||
const skip = resolveWizardSignalSkip(entryAction)
|
||||
const skip = resolveWizardSignalSkip(entryAction, municipalitySlug)
|
||||
const stepTitle = `${WIZARD_SIGNAL_BODY_STEP_TITLE_PREFIX}: ${municipalitySignalTypeLabels[signalType]}`
|
||||
|
||||
useCampaignFormSuccessToast(state, () => {
|
||||
recordLastActedMunicipality(municipalitySlug)
|
||||
router.push(CAMPAIGN_HOME)
|
||||
const sessionEntry = resolveWizardChainEntry(entryAction, 'register-signal')
|
||||
router.replace(wizardChainContinueHref(sessionEntry, 'register-signal', municipalitySlug))
|
||||
})
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export const WizardSignalTypeStep = ({
|
|||
}: WizardSignalTypeStepProps) => {
|
||||
const [infoType, setInfoType] = useState<MunicipalitySignalType | null>(null)
|
||||
const infoEntry = infoType ? municipalitySignalTypeMetaByType[infoType] : null
|
||||
const skip = resolveWizardSignalSkip(entryAction)
|
||||
const skip = resolveWizardSignalSkip(entryAction, municipalitySlug)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export const WizardTrendChoiceStep = ({
|
|||
prefillExtraParams,
|
||||
}: WizardTrendChoiceStepProps) => {
|
||||
const [infoEntry, setInfoEntry] = useState<PoliticalTrendWizardMetaEntry | null>(null)
|
||||
const skip = resolveWizardTrendSkip(entryAction)
|
||||
const skip = resolveWizardTrendSkip(entryAction, municipalitySlug)
|
||||
const options = selectablePoliticalTrendStatuses(currentStatus)
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import { Textarea } from '@/components/ui/textarea'
|
|||
import type { CampaignWizardActionId } from '@/lib/campaignActionRoutes'
|
||||
import { wizardTrendHref } from '@/lib/campaignActionRoutes'
|
||||
import { recordLastActedMunicipality } from '@/lib/campaignLastActedMunicipality'
|
||||
import { CAMPAIGN_HOME } from '@/lib/campaignPaths'
|
||||
import { wizardFlowTitleForSlug } from '@/lib/campaignWizardCopy'
|
||||
import {
|
||||
resolveWizardTrendSkip,
|
||||
|
|
@ -22,6 +21,7 @@ import {
|
|||
WIZARD_TREND_SAVE_LABEL,
|
||||
} from '@/lib/politicalTrendWizardUi'
|
||||
import type { PoliticalTrendStatusValue } from '@/lib/schemas/municipality'
|
||||
import { resolveWizardChainEntry, wizardChainContinueHref } from '@/lib/wizardActionChain'
|
||||
import { politicalTrendLabels } from '@/utilities/municipality/municipalityLabels'
|
||||
|
||||
type WizardTrendNoteStepProps = {
|
||||
|
|
@ -49,12 +49,13 @@ export const WizardTrendNoteStep = ({
|
|||
setMunicipalityPoliticalTrendFormAction,
|
||||
{},
|
||||
)
|
||||
const skip = resolveWizardTrendSkip(entryAction)
|
||||
const skip = resolveWizardTrendSkip(entryAction, municipalitySlug)
|
||||
const stepTitle = `Mudar tendência para ${politicalTrendLabels[trendStatus]}`
|
||||
|
||||
useCampaignFormSuccessToast(state, () => {
|
||||
recordLastActedMunicipality(municipalitySlug)
|
||||
router.replace(CAMPAIGN_HOME)
|
||||
const sessionEntry = resolveWizardChainEntry(entryAction, 'change-trend')
|
||||
router.replace(wizardChainContinueHref(sessionEntry, 'change-trend', municipalitySlug))
|
||||
})
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
'use client'
|
||||
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useState, useTransition } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import {
|
||||
MUNICIPALITY_EXPECTED_VOTES_ENDPOINT,
|
||||
|
|
@ -13,18 +15,20 @@ import { VoteEstimateScenarioInputs } from '@/components/campaign/votePledge/Vot
|
|||
import { Alert, AlertDescription } from '@/components/ui/Alert'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Spinner } from '@/components/ui/Spinner'
|
||||
import type { CampaignWizardActionId } from '@/lib/campaignActionRoutes'
|
||||
import { wizardActionHref } from '@/lib/campaignActionRoutes'
|
||||
import { postCampaignJson } from '@/lib/campaignJsonRequest'
|
||||
import { recordLastActedMunicipality } from '@/lib/campaignLastActedMunicipality'
|
||||
import { CAMPAIGN_HOME } from '@/lib/campaignPaths'
|
||||
import {
|
||||
resolveWizardVotesSkip,
|
||||
WIZARD_VOTES_FINAL_CTA_LABEL,
|
||||
WIZARD_VOTES_SAVED_MESSAGE,
|
||||
wizardFlowTitleForSlug,
|
||||
wizardNextStepPlaceholder,
|
||||
wizardNextStepTitle,
|
||||
} from '@/lib/campaignWizardCopy'
|
||||
import { type VoteEstimateScenario, type VoteEstimateScenarioViewModel } from '@/lib/voteEstimate'
|
||||
import { resolveWizardChainEntry, wizardChainContinueHref } from '@/lib/wizardActionChain'
|
||||
import {
|
||||
applyVoteShortcut,
|
||||
getWizardVoteViolation,
|
||||
|
|
@ -40,6 +44,7 @@ type WizardExpectedVotesStepProps = {
|
|||
municipalityName: string
|
||||
municipalitySlug: string
|
||||
initialExpectedVotes: VoteEstimateScenarioViewModel
|
||||
entryAction?: CampaignWizardActionId
|
||||
}
|
||||
|
||||
export const WizardExpectedVotesStep = ({
|
||||
|
|
@ -48,13 +53,15 @@ export const WizardExpectedVotesStep = ({
|
|||
municipalityName,
|
||||
municipalitySlug,
|
||||
initialExpectedVotes,
|
||||
entryAction,
|
||||
}: WizardExpectedVotesStepProps) => {
|
||||
const router = useRouter()
|
||||
const [isPending, startTransition] = useTransition()
|
||||
const [estimates, setEstimates] = useState(initialExpectedVotes)
|
||||
const [focusedScenario, setFocusedScenario] = useState<VoteEstimateScenario>('central')
|
||||
const [violation, setViolation] = useState<WizardVoteViolation | null>(null)
|
||||
const [saveError, setSaveError] = useState<string | null>(null)
|
||||
const [phase, setPhase] = useState<'editing' | 'saved'>('editing')
|
||||
const skip = resolveWizardVotesSkip(entryAction, municipalitySlug)
|
||||
|
||||
const clearViolationState = () => {
|
||||
if (violation) setViolation(null)
|
||||
|
|
@ -71,6 +78,11 @@ export const WizardExpectedVotesStep = ({
|
|||
handleValuesChange({ ...estimates, [focusedScenario]: nextValue })
|
||||
}
|
||||
|
||||
const continueAfterVotes = () => {
|
||||
const sessionEntry = resolveWizardChainEntry(entryAction, 'update-votes')
|
||||
router.replace(wizardChainContinueHref(sessionEntry, 'update-votes', municipalitySlug))
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (isPending) return
|
||||
|
||||
|
|
@ -103,30 +115,16 @@ export const WizardExpectedVotesStep = ({
|
|||
|
||||
setEstimates(payload.savedExpectedVotes)
|
||||
recordLastActedMunicipality(municipalitySlug)
|
||||
setPhase('saved')
|
||||
toast.success(WIZARD_VOTES_SAVED_MESSAGE)
|
||||
continueAfterVotes()
|
||||
})
|
||||
}
|
||||
|
||||
if (phase === 'saved') {
|
||||
return (
|
||||
<CampaignWizardShell
|
||||
flowTitle={wizardFlowTitleForSlug(actionSlug)}
|
||||
stepTitle="Votos atualizados"
|
||||
isEntryStep={false}
|
||||
previousHref={CAMPAIGN_HOME}
|
||||
dismissHref={CAMPAIGN_HOME}
|
||||
municipalityLabel={municipalityName}
|
||||
>
|
||||
<div className="flex flex-col gap-4" role="status" aria-live="polite">
|
||||
<p className="text-sm text-muted-foreground">{WIZARD_VOTES_SAVED_MESSAGE}</p>
|
||||
<p className="text-sm text-muted-foreground">{wizardNextStepPlaceholder(actionSlug)}</p>
|
||||
<Button asChild className="min-h-11 w-full sm:w-auto">
|
||||
<CampaignWizardNavLink href={CAMPAIGN_HOME}>Voltar ao Início</CampaignWizardNavLink>
|
||||
</Button>
|
||||
</div>
|
||||
</CampaignWizardShell>
|
||||
)
|
||||
}
|
||||
const trailingAction = skip ? (
|
||||
<Button variant="ghost" size="sm" className="min-h-11 px-2 text-sm" asChild>
|
||||
<CampaignWizardNavLink href={skip.href}>{skip.label}</CampaignWizardNavLink>
|
||||
</Button>
|
||||
) : undefined
|
||||
|
||||
return (
|
||||
<CampaignWizardShell
|
||||
|
|
@ -136,6 +134,8 @@ export const WizardExpectedVotesStep = ({
|
|||
previousHref={wizardActionHref(actionSlug)}
|
||||
dismissHref={CAMPAIGN_HOME}
|
||||
municipalityLabel={municipalityName}
|
||||
skip={skip}
|
||||
trailingAction={trailingAction}
|
||||
>
|
||||
<div
|
||||
className="flex flex-col gap-6"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import {
|
||||
CAMPAIGN_WIZARD_ACTION_SLUGS,
|
||||
campaignWizardActionIdForSlug,
|
||||
type CampaignWizardActionId,
|
||||
} from '@/lib/campaignActionRoutes'
|
||||
import { wizardFlowTitleForActionId } from '@/lib/campaignHomeActions'
|
||||
import { resolveWizardChainEntry, wizardChainContinueHref } from '@/lib/wizardActionChain'
|
||||
|
||||
export const WIZARD_MUNICIPALITY_STEP_TITLE = 'Em qual município?' as const
|
||||
|
||||
|
|
@ -30,13 +32,35 @@ export const WIZARD_MUNICIPALITY_SEARCH_EMPTY = 'Nenhum município encontrado.'
|
|||
|
||||
export const WIZARD_VOTES_SAVED_MESSAGE = 'Votos estimados atualizados.' as const
|
||||
|
||||
const WIZARD_VOTES_NEXT_FLOW_PLACEHOLDER =
|
||||
'Próximo passo deste fluxo (sinal, tendência ou resumo) em breve.' as const
|
||||
const WIZARD_VOTES_SKIP_LABEL = 'Pular ajuste de votos →' as const
|
||||
|
||||
export const WIZARD_VOTES_FINAL_CTA_LABEL = 'Salvar estimativas →' as const
|
||||
|
||||
const WIZARD_NEXT_STEP_GENERIC_PLACEHOLDER = 'Próximo passo deste fluxo em breve.' as const
|
||||
|
||||
export type WizardVotesSkipAction = {
|
||||
label: string
|
||||
href: string
|
||||
}
|
||||
|
||||
const shouldShowWizardVotesSkip = (entryAction: CampaignWizardActionId | undefined): boolean =>
|
||||
entryAction != null && entryAction !== 'update-votes'
|
||||
|
||||
export const resolveWizardVotesSkip = (
|
||||
entryAction: CampaignWizardActionId | undefined,
|
||||
municipalitySlug: string,
|
||||
): WizardVotesSkipAction | undefined =>
|
||||
shouldShowWizardVotesSkip(entryAction)
|
||||
? {
|
||||
label: WIZARD_VOTES_SKIP_LABEL,
|
||||
href: wizardChainContinueHref(
|
||||
resolveWizardChainEntry(entryAction, 'update-votes'),
|
||||
'update-votes',
|
||||
municipalitySlug,
|
||||
),
|
||||
}
|
||||
: undefined
|
||||
|
||||
export const wizardNextStepTitle = (actionSlug: string): string => {
|
||||
if (actionSlug === CAMPAIGN_WIZARD_ACTION_SLUGS['update-votes']) {
|
||||
return 'Ajustar votos estimados'
|
||||
|
|
@ -44,12 +68,8 @@ export const wizardNextStepTitle = (actionSlug: string): string => {
|
|||
return 'Continuar'
|
||||
}
|
||||
|
||||
export const wizardNextStepPlaceholder = (actionSlug: string): string => {
|
||||
if (actionSlug === CAMPAIGN_WIZARD_ACTION_SLUGS['update-votes']) {
|
||||
return WIZARD_VOTES_NEXT_FLOW_PLACEHOLDER
|
||||
}
|
||||
return WIZARD_NEXT_STEP_GENERIC_PLACEHOLDER
|
||||
}
|
||||
export const wizardNextStepPlaceholder = (_actionSlug: string): string =>
|
||||
WIZARD_NEXT_STEP_GENERIC_PLACEHOLDER
|
||||
|
||||
export const WIZARD_LEADERSHIP_GRID_TITLE = 'Quem coordena por aqui?' as const
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { CampaignWizardActionId } from '@/lib/campaignActionRoutes'
|
||||
import { CAMPAIGN_HOME } from '@/lib/campaignPaths'
|
||||
import { politicalTrendStatuses, type PoliticalTrendStatusValue } from '@/lib/schemas/municipality'
|
||||
import {
|
||||
municipalitySignalTypeLabels,
|
||||
type MunicipalitySignalType,
|
||||
} from '@/lib/schemas/municipalityUpdate'
|
||||
import { resolveWizardChainEntry, wizardChainContinueHref } from '@/lib/wizardActionChain'
|
||||
|
||||
const politicalTrendDisplayLabels: Record<PoliticalTrendStatusValue, string> = {
|
||||
favoravel: 'Favorável',
|
||||
|
|
@ -69,9 +69,17 @@ export const shouldShowWizardTrendSkip = (
|
|||
|
||||
export const resolveWizardTrendSkip = (
|
||||
entryAction: CampaignWizardActionId | undefined,
|
||||
municipalitySlug: string,
|
||||
): WizardTrendSkipAction | undefined =>
|
||||
shouldShowWizardTrendSkip(entryAction)
|
||||
? { label: WIZARD_TREND_SKIP_LABEL, href: CAMPAIGN_HOME }
|
||||
? {
|
||||
label: WIZARD_TREND_SKIP_LABEL,
|
||||
href: wizardChainContinueHref(
|
||||
resolveWizardChainEntry(entryAction, 'change-trend'),
|
||||
'change-trend',
|
||||
municipalitySlug,
|
||||
),
|
||||
}
|
||||
: undefined
|
||||
|
||||
export type PoliticalTrendNotePrefillSource =
|
||||
|
|
|
|||
97
src/lib/wizardActionChain.ts
Normal file
97
src/lib/wizardActionChain.ts
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* B98 — ordered sub-flow queue after the principal wizard action succeeds.
|
||||
* Pure navigation helpers; each link still writes via its existing action.
|
||||
*/
|
||||
|
||||
import {
|
||||
CAMPAIGN_WIZARD_ACTION_SLUGS,
|
||||
type CampaignWizardActionId,
|
||||
wizardActionHref,
|
||||
wizardSignalHref,
|
||||
wizardTrendHref,
|
||||
} from '@/lib/campaignActionRoutes'
|
||||
import { CAMPAIGN_HOME } from '@/lib/campaignPaths'
|
||||
|
||||
/** Actions that participate in the v1 adjustment chain (excludes demand). */
|
||||
export type WizardChainActionId = Exclude<CampaignWizardActionId, 'register-demand'>
|
||||
|
||||
const WIZARD_CHAIN_AFTER = {
|
||||
'update-votes': ['change-trend', 'register-signal', 'update-leadership'],
|
||||
'register-signal': ['change-trend', 'update-votes', 'update-leadership'],
|
||||
'change-trend': ['register-signal', 'update-votes', 'update-leadership'],
|
||||
'update-leadership': ['register-signal', 'update-votes', 'change-trend'],
|
||||
} as const satisfies Record<WizardChainActionId, readonly WizardChainActionId[]>
|
||||
|
||||
export const isWizardChainActionId = (
|
||||
id: CampaignWizardActionId | undefined,
|
||||
): id is WizardChainActionId => id != null && id in WIZARD_CHAIN_AFTER
|
||||
|
||||
/**
|
||||
* Remaining chained steps after `completedAction` in a session owned by `entryAction`.
|
||||
* When `completedAction` is the principal (or omitted), returns the full queue.
|
||||
*/
|
||||
export const wizardChainAfter = (
|
||||
entryAction: WizardChainActionId,
|
||||
completedAction?: WizardChainActionId,
|
||||
): WizardChainActionId[] => {
|
||||
const queue: WizardChainActionId[] = [...WIZARD_CHAIN_AFTER[entryAction]]
|
||||
if (!completedAction || completedAction === entryAction) {
|
||||
return queue
|
||||
}
|
||||
const index = queue.indexOf(completedAction)
|
||||
if (index === -1) {
|
||||
return []
|
||||
}
|
||||
return queue.slice(index + 1)
|
||||
}
|
||||
|
||||
export const nextWizardChainStep = (
|
||||
entryAction: WizardChainActionId,
|
||||
completedAction: WizardChainActionId,
|
||||
): WizardChainActionId | undefined => wizardChainAfter(entryAction, completedAction)[0]
|
||||
|
||||
const wizardHrefForChainStep = (
|
||||
action: WizardChainActionId,
|
||||
municipalitySlug: string,
|
||||
entryAction: WizardChainActionId,
|
||||
): string => {
|
||||
const actionSlug = CAMPAIGN_WIZARD_ACTION_SLUGS[action]
|
||||
switch (action) {
|
||||
case 'update-votes':
|
||||
case 'update-leadership':
|
||||
return wizardActionHref(actionSlug, municipalitySlug, { entryAction })
|
||||
case 'register-signal':
|
||||
return wizardSignalHref(actionSlug, municipalitySlug, undefined, entryAction)
|
||||
case 'change-trend':
|
||||
return wizardTrendHref(actionSlug, municipalitySlug, undefined, entryAction)
|
||||
default: {
|
||||
const exhaustive: never = action
|
||||
return exhaustive
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Href for the next chained wizard after completing/skipping `completedAction`,
|
||||
* or Início when the queue is empty / entry is not chainable.
|
||||
*/
|
||||
export const wizardChainContinueHref = (
|
||||
entryAction: CampaignWizardActionId,
|
||||
completedAction: WizardChainActionId,
|
||||
municipalitySlug: string,
|
||||
): string => {
|
||||
if (!isWizardChainActionId(entryAction)) {
|
||||
return CAMPAIGN_HOME
|
||||
}
|
||||
const next = nextWizardChainStep(entryAction, completedAction)
|
||||
if (!next) {
|
||||
return CAMPAIGN_HOME
|
||||
}
|
||||
return wizardHrefForChainStep(next, municipalitySlug, entryAction)
|
||||
}
|
||||
|
||||
/** Session owner for navigation: explicit `entry` query, else the current principal. */
|
||||
export const resolveWizardChainEntry = (
|
||||
entryAction: CampaignWizardActionId | undefined,
|
||||
currentAction: WizardChainActionId,
|
||||
): WizardChainActionId => (isWizardChainActionId(entryAction) ? entryAction : currentAction)
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
import type { CampaignWizardActionId } from '@/lib/campaignActionRoutes'
|
||||
import { WIZARD_LEADERSHIP_SKIP_LABEL } from '@/lib/campaignWizardCopy'
|
||||
import type { SupportStatus } from '@/lib/schemas/leadership'
|
||||
import { resolveWizardChainEntry, wizardChainContinueHref } from '@/lib/wizardActionChain'
|
||||
|
||||
/** Client-safe tile payload for the B70 leadership wizard grid. */
|
||||
export type WizardLeadershipTileViewModel = {
|
||||
|
|
@ -12,7 +14,27 @@ export type WizardLeadershipTileViewModel = {
|
|||
notes: string | null
|
||||
}
|
||||
|
||||
export type WizardLeadershipSkipAction = {
|
||||
label: string
|
||||
href: string
|
||||
}
|
||||
|
||||
export const showLeadershipWizardSkip = (entryAction?: CampaignWizardActionId): boolean => {
|
||||
const effectiveEntry = entryAction ?? 'update-leadership'
|
||||
return effectiveEntry !== 'update-leadership'
|
||||
}
|
||||
|
||||
export const resolveWizardLeadershipSkip = (
|
||||
entryAction: CampaignWizardActionId | undefined,
|
||||
municipalitySlug: string,
|
||||
): WizardLeadershipSkipAction | undefined =>
|
||||
showLeadershipWizardSkip(entryAction)
|
||||
? {
|
||||
label: WIZARD_LEADERSHIP_SKIP_LABEL,
|
||||
href: wizardChainContinueHref(
|
||||
resolveWizardChainEntry(entryAction, 'update-leadership'),
|
||||
'update-leadership',
|
||||
municipalitySlug,
|
||||
),
|
||||
}
|
||||
: undefined
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { CampaignWizardActionId } from '@/lib/campaignActionRoutes'
|
||||
import { CAMPAIGN_HOME } from '@/lib/campaignPaths'
|
||||
import { resolveWizardChainEntry, wizardChainContinueHref } from '@/lib/wizardActionChain'
|
||||
|
||||
export const WIZARD_SIGNAL_TYPE_STEP_TITLE = 'Que tipo de sinal?' as const
|
||||
|
||||
|
|
@ -22,7 +22,15 @@ export const shouldShowWizardSignalSkip = (
|
|||
|
||||
export const resolveWizardSignalSkip = (
|
||||
entryAction: CampaignWizardActionId | undefined,
|
||||
municipalitySlug: string,
|
||||
): WizardSignalSkipAction | undefined =>
|
||||
shouldShowWizardSignalSkip(entryAction)
|
||||
? { label: WIZARD_SIGNAL_SKIP_LABEL, href: CAMPAIGN_HOME }
|
||||
? {
|
||||
label: WIZARD_SIGNAL_SKIP_LABEL,
|
||||
href: wizardChainContinueHref(
|
||||
resolveWizardChainEntry(entryAction, 'register-signal'),
|
||||
'register-signal',
|
||||
municipalitySlug,
|
||||
),
|
||||
}
|
||||
: undefined
|
||||
|
|
|
|||
|
|
@ -219,6 +219,11 @@ test.describe('Wizard — ajuste de votos (B61 / B77)', () => {
|
|||
await step.getByRole('textbox', { name: 'Otimista' }).fill('350')
|
||||
await page.getByRole('button', { name: 'Salvar estimativas →' }).click()
|
||||
|
||||
await page.waitForURL(
|
||||
new RegExp(
|
||||
`/campanha/acoes/mudar-tendencia\\?municipio=${municipality.slug}&entry=update-votes`,
|
||||
),
|
||||
)
|
||||
await expect(page.getByText('Votos estimados atualizados.')).toBeVisible({ timeout: 15000 })
|
||||
expect(page.url()).not.toContain('cenario=')
|
||||
})
|
||||
|
|
@ -273,7 +278,7 @@ test.describe('Wizard — ajuste de votos (B61 / B77)', () => {
|
|||
})
|
||||
|
||||
test.describe('Wizard — atualizar liderança (B70)', () => {
|
||||
test('create leadership, save, and continue to home', async ({ campaign, page }) => {
|
||||
test('create leadership, save, and continue into the chain', async ({ campaign, page }) => {
|
||||
const { fixtures } = campaign
|
||||
const coordinator = await fixtures.createCampaignUser('coordinator', {
|
||||
name: fixtures.value('Coordenadora Geral'),
|
||||
|
|
@ -301,13 +306,17 @@ test.describe('Wizard — atualizar liderança (B70)', () => {
|
|||
await expect(page.getByRole('button', { name: 'Continuar' })).toBeVisible()
|
||||
|
||||
await page.getByRole('button', { name: 'Continuar' }).click()
|
||||
await page.waitForURL('/campanha')
|
||||
await expect(page.getByLabel('Buscar na campanha')).toBeVisible()
|
||||
await page.waitForURL(
|
||||
new RegExp(
|
||||
`/campanha/acoes/registrar-sinal\\?municipio=${municipality.slug}&entry=update-leadership`,
|
||||
),
|
||||
)
|
||||
await expect(page.getByRole('heading', { name: 'Que tipo de sinal?' })).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Wizard — registrar sinal (B63)', () => {
|
||||
test('standalone flow: type grid, body step, save returns to Início', async ({
|
||||
test('standalone flow: type grid, body step, save continues the chain', async ({
|
||||
campaign,
|
||||
page,
|
||||
}) => {
|
||||
|
|
@ -335,7 +344,11 @@ test.describe('Wizard — registrar sinal (B63)', () => {
|
|||
await body.fill('Adversário marcou presença no centro do município.')
|
||||
await page.getByRole('button', { name: 'Salvar' }).click()
|
||||
|
||||
await page.waitForURL(/\/campanha$/)
|
||||
await page.waitForURL(
|
||||
new RegExp(
|
||||
`/campanha/acoes/mudar-tendencia\\?municipio=${municipality.slug}&entry=register-signal`,
|
||||
),
|
||||
)
|
||||
await expect(page.getByText('Sinal registrado.')).toBeVisible()
|
||||
})
|
||||
|
||||
|
|
@ -362,7 +375,7 @@ test.describe('Wizard — registrar sinal (B63)', () => {
|
|||
await expect(page.getByRole('dialog')).toHaveCount(0)
|
||||
})
|
||||
|
||||
test('embedded flow shows skip link', async ({ campaign, page }) => {
|
||||
test('embedded flow shows skip link to the next chain step', async ({ campaign, page }) => {
|
||||
const { fixtures } = campaign
|
||||
const coordinator = await fixtures.createCampaignUser('coordinator', {
|
||||
name: fixtures.value('Coordenadora Geral'),
|
||||
|
|
@ -374,8 +387,11 @@ test.describe('Wizard — registrar sinal (B63)', () => {
|
|||
`/campanha/acoes/registrar-sinal?municipio=${municipality.slug}&entry=update-votes`,
|
||||
)
|
||||
|
||||
await expect(page.getByRole('link', { name: 'Pular registro de sinal' })).toBeVisible({
|
||||
timeout: 15000,
|
||||
})
|
||||
const skip = page.getByRole('link', { name: 'Pular registro de sinal' })
|
||||
await expect(skip).toBeVisible({ timeout: 15000 })
|
||||
await expect(skip).toHaveAttribute(
|
||||
'href',
|
||||
`/campanha/acoes/atualizar-lideranca?municipio=${municipality.slug}&entry=update-votes`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { CAMPAIGN_HOME } from '@/lib/campaignPaths'
|
||||
import { CAMPAIGN_ACTIONS_HOME } from '@/lib/campaignActionRoutes'
|
||||
import {
|
||||
politicalTrendWizardMeta,
|
||||
politicalTrendWizardMetaByStatus,
|
||||
|
|
@ -39,16 +39,16 @@ describe('politicalTrendWizardUi', () => {
|
|||
it('hides skip for standalone change-trend entry', () => {
|
||||
expect(shouldShowWizardTrendSkip(undefined)).toBe(false)
|
||||
expect(shouldShowWizardTrendSkip('change-trend')).toBe(false)
|
||||
expect(resolveWizardTrendSkip(undefined)).toBeUndefined()
|
||||
expect(resolveWizardTrendSkip('change-trend')).toBeUndefined()
|
||||
expect(resolveWizardTrendSkip(undefined, 'cairu')).toBeUndefined()
|
||||
expect(resolveWizardTrendSkip('change-trend', 'cairu')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('shows skip when embedded from another wizard action', () => {
|
||||
it('shows skip to the next chain step when embedded from another wizard', () => {
|
||||
expect(shouldShowWizardTrendSkip('update-votes')).toBe(true)
|
||||
expect(shouldShowWizardTrendSkip('register-signal')).toBe(true)
|
||||
expect(resolveWizardTrendSkip('update-votes')).toEqual({
|
||||
expect(resolveWizardTrendSkip('update-votes', 'cairu')).toEqual({
|
||||
label: WIZARD_TREND_SKIP_LABEL,
|
||||
href: CAMPAIGN_HOME,
|
||||
href: `${CAMPAIGN_ACTIONS_HOME}/registrar-sinal?municipio=cairu&entry=update-votes`,
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
80
tests/unit/wizardActionChain.unit.spec.ts
Normal file
80
tests/unit/wizardActionChain.unit.spec.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { CAMPAIGN_ACTIONS_HOME } from '@/lib/campaignActionRoutes'
|
||||
import { CAMPAIGN_HOME } from '@/lib/campaignPaths'
|
||||
import {
|
||||
isWizardChainActionId,
|
||||
nextWizardChainStep,
|
||||
resolveWizardChainEntry,
|
||||
wizardChainAfter,
|
||||
wizardChainContinueHref,
|
||||
} from '@/lib/wizardActionChain'
|
||||
|
||||
describe('wizardActionChain', () => {
|
||||
it('excludes register-demand from the chainable set', () => {
|
||||
expect(isWizardChainActionId('update-votes')).toBe(true)
|
||||
expect(isWizardChainActionId('register-demand')).toBe(false)
|
||||
expect(isWizardChainActionId(undefined)).toBe(false)
|
||||
})
|
||||
|
||||
it('returns the full queue after the principal action', () => {
|
||||
expect(wizardChainAfter('update-votes')).toEqual([
|
||||
'change-trend',
|
||||
'register-signal',
|
||||
'update-leadership',
|
||||
])
|
||||
expect(wizardChainAfter('update-votes', 'update-votes')).toEqual([
|
||||
'change-trend',
|
||||
'register-signal',
|
||||
'update-leadership',
|
||||
])
|
||||
expect(wizardChainAfter('register-signal', 'register-signal')).toEqual([
|
||||
'change-trend',
|
||||
'update-votes',
|
||||
'update-leadership',
|
||||
])
|
||||
expect(wizardChainAfter('change-trend', 'change-trend')).toEqual([
|
||||
'register-signal',
|
||||
'update-votes',
|
||||
'update-leadership',
|
||||
])
|
||||
expect(wizardChainAfter('update-leadership', 'update-leadership')).toEqual([
|
||||
'register-signal',
|
||||
'update-votes',
|
||||
'change-trend',
|
||||
])
|
||||
})
|
||||
|
||||
it('slices the remaining queue after a chained step', () => {
|
||||
expect(wizardChainAfter('update-votes', 'change-trend')).toEqual([
|
||||
'register-signal',
|
||||
'update-leadership',
|
||||
])
|
||||
expect(wizardChainAfter('update-votes', 'register-signal')).toEqual(['update-leadership'])
|
||||
expect(wizardChainAfter('update-votes', 'update-leadership')).toEqual([])
|
||||
expect(nextWizardChainStep('change-trend', 'register-signal')).toBe('update-votes')
|
||||
expect(nextWizardChainStep('update-leadership', 'change-trend')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('builds continue hrefs with entry + municipio, or Início at the end', () => {
|
||||
expect(wizardChainContinueHref('update-votes', 'update-votes', 'cairu')).toBe(
|
||||
`${CAMPAIGN_ACTIONS_HOME}/mudar-tendencia?municipio=cairu&entry=update-votes`,
|
||||
)
|
||||
expect(wizardChainContinueHref('update-votes', 'change-trend', 'cairu')).toBe(
|
||||
`${CAMPAIGN_ACTIONS_HOME}/registrar-sinal?municipio=cairu&entry=update-votes`,
|
||||
)
|
||||
expect(wizardChainContinueHref('register-signal', 'change-trend', 'cairu')).toBe(
|
||||
`${CAMPAIGN_ACTIONS_HOME}/atualizar-votos?municipio=cairu&entry=register-signal`,
|
||||
)
|
||||
expect(wizardChainContinueHref('change-trend', 'update-leadership', 'cairu')).toBe(
|
||||
CAMPAIGN_HOME,
|
||||
)
|
||||
expect(wizardChainContinueHref('register-demand', 'update-votes', 'cairu')).toBe(CAMPAIGN_HOME)
|
||||
})
|
||||
|
||||
it('resolves session entry from query or current principal', () => {
|
||||
expect(resolveWizardChainEntry(undefined, 'update-votes')).toBe('update-votes')
|
||||
expect(resolveWizardChainEntry('register-signal', 'update-votes')).toBe('register-signal')
|
||||
expect(resolveWizardChainEntry('register-demand', 'change-trend')).toBe('change-trend')
|
||||
})
|
||||
})
|
||||
|
|
@ -3,12 +3,15 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
|
|||
|
||||
import { WizardExpectedVotesStep } from '@/components/campaign/shared/WizardExpectedVotesStep'
|
||||
import { CampaignWizardChromeProvider } from '@/components/campaign/shell/CampaignWizardChromeContext'
|
||||
import { CAMPAIGN_ACTIONS_HOME } from '@/lib/campaignActionRoutes'
|
||||
import { postCampaignJson } from '@/lib/campaignJsonRequest'
|
||||
|
||||
const replaceMock = vi.fn()
|
||||
|
||||
vi.mock('next/navigation', () => ({
|
||||
useRouter: () => ({
|
||||
push: vi.fn(),
|
||||
replace: vi.fn(),
|
||||
replace: replaceMock,
|
||||
}),
|
||||
}))
|
||||
|
||||
|
|
@ -16,6 +19,10 @@ vi.mock('@/lib/campaignJsonRequest', () => ({
|
|||
postCampaignJson: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('sonner', () => ({
|
||||
toast: { success: vi.fn() },
|
||||
}))
|
||||
|
||||
const defaultProps = {
|
||||
actionSlug: 'atualizar-votos',
|
||||
municipalityId: 1,
|
||||
|
|
@ -35,6 +42,7 @@ describe('WizardExpectedVotesStep', () => {
|
|||
afterEach(() => {
|
||||
cleanup()
|
||||
vi.mocked(postCampaignJson).mockReset()
|
||||
replaceMock.mockReset()
|
||||
})
|
||||
|
||||
it('applies shortcuts to the focused scenario', () => {
|
||||
|
|
@ -73,7 +81,7 @@ describe('WizardExpectedVotesStep', () => {
|
|||
expect(screen.getByLabelText('Pessimista').getAttribute('aria-invalid')).toBeNull()
|
||||
})
|
||||
|
||||
it('posts batch expected votes when estimates are coherent', async () => {
|
||||
it('posts batch expected votes and continues the wizard chain', async () => {
|
||||
vi.mocked(postCampaignJson).mockResolvedValue({
|
||||
ok: true,
|
||||
payload: {
|
||||
|
|
@ -97,7 +105,18 @@ describe('WizardExpectedVotesStep', () => {
|
|||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('status').textContent).toMatch(/Votos estimados atualizados/i)
|
||||
expect(replaceMock).toHaveBeenCalledWith(
|
||||
`${CAMPAIGN_ACTIONS_HOME}/mudar-tendencia?municipio=cairu&entry=update-votes`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('shows skip to the next chain step when votes is chained', () => {
|
||||
renderVotesStep({ ...defaultProps, entryAction: 'register-signal' })
|
||||
|
||||
const skip = screen.getByRole('link', { name: /Pular ajuste de votos/i })
|
||||
expect(skip.getAttribute('href')).toBe(
|
||||
`${CAMPAIGN_ACTIONS_HOME}/atualizar-lideranca?municipio=cairu&entry=register-signal`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { showLeadershipWizardSkip } from '@/lib/wizardLeadershipContract'
|
||||
import { CAMPAIGN_HOME } from '@/lib/campaignPaths'
|
||||
import { WIZARD_LEADERSHIP_SKIP_LABEL } from '@/lib/campaignWizardCopy'
|
||||
import {
|
||||
resolveWizardLeadershipSkip,
|
||||
showLeadershipWizardSkip,
|
||||
} from '@/lib/wizardLeadershipContract'
|
||||
|
||||
describe('showLeadershipWizardSkip', () => {
|
||||
it('hides skip for standalone entry (implicit update-leadership)', () => {
|
||||
|
|
@ -15,3 +20,18 @@ describe('showLeadershipWizardSkip', () => {
|
|||
expect(showLeadershipWizardSkip('update-votes')).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('resolveWizardLeadershipSkip', () => {
|
||||
it('returns undefined for standalone entry', () => {
|
||||
expect(resolveWizardLeadershipSkip(undefined, 'cairu')).toBeUndefined()
|
||||
expect(resolveWizardLeadershipSkip('update-leadership', 'cairu')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('points skip to Início when leadership is the last chained step', () => {
|
||||
// In the v1 matrix, leadership is always the final chained elo.
|
||||
expect(resolveWizardLeadershipSkip('update-votes', 'cairu')).toEqual({
|
||||
label: WIZARD_LEADERSHIP_SKIP_LABEL,
|
||||
href: CAMPAIGN_HOME,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { CAMPAIGN_HOME } from '@/lib/campaignPaths'
|
||||
import { CAMPAIGN_ACTIONS_HOME } from '@/lib/campaignActionRoutes'
|
||||
import {
|
||||
municipalitySignalTypeMeta,
|
||||
municipalitySignalTypeMetaByType,
|
||||
|
|
@ -16,16 +16,20 @@ describe('wizardSignalUi', () => {
|
|||
it('hides skip for standalone register-signal entry', () => {
|
||||
expect(shouldShowWizardSignalSkip(undefined)).toBe(false)
|
||||
expect(shouldShowWizardSignalSkip('register-signal')).toBe(false)
|
||||
expect(resolveWizardSignalSkip(undefined)).toBeUndefined()
|
||||
expect(resolveWizardSignalSkip('register-signal')).toBeUndefined()
|
||||
expect(resolveWizardSignalSkip(undefined, 'cairu')).toBeUndefined()
|
||||
expect(resolveWizardSignalSkip('register-signal', 'cairu')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('shows skip when embedded from another wizard action', () => {
|
||||
it('shows skip to the next chain step when embedded from another wizard', () => {
|
||||
expect(shouldShowWizardSignalSkip('update-votes')).toBe(true)
|
||||
expect(shouldShowWizardSignalSkip('change-trend')).toBe(true)
|
||||
expect(resolveWizardSignalSkip('update-votes')).toEqual({
|
||||
expect(resolveWizardSignalSkip('update-votes', 'cairu')).toEqual({
|
||||
label: WIZARD_SIGNAL_SKIP_LABEL,
|
||||
href: CAMPAIGN_HOME,
|
||||
href: `${CAMPAIGN_ACTIONS_HOME}/atualizar-lideranca?municipio=cairu&entry=update-votes`,
|
||||
})
|
||||
expect(resolveWizardSignalSkip('change-trend', 'cairu')).toEqual({
|
||||
label: WIZARD_SIGNAL_SKIP_LABEL,
|
||||
href: `${CAMPAIGN_ACTIONS_HOME}/atualizar-votos?municipio=cairu&entry=change-trend`,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue