B63+: wire wizard signal steps to B75 chrome and DRY skip.

This commit is contained in:
Francisco Ignácio Fontoura Solla 2026-07-30 04:00:35 -03:00
parent 09fa21d4b3
commit 8c7633da4a
6 changed files with 67 additions and 42 deletions

View file

@ -1,27 +1,26 @@
'use client'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { useActionState } from 'react'
import { createMunicipalityListSignalFormAction } from '@/app/(campaign)/campanha/(app)/municipios/municipalityStaffFormActions'
import { CampaignFormActionMessage } from '@/components/campaign/shared/CampaignFormActionMessage'
import { CampaignWizardShell } from '@/components/campaign/shared/CampaignWizardShell'
import { WizardSignalSkipTrailing } from '@/components/campaign/municipality/WizardSignalSkipTrailing'
import { useCampaignFormSuccessToast } from '@/components/campaign/shared/useCampaignFormSuccessToast'
import { Button } from '@/components/ui/button'
import { Spinner } from '@/components/ui/Spinner'
import { Textarea } from '@/components/ui/textarea'
import type { CampaignWizardActionId } from '@/lib/campaignActionRoutes'
import { wizardSignalHref } from '@/lib/campaignActionRoutes'
import { wizardFlowTitleForSlug } from '@/lib/campaignWizardCopy'
import { CAMPAIGN_HOME } from '@/lib/campaignPaths'
import type { MunicipalitySignalType } from '@/lib/schemas/municipalityUpdate'
import { municipalitySignalTypeLabels } from '@/lib/schemas/municipalityUpdate'
import {
shouldShowWizardSignalSkip,
resolveWizardSignalSkip,
WIZARD_SIGNAL_BODY_STEP_TITLE_PREFIX,
WIZARD_SIGNAL_SAVE_LABEL,
WIZARD_SIGNAL_SKIP_LABEL,
wizardSignalSkipHref,
} from '@/lib/wizardSignalUi'
type WizardSignalBodyStepProps = {
@ -46,7 +45,7 @@ export const WizardSignalBodyStep = ({
createMunicipalityListSignalFormAction,
{},
)
const showSkip = shouldShowWizardSignalSkip(entryAction)
const skip = resolveWizardSignalSkip(entryAction)
const stepTitle = `${WIZARD_SIGNAL_BODY_STEP_TITLE_PREFIX}: ${municipalitySignalTypeLabels[signalType]}`
useCampaignFormSuccessToast(state, () => {
@ -55,16 +54,13 @@ export const WizardSignalBodyStep = ({
return (
<CampaignWizardShell
flowTitle={wizardFlowTitleForSlug(actionSlug)}
isEntryStep={false}
stepTitle={stepTitle}
previousHref={wizardSignalHref(actionSlug, municipalitySlug, undefined, entryAction)}
municipalityLabel={municipalityName}
trailingAction={
showSkip ? (
<Button variant="link" size="sm" className="h-auto px-2 py-1 text-xs" asChild>
<Link href={wizardSignalSkipHref()}>{WIZARD_SIGNAL_SKIP_LABEL}</Link>
</Button>
) : undefined
}
skip={skip}
trailingAction={skip ? <WizardSignalSkipTrailing skip={skip} /> : undefined}
>
<form
action={submitAction}

View file

@ -0,0 +1,14 @@
import Link from 'next/link'
import { Button } from '@/components/ui/button'
import type { WizardSignalSkipAction } from '@/lib/wizardSignalUi'
type WizardSignalSkipTrailingProps = {
skip: WizardSignalSkipAction
}
export const WizardSignalSkipTrailing = ({ skip }: WizardSignalSkipTrailingProps) => (
<Button variant="link" size="sm" className="h-auto px-2 py-1 text-xs" asChild>
<Link href={skip.href}>{skip.label}</Link>
</Button>
)

View file

@ -2,10 +2,10 @@
import { Info } from 'lucide-react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { useState } from 'react'
import { CampaignWizardShell } from '@/components/campaign/shared/CampaignWizardShell'
import { WizardSignalSkipTrailing } from '@/components/campaign/municipality/WizardSignalSkipTrailing'
import { Button } from '@/components/ui/button'
import {
Drawer,
@ -18,14 +18,16 @@ import {
} from '@/components/ui/Drawer'
import type { CampaignWizardActionId } from '@/lib/campaignActionRoutes'
import { wizardActionHref, wizardSignalHref } from '@/lib/campaignActionRoutes'
import { municipalitySignalTypeMeta } from '@/lib/municipalitySignalTypeMeta'
import { wizardFlowTitleForSlug } from '@/lib/campaignWizardCopy'
import {
municipalitySignalTypeMeta,
municipalitySignalTypeMetaByType,
} from '@/lib/municipalitySignalTypeMeta'
import type { MunicipalitySignalType } from '@/lib/schemas/municipalityUpdate'
import { cn } from '@/lib/utils'
import {
shouldShowWizardSignalSkip,
WIZARD_SIGNAL_SKIP_LABEL,
resolveWizardSignalSkip,
WIZARD_SIGNAL_TYPE_STEP_TITLE,
wizardSignalSkipHref,
} from '@/lib/wizardSignalUi'
type WizardSignalTypeStepProps = {
@ -41,44 +43,33 @@ export const WizardSignalTypeStep = ({
municipalitySlug,
entryAction,
}: WizardSignalTypeStepProps) => {
const router = useRouter()
const [infoType, setInfoType] = useState<MunicipalitySignalType | null>(null)
const infoEntry = infoType
? municipalitySignalTypeMeta.find((entry) => entry.type === infoType)
: null
const showSkip = shouldShowWizardSignalSkip(entryAction)
const infoEntry = infoType ? municipalitySignalTypeMetaByType[infoType] : null
const skip = resolveWizardSignalSkip(entryAction)
return (
<>
<CampaignWizardShell
flowTitle={wizardFlowTitleForSlug(actionSlug)}
isEntryStep={false}
stepTitle={WIZARD_SIGNAL_TYPE_STEP_TITLE}
previousHref={wizardActionHref(actionSlug)}
municipalityLabel={municipalityName}
contentAlign="end"
trailingAction={
showSkip ? (
<Button variant="link" size="sm" className="h-auto px-2 py-1 text-xs" asChild>
<Link href={wizardSignalSkipHref()}>{WIZARD_SIGNAL_SKIP_LABEL}</Link>
</Button>
) : undefined
}
skip={skip}
trailingAction={skip ? <WizardSignalSkipTrailing skip={skip} /> : undefined}
>
<ul className="grid list-none grid-cols-2 gap-3 md:grid-cols-3">
{municipalitySignalTypeMeta.map((entry) => {
const Icon = entry.icon
return (
<li key={entry.type} className="relative">
<button
type="button"
<Link
href={wizardSignalHref(actionSlug, municipalitySlug, entry.type, entryAction)}
className={cn(
'flex aspect-square w-full flex-col justify-between rounded-lg border border-border bg-transparent p-3 pr-10 text-left',
'transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
)}
onClick={() =>
router.push(
wizardSignalHref(actionSlug, municipalitySlug, entry.type, entryAction),
)
}
>
<Icon className="size-5 shrink-0 text-foreground" aria-hidden />
<div className="flex flex-col gap-0.5">
@ -87,7 +78,7 @@ export const WizardSignalTypeStep = ({
{entry.shortDescription}
</span>
</div>
</button>
</Link>
<Button
type="button"
variant="ghost"

View file

@ -51,3 +51,7 @@ export const municipalitySignalTypeMeta: MunicipalitySignalTypeMetaEntry[] =
infoContent: signalTypeInfoContent[type],
icon: signalTypeIcons[type],
}))
export const municipalitySignalTypeMetaByType = Object.fromEntries(
municipalitySignalTypeMeta.map((entry) => [entry.type, entry]),
) as Record<MunicipalitySignalType, MunicipalitySignalTypeMetaEntry>

View file

@ -11,9 +11,18 @@ export const WIZARD_SIGNAL_SAVE_LABEL = 'Salvar' as const
export const WIZARD_SIGNAL_SAVED_MESSAGE = 'Sinal registrado.' as const
export const wizardSignalSkipHref = (): string => CAMPAIGN_HOME
export type WizardSignalSkipAction = {
label: string
href: string
}
/** Skip is visible only when the wizard was embedded from another flow (not standalone register-signal). */
export const shouldShowWizardSignalSkip = (
entryAction: CampaignWizardActionId | undefined,
): boolean => entryAction != null && entryAction !== 'register-signal'
export const resolveWizardSignalSkip = (
entryAction: CampaignWizardActionId | undefined,
): WizardSignalSkipAction | undefined =>
shouldShowWizardSignalSkip(entryAction)
? { label: WIZARD_SIGNAL_SKIP_LABEL, href: CAMPAIGN_HOME }
: undefined

View file

@ -1,18 +1,29 @@
import { describe, expect, it } from 'vitest'
import { municipalitySignalTypeMeta } from '@/lib/municipalitySignalTypeMeta'
import { CAMPAIGN_HOME } from '@/lib/campaignPaths'
import { municipalitySignalTypeMeta, municipalitySignalTypeMetaByType } from '@/lib/municipalitySignalTypeMeta'
import { municipalitySignalTypes } from '@/lib/schemas/municipalityUpdate'
import { shouldShowWizardSignalSkip } from '@/lib/wizardSignalUi'
import {
resolveWizardSignalSkip,
shouldShowWizardSignalSkip,
WIZARD_SIGNAL_SKIP_LABEL,
} from '@/lib/wizardSignalUi'
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()
})
it('shows skip when embedded from another wizard action', () => {
expect(shouldShowWizardSignalSkip('update-votes')).toBe(true)
expect(shouldShowWizardSignalSkip('change-trend')).toBe(true)
expect(resolveWizardSignalSkip('update-votes')).toEqual({
label: WIZARD_SIGNAL_SKIP_LABEL,
href: CAMPAIGN_HOME,
})
})
})
@ -21,7 +32,7 @@ describe('municipalitySignalTypeMeta', () => {
expect(municipalitySignalTypeMeta).toHaveLength(municipalitySignalTypes.length)
for (const type of municipalitySignalTypes) {
const entry = municipalitySignalTypeMeta.find((item) => item.type === type)
const entry = municipalitySignalTypeMetaByType[type]
expect(entry?.label.length).toBeGreaterThan(0)
expect(entry?.shortDescription.length).toBeGreaterThan(0)
expect(entry?.infoContent.length).toBeGreaterThan(20)