teqo/src/components/PetitionForm.tsx
Francisco Ignácio Fontoura Solla 4806c417c8 Engineering Pass 3: consolidate patterns and lock them with guards.
Fold duplicated campanha access, loaders, list/filter/form shells, and scripts into shared modules; reorganize utilities by domain; stabilize e2e; and document prevention-first audit workflow so the same smells cannot creep back in.
2026-07-29 06:11:29 -03:00

150 lines
5.1 KiB
TypeScript

'use client'
import { submitPetitionSignature } from '@/app/(frontend)/actions/submitPetitionSignature'
import {
Field,
FieldDescription,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSet,
} from '@/components/ui/field'
import { trackMetaLead } from '@/lib/facebookPixel'
import { petitionFormSchema, type PetitionFormInput } from '@/lib/schemas/petition-form'
import { Petition } from '@/payload-types'
import { zodResolver } from '@hookform/resolvers/zod'
import { useState, useTransition } from 'react'
import { FormProvider, useForm, type SubmitHandler } from 'react-hook-form'
import { CitySelect } from './CitySelect'
import { EmailInput } from './EmailInput'
import { NameInput } from './NameInput'
import { PetitionSuccessDialog } from './PetitionSuccessDialog'
import { PhoneInput } from './PhoneInput'
import { PostalCodeInput } from './PostalCodeInput'
import { SIGNATURE_CREATED_EVENT, type SignatureCreatedDetail } from './SignatureCounter'
import { StateSelect } from './StateSelect'
import { Button } from './ui/button'
import { Textarea } from './ui/textarea'
interface PetitionFormProps {
id: string
petition: Petition
consentHTML: string
facebookPixelId?: string
}
export const PetitionForm = ({ id, petition, consentHTML, facebookPixelId }: PetitionFormProps) => {
const methods = useForm<PetitionFormInput>({
resolver: zodResolver(petitionFormSchema),
defaultValues: {
name: '',
email: '',
phone: '',
state: undefined,
city: '',
postalCode: '',
comment: '',
},
})
const [isSubmitting, startTransition] = useTransition()
const [isSuccessOpen, setIsSuccessOpen] = useState(false)
const [signatureNumber, setSignatureNumber] = useState<number | null>(null)
const onSubmit: SubmitHandler<PetitionFormInput> = (input) => {
startTransition(async () => {
try {
const result = await submitPetitionSignature({
...input,
petitionId: petition.id,
})
if (facebookPixelId) {
trackMetaLead(facebookPixelId, petition.title, crypto.randomUUID())
}
methods.reset()
setSignatureNumber(result.signatureNumber)
const detail: SignatureCreatedDetail = {
petitionId: petition.id,
count: result.signatureNumber,
}
window.dispatchEvent(new CustomEvent(SIGNATURE_CREATED_EVENT, { detail }))
setIsSuccessOpen(true)
} catch {
methods.setError('root', {
message: 'Falha ao enviar assinatura. Tente novamente.',
})
}
})
}
return (
<FormProvider {...methods}>
<PetitionSuccessDialog
open={isSuccessOpen}
onOpenChange={setIsSuccessOpen}
petitionTitle={petition.title}
signatureNumber={signatureNumber}
/>
<form id={id} onSubmit={methods.handleSubmit(onSubmit)}>
<FieldSet>
{petition.form.title ? <FieldLegend>{petition.form.title}</FieldLegend> : null}
{petition.form.subtitle ? (
<FieldDescription>{petition.form.subtitle}</FieldDescription>
) : null}
<FieldGroup>
<Field>
<FieldLabel htmlFor="name">Nome Completo</FieldLabel>
<NameInput />
</Field>
<Field>
<FieldLabel htmlFor="email">E-mail</FieldLabel>
<EmailInput />
</Field>
<Field>
<FieldLabel htmlFor="phone">Celular</FieldLabel>
<PhoneInput />
</Field>
<Field>
<FieldLabel htmlFor="state">Estado</FieldLabel>
<StateSelect />
</Field>
<Field>
<FieldLabel htmlFor="city">Cidade</FieldLabel>
<CitySelect />
</Field>
<Field>
<FieldLabel htmlFor="postalCode">CEP</FieldLabel>
<PostalCodeInput />
</Field>
<Field>
<FieldLabel htmlFor="comment">Comentário</FieldLabel>
<Textarea id="comment" {...methods.register('comment')} />
</Field>
<Field>
{consentHTML ? (
<div
className="petition-consent text-left text-sm leading-normal font-normal text-muted-foreground [&_a]:underline [&_a]:underline-offset-4 [&_a:hover]:text-primary [&_p]:m-0 [&_strong]:font-semibold [&_strong]:text-foreground"
dangerouslySetInnerHTML={{
__html: consentHTML,
}}
/>
) : null}
{methods.formState.errors.root?.message ? (
<FieldDescription className="text-destructive">
{methods.formState.errors.root.message}
</FieldDescription>
) : null}
<Button
type="submit"
disabled={isSubmitting}
className="text-primary-foreground hover:text-primary-foreground"
>
{isSubmitting ? 'Enviando...' : 'Assinar'}
</Button>
</Field>
</FieldGroup>
</FieldSet>
</form>
</FormProvider>
)
}