import { useForm, Controller, useWatch, type Control } from 'react-hook-form' import { PatternFormat } from 'react-number-format' import { zodResolver } from '@hookform/resolvers/zod' import { ErrorMessage } from '@hookform/error-message' import { z } from 'zod' import { ArrowRightIcon, CircleQuestionMarkIcon } from 'lucide-react' import valid from 'card-validator' import { Button } from '@repo/ui/components/ui/button' import { Kbd } from '@repo/ui/components/ui/kbd' import { Label } from '@repo/ui/components/ui/label' import { RadioGroup, RadioGroupItem } from '@repo/ui/components/ui/radio-group' import { Separator } from '@repo/ui/components/ui/separator' import { Input } from '@repo/ui/components/ui/input' import { Card, CardContent } from '@repo/ui/components/ui/card' import { HoverCard, HoverCardContent, HoverCardTrigger } from '@repo/ui/components/ui/hover-card' import { Field, FieldError, FieldGroup, FieldLabel, FieldSet } from '@repo/ui/components/ui/field' import { NativeSelect, NativeSelectOption } from '@repo/ui/components/ui/native-select' import { Currency } from '@repo/ui/components/currency' import { useWizard } from '@/components/wizard' import { isName } from '../_.$orgid.users.add/data' import type { PaymentMethod } from '@repo/ui/routes/orders/data' import type { WizardState } from './route' import { applyDiscount } from './discount' const creditCard = z.object({ holder_name: z .string() .trim() .nonempty('Digite um nome') .refine(isName, { message: 'Nome inválido' }), number: z.string().refine( (value) => { const numberValidation = valid.number(value) return numberValidation.isValid }, { error: 'Número do cartão inválido' } ), exp_month: z.string().min(2), exp_year: z.string().min(4), cvv: z.string().min(3).max(4) }) const formSchema = z.discriminatedUnion('payment_method', [ z.object({ payment_method: z.literal('PIX') }), z.object({ payment_method: z.literal('BANK_SLIP') }), z.object({ payment_method: z.literal('MANUAL') }), z.object({ payment_method: z.literal('CREDIT_CARD'), credit_card: creditCard, installments: z.coerce.number().int().min(1).max(12) }) ]) type Schema = z.input export type CreditCard = z.infer type PaymentProps = { state: WizardState onSubmit: (value: any) => void | Promise payment_method?: PaymentMethod credit_card?: CreditCard } export function Payment({ onSubmit, state, payment_method: paymentMethodInit, credit_card: creditCardInit = undefined }: PaymentProps) { const wizard = useWizard() const { control, handleSubmit } = useForm({ defaultValues: { payment_method: paymentMethodInit, installments: state?.installments ?? 1, credit_card: creditCardInit }, resolver: zodResolver(formSchema) }) const paymentMethod = useWatch({ control, name: 'payment_method' }) const subtotal = state.items.reduce( (acc, { course, quantity }) => acc + (course?.unit_price || 0) * (Number.isFinite(quantity) && quantity > 0 ? quantity : 1), 0 ) const discount = state.coupon ? applyDiscount(subtotal, state.coupon.amount, state.coupon.type) * -1 : 0 const total = subtotal > 0 ? subtotal + discount : 0 const onSubmit_ = async (data: Schema) => { await onSubmit({ credit_card: undefined, ...data }) wizard('review') } return (
(
(

{message}

)} />
)} /> {paymentMethod === 'CREDIT_CARD' ? ( ) : null}
) } export function CreditCard({ total, control }: { total: number control: Control }) { const currentYear = new Date().getFullYear() const years = Array.from({ length: 10 }, (_, i) => currentYear + i) return (
{/* Credir card number */} ( Número do cartão { onChange(value) }} {...field} /> {fieldState.invalid && ( )} )} /> {/* Holder name */} ( Nome do titular {fieldState.invalid && ( )} )} />
( Mês Selecione {Array.from({ length: 12 }, (_, i) => { const v = String(i + 1).padStart(2, '0') return ( {v} ) })} )} /> ( Ano Selecione {years.map((year) => ( {year} ))} )} /> ( CVC

O CVC é o código de segurança do cartão de crédito.

Ele fica no verso do cartão e geralmente possui{' '} 3 dígitos (ou 4 dígitos na frente, no caso do American Express).

)} />
( Parcelas {Array.from({ length: 12 }, (_, index) => { const installment = index + 1 // 1 -> 12 if (installment === 1) { return ( {total} à vista ) } const value = calcInterest(total, installment) / installment return ( {installment}x {value} com juros ) })} )} />
) } export const calcInterest = (total: number, installment: number) => { const rate2to6 = 0.055 const rate7to12 = 0.0608 const rate = installment >= 7 ? rate7to12 : rate2to6 return total * ((1 - 0.0382) / (1 - rate)) }