Improve form field behavior, phone formatting

This commit is contained in:
Francisco Ignácio Fontoura Solla 2026-03-20 12:17:16 -03:00
parent 135f456543
commit 1e186e5296
8 changed files with 78 additions and 19 deletions

View file

@ -3,12 +3,13 @@ import { FormInput } from './FormInput'
export const EmailInput = (props: ComponentProps<'input'>) => (
<FormInput
form="email"
name="email"
id="email"
placeholder="Digite seu e-mail"
autoComplete="email"
inputMode="email"
type="text"
minLength={5}
maxLength={254}
required
{...props}

View file

@ -1,13 +1,43 @@
import type { ComponentProps } from 'react'
'use client'
import { ChangeEventHandler, type ComponentProps } from 'react'
import { Input } from './ui/input'
import { useFormContext } from 'react-hook-form'
import {
type FieldPath,
type FieldValues,
type RegisterOptions,
useFormContext,
} from 'react-hook-form'
interface FormInpuProps extends ComponentProps<'input'> {
form: string
interface FormInpuProps<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
> extends ComponentProps<'input'> {
name: TFieldName
options?: RegisterOptions<TFieldValues, TFieldName>
format?: (value: string) => string
}
export const FormInput = ({ form, ...props }: FormInpuProps) => {
const { register } = useFormContext()
export const FormInput = <
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
>({
name,
options,
format,
onChange,
...props
}: FormInpuProps<TFieldValues, TFieldName>) => {
const { register } = useFormContext<TFieldValues>()
const { onChange: onValueChange, ...registerResult } = register(name, options)
return <Input {...props} {...register(form)} />
const handleChange: ChangeEventHandler<HTMLInputElement> = (e) => {
const targetValue = e.target.value
const formattedValue = format?.(targetValue) ?? targetValue
e.target.value = formattedValue
onValueChange(e)
onChange?.(e)
}
return <Input {...props} {...registerResult} onChange={handleChange} />
}

View file

@ -3,12 +3,13 @@ import { FormInput } from './FormInput'
export const NameInput = (props: ComponentProps<'input'>) => (
<FormInput
form="name"
name="name"
id="name"
type="text"
placeholder="Digite seu nome completo"
autoComplete="name"
maxLength={120}
minLength={2}
required
{...props}
/>

View file

@ -3,14 +3,30 @@ import { FormInput } from './FormInput'
export const PhoneInput = (props: ComponentProps<'input'>) => (
<FormInput
form="phone"
name="phone"
id="phone"
placeholder="(71) 99999-9999"
autoComplete="tel"
inputMode="tel"
type="tel"
maxLength={15}
required
minLength={15}
format={formatPhone}
options={{
setValueAs: sanitizePhone,
}}
{...props}
/>
)
const sanitizePhone = (value: string) => value.replace(/\D/g, '').slice(0, 11)
const formatPhone = (value: string) => {
const digits = sanitizePhone(value)
if (!digits) return ''
if (digits.length <= 2) return `(${digits}`
if (digits.length <= 7) return `(${digits.slice(0, 2)}) ${digits.slice(2)}`
return `(${digits.slice(0, 2)}) ${digits.slice(2, 7)}-${digits.slice(7)}`
}

View file

@ -2,5 +2,12 @@ import type { ComponentProps } from 'react'
import { FormInput } from './FormInput'
export const PostalCodeInput = (props: ComponentProps<'input'>) => (
<FormInput form="postalCode" id="postalCode" type="text" placeholder="00000-000" {...props} />
<FormInput
name="postalCode"
id="postalCode"
type="text"
placeholder="00000-000"
pattern="^(?:\d{8})?$"
{...props}
/>
)

View file

@ -6,7 +6,7 @@ interface StateSelectProps {
placeholder?: string
}
export const StateSelect = ({ placeholder = 'Selecione um estado' }) => {
export const StateSelect = ({ placeholder = 'Selecione um estado' }: StateSelectProps) => {
const { register } = useFormContext()
return (

View file

@ -11,7 +11,7 @@ import { Textarea } from '@/components/ui/textarea'
import { WhatsAppFormInput, whatsAppFormSchema } from '@/lib/schemas/whatsapp-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'
import { useTransition } from 'react'
import { useEffect, useTransition } from 'react'
import { submitWhatsapp } from '@/app/(frontend)/actions/submitWhatsapp'
interface WhatsappFormProps {
@ -40,7 +40,8 @@ export const WhatsappForm = ({ classname, onSubmit }: WhatsappFormProps) => {
await submitWhatsapp(input)
methods.reset()
onSubmit()
} catch {
} catch (e) {
console.log(e)
methods.setError('root', {
message: 'Falha ao enviar assinatura. Tente novamente.',
})
@ -78,11 +79,15 @@ export const WhatsappForm = ({ classname, onSubmit }: WhatsappFormProps) => {
id="comment"
{...methods.register('comment')}
placeholder='Campo de Interesse: "O que você mais quer ver no canal?" (Votações, Bastidores, Ações Sociais, Agenda).'
required
className="min-h-22"
/>
</Field>
<Field>
{methods.formState.errors.root?.message ? (
<FieldDescription className="text-destructive">
{methods.formState.errors.root.message}
</FieldDescription>
) : null}
<Button
type="submit"
disabled={isSubmitting}

View file

@ -1,8 +1,7 @@
import * as React from 'react'
import { cn } from '@/lib/utils'
import { ComponentProps } from 'react'
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
function Input({ className, type, ...props }: ComponentProps<'input'>) {
return (
<input
type={type}