import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect, useMemo, useRef } from "react"; import { Controller, useForm, useWatch } from "react-hook-form"; import { z } from "zod"; import { Button } from "@/components/ui/button"; import { DatePicker } from "@/components/ui/date-picker"; import { ResponsiveDialog, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, ResponsiveDialogTitle, } from "@/components/ui/responsive-dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import type { Expense } from "@/lib/api"; import { useExpensesContext } from "../ExpensesProvider"; import { useQuotes, useWallets } from "@/lib/queries"; const paySchema = z.object({ amountPayed: z.number().positive("El monto debe ser mayor a 0"), paymentDate: z.date({ message: "La fecha es obligatoria" }), walletId: z .number() .positive("Seleccioná una billetera") .refine((v) => v > 0, "Seleccioná una billetera"), usdcConversionRate: z .number() .positive("La cotización debe ser mayor a 0") .optional(), }); type PayFormValues = z.infer; interface PayExpenseDialogProps { expense: Expense | null; open: boolean; onOpenChange: (open: boolean) => void; } export function PayExpenseDialog({ expense, open, onOpenChange, }: PayExpenseDialogProps) { const { payExpense } = useExpensesContext(); const amountInputRef = useRef(null); const prevWalletRef = useRef(0); const { data: wallets } = useWallets(); const { data: quotes } = useQuotes(); const defaultWallet = useMemo( () => wallets?.find((w) => w.isDefault), [wallets], ); const beloSell = useMemo(() => { if (!quotes) return null; const belo = quotes.find((q) => q.type === "BELO"); return belo ? Number(belo.sell) : null; }, [quotes]); const { register, handleSubmit, control, formState: { errors, isSubmitting }, reset, setValue, trigger, } = useForm({ resolver: zodResolver(paySchema), defaultValues: { amountPayed: 0, paymentDate: undefined, walletId: 0, usdcConversionRate: undefined, }, }); const { ref: amountPayedRef, ...amountPayedField } = register("amountPayed", { setValueAs: (value) => Number(value), }); const usdcConversionRateField = register("usdcConversionRate", { setValueAs: (value) => (value === "" ? undefined : Number(value)), }); const selectedWalletId = useWatch({ control, name: "walletId" }); const selectedWallet = wallets?.find((w) => w.id === selectedWalletId); const isUSDC = selectedWallet?.currency === "USDC"; useEffect(() => { if (expense) { const defaultId = defaultWallet?.id ?? 0; reset({ amountPayed: Number(expense.amount), paymentDate: new Date(), walletId: defaultId, usdcConversionRate: undefined, }); prevWalletRef.current = defaultId; } }, [expense, defaultWallet, reset]); useEffect(() => { if ( isUSDC && beloSell && selectedWalletId && selectedWalletId !== prevWalletRef.current ) { setValue("usdcConversionRate", beloSell); trigger("usdcConversionRate"); prevWalletRef.current = selectedWalletId; } }, [isUSDC, beloSell, selectedWalletId, setValue, trigger]); useEffect(() => { if (!open || !expense) return; const frame = requestAnimationFrame(() => { amountInputRef.current?.focus(); amountInputRef.current?.select(); }); return () => cancelAnimationFrame(frame); }, [open, expense]); function handleClose(open: boolean) { onOpenChange(open); if (!open) reset(); } async function onSubmit(data: PayFormValues) { if (!expense) return; await payExpense(expense.id, { amountPayed: data.amountPayed, paymentDate: data.paymentDate.toISOString(), walletId: data.walletId, usdcConversionRate: isUSDC ? data.usdcConversionRate : undefined, }); handleClose(false); } if (!expense) return null; return ( Registrar pago
{expense.description} Monto original: $ {Number(expense.amount).toLocaleString("es-AR", { minimumFractionDigits: 2, })}
{ amountPayedRef(node); amountInputRef.current = node; }} className="h-8 rounded-lg border border-input bg-background px-2.5 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50" /> {errors.amountPayed && ( {errors.amountPayed.message} )}
( )} /> {errors.walletId && ( {errors.walletId.message} )}
{isUSDC && (
{errors.usdcConversionRate && ( {errors.usdcConversionRate.message} )} Pre-sugerido: {beloSell ?? "—"} ARS/USDC (BELO)
)}
Fecha de pago ( )} /> {errors.paymentDate && ( {errors.paymentDate.message} )}
); }