- 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.
102 lines
3.1 KiB
TypeScript
102 lines
3.1 KiB
TypeScript
import { useWalletMovements } from "@/lib/queries";
|
|
import type { Wallet } from "@/lib/api";
|
|
import {
|
|
ResponsiveDialog,
|
|
ResponsiveDialogContent,
|
|
ResponsiveDialogHeader,
|
|
ResponsiveDialogTitle,
|
|
} from "@/components/ui/responsive-dialog";
|
|
|
|
const amountFormatter = new Intl.NumberFormat("es-AR", {
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 4,
|
|
});
|
|
|
|
const dateFormatter = new Intl.DateTimeFormat("es-AR", {
|
|
day: "2-digit",
|
|
month: "2-digit",
|
|
year: "numeric",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
});
|
|
|
|
const typeLabels: Record<string, string> = {
|
|
DEPOSIT: "Depósito",
|
|
WITHDRAWAL: "Extracción",
|
|
TRANSFER_IN: "Transferencia recibida",
|
|
TRANSFER_OUT: "Transferencia enviada",
|
|
ADJUSTMENT: "Ajuste",
|
|
};
|
|
|
|
interface WalletMovementsDialogProps {
|
|
wallet: Wallet;
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
}
|
|
|
|
export function WalletMovementsDialog({
|
|
wallet,
|
|
open,
|
|
onOpenChange,
|
|
}: WalletMovementsDialogProps) {
|
|
const { data: movements, isLoading } = useWalletMovements(
|
|
open ? wallet.id : 0,
|
|
);
|
|
|
|
return (
|
|
<ResponsiveDialog open={open} onOpenChange={onOpenChange}>
|
|
<ResponsiveDialogContent>
|
|
<ResponsiveDialogHeader>
|
|
<ResponsiveDialogTitle>
|
|
Movimientos de {wallet.name}
|
|
</ResponsiveDialogTitle>
|
|
</ResponsiveDialogHeader>
|
|
|
|
{isLoading ? (
|
|
<p className="text-sm text-muted-foreground">Cargando...</p>
|
|
) : movements && movements.length > 0 ? (
|
|
<div className="max-h-96 space-y-1 overflow-y-auto">
|
|
{movements.map((m) => (
|
|
<div
|
|
key={m.id}
|
|
className="flex items-center justify-between rounded-md p-2 text-sm hover:bg-muted/50"
|
|
>
|
|
<div className="flex flex-col">
|
|
<span className="font-medium">{typeLabels[m.type] ?? m.type}</span>
|
|
{m.description && (
|
|
<span className="text-xs text-muted-foreground">
|
|
{m.description}
|
|
</span>
|
|
)}
|
|
{m.transferGroupId && (
|
|
<span className="text-xs text-muted-foreground font-mono">
|
|
ID: {m.transferGroupId.slice(0, 8)}...
|
|
</span>
|
|
)}
|
|
<span className="text-xs text-muted-foreground">
|
|
{dateFormatter.format(new Date(m.createdAt))}
|
|
</span>
|
|
</div>
|
|
<span
|
|
className={`tabular-nums font-medium ${
|
|
m.amount >= 0
|
|
? "text-emerald-600 dark:text-emerald-400"
|
|
: "text-red-600 dark:text-red-400"
|
|
}`}
|
|
>
|
|
{m.amount >= 0 ? "+" : ""}
|
|
{amountFormatter.format(m.amount)}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-sm text-muted-foreground">
|
|
Sin movimientos registrados.
|
|
</p>
|
|
)}
|
|
</ResponsiveDialogContent>
|
|
</ResponsiveDialog>
|
|
);
|
|
}
|