- Added createWallet, updateWallet, deleteWallet, depositWallet, transferWallet, and listMovements handlers. - Created corresponding routes for wallet operations. - Developed frontend components for wallet management including dialogs for creating, editing, depositing, adjusting balance, transferring, and viewing movements. - Integrated wallet management into the authenticated routes.
129 lines
3.7 KiB
TypeScript
129 lines
3.7 KiB
TypeScript
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { useForm } from "react-hook-form";
|
|
import { z } from "zod";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
ResponsiveDialog,
|
|
ResponsiveDialogContent,
|
|
ResponsiveDialogFooter,
|
|
ResponsiveDialogHeader,
|
|
ResponsiveDialogTitle,
|
|
} from "@/components/ui/responsive-dialog";
|
|
import { useDepositWallet } from "@/lib/queries";
|
|
import type { Wallet } from "@/lib/api";
|
|
|
|
const depositSchema = z.object({
|
|
amount: z.number().positive("El monto debe ser mayor a 0"),
|
|
description: z.string().max(255).optional(),
|
|
});
|
|
|
|
type DepositFormValues = z.infer<typeof depositSchema>;
|
|
|
|
interface DepositDialogProps {
|
|
wallet: Wallet;
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
}
|
|
|
|
export function DepositDialog({
|
|
wallet,
|
|
open,
|
|
onOpenChange,
|
|
}: DepositDialogProps) {
|
|
const deposit = useDepositWallet();
|
|
|
|
const {
|
|
register,
|
|
handleSubmit,
|
|
formState: { errors, isSubmitting },
|
|
reset,
|
|
} = useForm<DepositFormValues>({
|
|
resolver: zodResolver(depositSchema),
|
|
defaultValues: { amount: 0, description: "" },
|
|
});
|
|
|
|
function handleClose(open: boolean) {
|
|
onOpenChange(open);
|
|
if (!open) reset();
|
|
}
|
|
|
|
async function onSubmit(data: DepositFormValues) {
|
|
await deposit.mutateAsync({
|
|
id: wallet.id,
|
|
data: {
|
|
amount: data.amount,
|
|
description: data.description || undefined,
|
|
},
|
|
});
|
|
handleClose(false);
|
|
}
|
|
|
|
return (
|
|
<ResponsiveDialog open={open} onOpenChange={handleClose}>
|
|
<ResponsiveDialogContent>
|
|
<ResponsiveDialogHeader>
|
|
<ResponsiveDialogTitle>
|
|
Depositar en {wallet.name}
|
|
</ResponsiveDialogTitle>
|
|
</ResponsiveDialogHeader>
|
|
|
|
<form
|
|
id="deposit-form"
|
|
onSubmit={handleSubmit(onSubmit)}
|
|
className="flex flex-col gap-4"
|
|
>
|
|
<div className="flex flex-col gap-1.5">
|
|
<label htmlFor="deposit-amount" className="text-sm font-medium">
|
|
Monto
|
|
</label>
|
|
<input
|
|
id="deposit-amount"
|
|
type="number"
|
|
step="0.01"
|
|
min="0"
|
|
{...register("amount", { valueAsNumber: true })}
|
|
placeholder="1000"
|
|
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.amount && (
|
|
<span className="text-xs text-destructive">
|
|
{errors.amount.message}
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-1.5">
|
|
<label htmlFor="deposit-desc" className="text-sm font-medium">
|
|
Descripción (opcional)
|
|
</label>
|
|
<input
|
|
id="deposit-desc"
|
|
{...register("description")}
|
|
placeholder="Ej: Sueldo"
|
|
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.description && (
|
|
<span className="text-xs text-destructive">
|
|
{errors.description.message}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</form>
|
|
|
|
<ResponsiveDialogFooter>
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => handleClose(false)}
|
|
disabled={isSubmitting}
|
|
>
|
|
Cancelar
|
|
</Button>
|
|
<Button type="submit" form="deposit-form" disabled={isSubmitting}>
|
|
Depositar
|
|
</Button>
|
|
</ResponsiveDialogFooter>
|
|
</ResponsiveDialogContent>
|
|
</ResponsiveDialog>
|
|
);
|
|
}
|