add dialog

This commit is contained in:
2025-12-27 20:48:04 -03:00
parent 976a7da0a9
commit 8b81d5c245
2 changed files with 357 additions and 161 deletions

View File

@@ -176,19 +176,13 @@ export function CreditCard({ control }: { control: Control<Schema> }) {
<CardContent> <CardContent>
<FieldGroup> <FieldGroup>
<FieldSet> <FieldSet>
<FieldGroup>
{/* Credir card number */} {/* Credir card number */}
<Controller <Controller
control={control} control={control}
name="credit_card.number" name="credit_card.number"
render={({ render={({ field: { onChange, ref, ...field }, fieldState }) => (
field: { onChange, ref, ...field },
fieldState
}) => (
<Field data-invalid={fieldState.invalid}> <Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name}> <FieldLabel htmlFor={field.name}>Número do cartão</FieldLabel>
Número do cartão
</FieldLabel>
<PatternFormat <PatternFormat
id={field.name} id={field.name}
format="#### #### #### ####" format="#### #### #### ####"
@@ -216,9 +210,7 @@ export function CreditCard({ control }: { control: Control<Schema> }) {
defaultValue="" defaultValue=""
render={({ field, fieldState }) => ( render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}> <Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name}> <FieldLabel htmlFor={field.name}>Nome do titular</FieldLabel>
Nome do titular
</FieldLabel>
<Input <Input
id={field.name} id={field.name}
aria-invalid={fieldState.invalid} aria-invalid={fieldState.invalid}
@@ -231,7 +223,7 @@ export function CreditCard({ control }: { control: Control<Schema> }) {
)} )}
/> />
<div className="grid grid-cols-3 gap-4"> <FieldSet className="grid grid-cols-3 gap-4">
<Controller <Controller
control={control} control={control}
name="credit_card.exp_month" name="credit_card.exp_month"
@@ -328,8 +320,7 @@ export function CreditCard({ control }: { control: Control<Schema> }) {
</Field> </Field>
)} )}
/> />
</div> </FieldSet>
</FieldGroup>
</FieldSet> </FieldSet>
</FieldGroup> </FieldGroup>
</CardContent> </CardContent>

View File

