feat(wallets): implement wallet management features including create, update, delete, deposit, transfer, and list movements
- 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.
This commit is contained in:
128
apps/frontend/src/features/wallets/components/DepositDialog.tsx
Normal file
128
apps/frontend/src/features/wallets/components/DepositDialog.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user