@@ -1,6 +1,9 @@
import { useToggle } from 'ahooks' import { useToggle } from 'ahooks'
import { PencilIcon } from 'lucide-react' import { PatternFormat } from 'react-number-format'
import { useForm } from 'react-hook-form' import { ExternalLinkIcon, PencilIcon, SearchIcon } from 'lucide-react'
import { Controller, useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import valid from 'card-validator' import valid from 'card-validator'
import { Currency } from '@repo/ui/components/currency' import { Currency } from '@repo/ui/components/currency'
@@ -39,6 +42,21 @@ import { useWizard } from '@/components/wizard'
import { type WizardState } from './route' import { type WizardState } from './route'
import { applyDiscount } from './discount' import { applyDiscount } from './discount'
import { paymentMethods } from '@repo/ui/routes/orders/data' import { paymentMethods } from '@repo/ui/routes/orders/data'
import {
Field,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
FieldSet
} from '@repo/ui/components/ui/field'
import { Input } from '@repo/ui/components/ui/input'
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput
} from '@repo/ui/components/ui/input-group'
type ReviewProps = { type ReviewProps = {
state: WizardState state: WizardState
@@ -64,7 +82,7 @@ export function Review({ state, onSubmit }: ReviewProps) {
return ( return (
<> <>
<Address {...state} /> <Address total={total} {...state} />
<form <form
onSubmit={async (e) => { onSubmit={async (e) => {
@@ -150,9 +168,12 @@ export function Review({ state, onSubmit }: ReviewProps) {
) )
} }
export function Address({ payment_method, credit_card }: WizardState) { export function Address({
total,
payment_method,
credit_card
}: WizardState & { total: number }) {
const numberValidation = valid.number(credit_card?.number) const numberValidation = valid.number(credit_card?.number)
// console.log(numberValidation)
return ( return (
<ItemGroup className="grid lg:grid-cols-2 gap-4"> <ItemGroup className="grid lg:grid-cols-2 gap-4">
@@ -170,7 +191,7 @@ export function Address({ payment_method, credit_card }: WizardState) {
</ul> </ul>
</ItemContent> </ItemContent>
<ItemActions> <ItemActions>
<DialogDemo /> <AddressDialog />
</ItemActions> </ItemActions>
</Item> </Item>
@@ -178,14 +199,20 @@ export function Address({ payment_method, credit_card }: WizardState) {
<ItemContent> <ItemContent>
<ItemTitle>Forma de pagamento</ItemTitle> <ItemTitle>Forma de pagamento</ItemTitle>
<ItemDescription> <ItemDescription>
{payment_method ? paymentMethods[payment_method] : payment_method}
{credit_card ? ( {credit_card ? (
<> <>
<br /> {numberValidation.card?.niceType} (Crédito) ****{' '}
{numberValidation.card?.niceType} ****{' '}
{credit_card.number.slice(-4)} {credit_card.number.slice(-4)}
<br />
1x <Currency>{total}</Currency>
</> </>
) : null} ) : (
<>
{payment_method
? paymentMethods[payment_method]
: payment_method}
</>
)}
</ItemDescription> </ItemDescription>
</ItemContent> </ItemContent>
</Item> </Item>
@@ -193,14 +220,27 @@ export function Address({ payment_method, credit_card }: WizardState) {
) )
} }
export function DialogDemo() { const formSchema = z.object({
const form = useForm() postcode: z.string().min(1, 'Digite um CEP'),
const { handleSubmit } = form address1: z.string().min(1, 'Digite um endereço'),
address2: z.string().optional(),
neighborhood: z.string().min(1, 'Digite o bairro'),
city: z.string().min(1, 'Digite a cidade'),
state: z.string().min(1, 'Digite o estado')
})
const onSubmit = async () => {} type Schema = z.infer<typeof formSchema>
export function AddressDialog() {
const { handleSubmit, control } = useForm({
resolver: zodResolver(formSchema)
})
const onSubmit = async (data: Schema) => {
console.log(data)
}
return ( return (
<form onSubmit={handleSubmit(onSubmit)}>
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button <Button
@@ -214,6 +254,7 @@ export function DialogDemo() {
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="sm:max-w-[425px]"> <DialogContent className="sm:max-w-[425px]">
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<DialogHeader> <DialogHeader>
<DialogTitle>Editar endereço</DialogTitle> <DialogTitle>Editar endereço</DialogTitle>
<DialogDescription> <DialogDescription>
@@ -221,6 +262,170 @@ export function DialogDemo() {
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<FieldGroup>
<FieldSet>
<Controller
control={control}
name="postcode"
defaultValue=""
render={({
field: { onChange, ref, ...field },
fieldState
}) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name}>CEP</FieldLabel>
<InputGroup>
{/*<InputGroupInput
id={field.name}
aria-invalid={fieldState.invalid}
{...field}
/>*/}
<PatternFormat
id={field.name}
format="#####-###"
mask="_"
placeholder="_____-___"
customInput={InputGroupInput}
getInputRef={ref}
aria-invalid={fieldState.invalid}
onValueChange={({ value }) => {
onChange(value)
}}
{...field}
/>
<InputGroupAddon align="inline-end">
<InputGroupButton type="button" variant="secondary">
<SearchIcon />
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
<FieldDescription>
<a
href="https://buscacepinter.correios.com.br/app/endereco/index.php"
target="_blank"
rel="noopener noreferrer"
className="inline-flex gap-1 items-center "
>
Não sei o CEP <ExternalLinkIcon className="size-4" />
</a>
</FieldDescription>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
<Controller
control={control}
name="address1"
defaultValue=""
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name}>Endereço</FieldLabel>
<Input
id={field.name}
aria-invalid={fieldState.invalid}
{...field}
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
<Controller
control={control}
name="address2"
defaultValue=""
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name}>
Complemento{' '}
<span className="text-muted-foreground">(opcional)</span>
</FieldLabel>
<Input
id={field.name}
aria-invalid={fieldState.invalid}
{...field}
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
</FieldSet>
<FieldSet className="grid grid-cols-3">
{/* Neighborhood */}
<Controller
control={control}
name="neighborhood"
defaultValue=""
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name}>Bairro</FieldLabel>
<Input
id={field.name}
aria-invalid={fieldState.invalid}
{...field}
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
{/* City */}
<Controller
control={control}
name="city"
defaultValue=""
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name}>Cidade</FieldLabel>
<Input
id={field.name}
aria-invalid={fieldState.invalid}
{...field}
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
{/* State */}
<Controller
control={control}
name="state"
defaultValue=""
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name}>Estado</FieldLabel>
<Input
id={field.name}
aria-invalid={fieldState.invalid}
{...field}
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
</FieldSet>
</FieldGroup>
<DialogFooter className="*:cursor-pointer"> <DialogFooter className="*:cursor-pointer">
<DialogClose asChild> <DialogClose asChild>
<Button <Button
@@ -234,8 +439,8 @@ export function DialogDemo() {
<Button type="submit">Atualizar endereço</Button> <Button type="submit">Atualizar endereço</Button>
</DialogFooter> </DialogFooter>
</form>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
</form>
) )
} }