From c5b33c007fc865982af2a31bff59fda9b8fb49a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Garc=C3=ADa=20M=C3=A9ndez?= Date: Mon, 16 Feb 2026 21:46:49 -0500 Subject: [PATCH] Show deposit options from backend with grouped categories Replace hardcoded wallet display with dynamic deposit options from API. New DepositOptionCard and DepositOptionsList components with copy-to-clipboard, grouped by category (bank, crypto, lemon, SWIFT, cash). DepositForm derives methods dynamically from active options. Full i18n support (ES/EN). --- .../features/deposits/DepositOptionCard.jsx | 65 +++++++++++++++++++ .../deposits/DepositOptionCard.test.jsx | 56 ++++++++++++++++ .../features/deposits/DepositOptionsList.jsx | 46 +++++++++++++ .../deposits/DepositOptionsList.test.jsx | 57 ++++++++++++++++ .../features/requests/DepositForm.jsx | 54 +++++++++++---- .../features/requests/DepositForm.test.jsx | 50 ++++++++++++-- src/hooks/useDepositOptions.js | 26 ++++++++ src/hooks/useDepositOptions.test.jsx | 64 ++++++++++++++++++ src/i18n.js | 58 +++++++++++++++-- src/pages/WalletsPage.jsx | 12 ++-- src/pages/WalletsPage.test.jsx | 38 ++++++++--- src/services/api.js | 19 +++++- 12 files changed, 507 insertions(+), 38 deletions(-) create mode 100644 src/components/features/deposits/DepositOptionCard.jsx create mode 100644 src/components/features/deposits/DepositOptionCard.test.jsx create mode 100644 src/components/features/deposits/DepositOptionsList.jsx create mode 100644 src/components/features/deposits/DepositOptionsList.test.jsx create mode 100644 src/hooks/useDepositOptions.js create mode 100644 src/hooks/useDepositOptions.test.jsx diff --git a/src/components/features/deposits/DepositOptionCard.jsx b/src/components/features/deposits/DepositOptionCard.jsx new file mode 100644 index 0000000..ffd3801 --- /dev/null +++ b/src/components/features/deposits/DepositOptionCard.jsx @@ -0,0 +1,65 @@ +import { useState } from 'react'; +import { Card } from '../../ui/Card'; +import { Button } from '../../ui/Button'; +import { useTranslation } from 'react-i18next'; + +const COPYABLE_KEYS = [ + 'cbu_cvu', + 'alias', + 'lemon_tag', + 'address', + 'swift_code', + 'account_number', + 'iban', +]; + +export const DepositOptionCard = ({ option }) => { + const [copiedKey, setCopiedKey] = useState(null); + const { t } = useTranslation(); + + const handleCopy = async (value, key) => { + try { + await navigator.clipboard.writeText(value); + setCopiedKey(key); + setTimeout(() => setCopiedKey(null), 2000); + } catch (error) { + console.error('Failed to copy:', error); + } + }; + + const details = option.details || {}; + const detailEntries = Object.entries(details).filter(([, v]) => v); + + return ( + +
+
+

{option.label}

+ + {option.currency} + +
+ +
+ {detailEntries.map(([key, value]) => ( +
+
+

{t(`deposits.detailLabels.${key}`, key)}

+

{value}

+
+ {COPYABLE_KEYS.includes(key) && ( + + )} +
+ ))} +
+
+
+ ); +}; diff --git a/src/components/features/deposits/DepositOptionCard.test.jsx b/src/components/features/deposits/DepositOptionCard.test.jsx new file mode 100644 index 0000000..571c217 --- /dev/null +++ b/src/components/features/deposits/DepositOptionCard.test.jsx @@ -0,0 +1,56 @@ +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { describe, it, expect, vi } from 'vitest'; +import { DepositOptionCard } from './DepositOptionCard'; + +describe('DepositOptionCard', () => { + const bankOption = { + id: '1', + category: 'BANK_ARS', + label: 'Banco Galicia', + currency: 'ARS', + details: { bank_name: 'Galicia', holder: 'Winbit SRL', cbu_cvu: '0070000000' }, + }; + + const cryptoOption = { + id: '2', + category: 'CRYPTO', + label: 'USDT TRC20', + currency: 'USDT', + details: { address: 'TF7j33woKnMVFALtvRVdnFWnneNrUCVvAr', network: 'TRC20' }, + }; + + it('renders bank option details', () => { + render(); + expect(screen.getByText('Banco Galicia')).toBeInTheDocument(); + expect(screen.getByText('ARS')).toBeInTheDocument(); + expect(screen.getByText('Galicia')).toBeInTheDocument(); + expect(screen.getByText('Winbit SRL')).toBeInTheDocument(); + expect(screen.getByText('0070000000')).toBeInTheDocument(); + }); + + it('renders crypto option details', () => { + render(); + expect(screen.getByText('USDT TRC20')).toBeInTheDocument(); + expect(screen.getByText('TF7j33woKnMVFALtvRVdnFWnneNrUCVvAr')).toBeInTheDocument(); + expect(screen.getByText('TRC20')).toBeInTheDocument(); + }); + + it('has copy buttons for copyable fields', () => { + render(); + const copyButtons = screen.getAllByRole('button', { name: /Copiar/i }); + expect(copyButtons.length).toBeGreaterThan(0); + }); + + it('copies text to clipboard on button click', async () => { + const writeText = vi.fn().mockResolvedValue(undefined); + Object.assign(navigator, { clipboard: { writeText } }); + + render(); + const copyButtons = screen.getAllByRole('button', { name: /Copiar/i }); + fireEvent.click(copyButtons[0]); + + await waitFor(() => { + expect(writeText).toHaveBeenCalledWith('0070000000'); + }); + }); +}); diff --git a/src/components/features/deposits/DepositOptionsList.jsx b/src/components/features/deposits/DepositOptionsList.jsx new file mode 100644 index 0000000..4de74f5 --- /dev/null +++ b/src/components/features/deposits/DepositOptionsList.jsx @@ -0,0 +1,46 @@ +import { DepositOptionCard } from './DepositOptionCard'; +import { EmptyState } from '../../ui/EmptyState'; +import { useTranslation } from 'react-i18next'; + +const CATEGORY_ORDER = ['CASH_ARS', 'CASH_USD', 'BANK_ARS', 'LEMON', 'CRYPTO', 'SWIFT']; + +export const DepositOptionsList = ({ options }) => { + const { t } = useTranslation(); + + if (!options || options.length === 0) { + return ( + + ); + } + + const grouped = {}; + for (const opt of options) { + if (!grouped[opt.category]) { + grouped[opt.category] = []; + } + grouped[opt.category].push(opt); + } + + const sortedCategories = CATEGORY_ORDER.filter((cat) => grouped[cat]); + + return ( +
+ {sortedCategories.map((category) => ( +
+

+ {t(`deposits.categories.${category}`)} +

+
+ {grouped[category].map((opt) => ( + + ))} +
+
+ ))} +
+ ); +}; diff --git a/src/components/features/deposits/DepositOptionsList.test.jsx b/src/components/features/deposits/DepositOptionsList.test.jsx new file mode 100644 index 0000000..d24f336 --- /dev/null +++ b/src/components/features/deposits/DepositOptionsList.test.jsx @@ -0,0 +1,57 @@ +import { render, screen } from '@testing-library/react'; +import { describe, it, expect } from 'vitest'; +import { DepositOptionsList } from './DepositOptionsList'; + +describe('DepositOptionsList', () => { + const mockOptions = [ + { + id: '1', + category: 'BANK_ARS', + label: 'Banco Galicia', + currency: 'ARS', + details: { bank_name: 'Galicia', holder: 'Winbit SRL', cbu_cvu: '0070000' }, + position: 1, + }, + { + id: '2', + category: 'CRYPTO', + label: 'USDT TRC20', + currency: 'USDT', + details: { address: 'TF7j33wo', network: 'TRC20' }, + position: 2, + }, + { + id: '3', + category: 'LEMON', + label: 'Lemon Cash', + currency: 'ARS', + details: { lemon_tag: '$winbit' }, + position: 3, + }, + ]; + + it('renders options grouped by category', () => { + render(); + + expect(screen.getByText('Transferencia bancaria ARS')).toBeInTheDocument(); + expect(screen.getAllByText('Lemon Cash').length).toBeGreaterThan(0); + expect(screen.getByText('Cripto')).toBeInTheDocument(); + }); + + it('renders all option labels', () => { + render(); + + expect(screen.getByText('Banco Galicia')).toBeInTheDocument(); + expect(screen.getByText('USDT TRC20')).toBeInTheDocument(); + }); + + it('renders empty state when no options', () => { + render(); + expect(screen.getByText(/No hay opciones disponibles/)).toBeInTheDocument(); + }); + + it('renders empty state when options is null', () => { + render(); + expect(screen.getByText(/No hay opciones disponibles/)).toBeInTheDocument(); + }); +}); diff --git a/src/components/features/requests/DepositForm.jsx b/src/components/features/requests/DepositForm.jsx index 9cdc12b..c34fd7a 100644 --- a/src/components/features/requests/DepositForm.jsx +++ b/src/components/features/requests/DepositForm.jsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useMemo } from 'react'; import { Card } from '../../ui/Card'; import { Input } from '../../ui/Input'; import { Select } from '../../ui/Select'; @@ -9,25 +9,57 @@ import { useTranslation } from 'react-i18next'; const CASH_METHODS = ['CASH_ARS', 'CASH_USD']; -export const DepositForm = ({ userEmail }) => { +const FALLBACK_METHODS = [ + { value: 'CASH_ARS', labelKey: 'requests.method.cash_ars' }, + { value: 'CASH_USD', labelKey: 'requests.method.cash_usd' }, + { value: 'TRANSFER_ARS', labelKey: 'requests.method.transfer_ars' }, + { value: 'SWIFT', labelKey: 'requests.method.swift' }, + { value: 'CRYPTO', labelKey: 'requests.method.crypto' }, +]; + +const CATEGORY_TO_METHOD = { + CASH_ARS: 'CASH_ARS', + CASH_USD: 'CASH_USD', + BANK_ARS: 'TRANSFER_ARS', + LEMON: 'LEMON', + CRYPTO: 'CRYPTO', + SWIFT: 'SWIFT', +}; + +export const DepositForm = ({ userEmail, depositOptions = [] }) => { const { t } = useTranslation(); + + const methodOptions = useMemo(() => { + if (!depositOptions || depositOptions.length === 0) { + return FALLBACK_METHODS.map((m) => ({ value: m.value, label: t(m.labelKey) })); + } + + const seen = new Set(); + const methods = []; + for (const opt of depositOptions) { + const method = CATEGORY_TO_METHOD[opt.category] || opt.category; + if (!seen.has(method)) { + seen.add(method); + methods.push({ + value: method, + label: t(`deposits.categories.${opt.category}`, opt.category), + }); + } + } + return methods; + }, [depositOptions, t]); + + const defaultMethod = methodOptions[0]?.value || 'CASH_ARS'; + const [formData, setFormData] = useState({ amount: '', - method: 'CASH_ARS', + method: defaultMethod, }); const [attachment, setAttachment] = useState(null); const [loading, setLoading] = useState(false); const [message, setMessage] = useState(null); const [modal, setModal] = useState(null); - const methodOptions = [ - { value: 'CASH_ARS', label: t('requests.method.cash_ars') }, - { value: 'CASH_USD', label: t('requests.method.cash_usd') }, - { value: 'TRANSFER_ARS', label: t('requests.method.transfer_ars') }, - { value: 'SWIFT', label: t('requests.method.swift') }, - { value: 'CRYPTO', label: t('requests.method.crypto') }, - ]; - const isCash = CASH_METHODS.includes(formData.method); const attachmentRequired = !isCash; diff --git a/src/components/features/requests/DepositForm.test.jsx b/src/components/features/requests/DepositForm.test.jsx index c40843d..b0ec172 100644 --- a/src/components/features/requests/DepositForm.test.jsx +++ b/src/components/features/requests/DepositForm.test.jsx @@ -8,12 +8,30 @@ vi.mock('../../../services/api', () => ({ createInvestorRequest: vi.fn(), })); +const mockDepositOptions = [ + { id: '1', category: 'CASH_ARS', label: 'Efectivo', currency: 'ARS', details: {} }, + { + id: '2', + category: 'BANK_ARS', + label: 'Galicia', + currency: 'ARS', + details: { bank_name: 'Galicia', holder: 'Winbit', cbu_cvu: '007' }, + }, + { + id: '3', + category: 'CRYPTO', + label: 'USDT TRC20', + currency: 'USDT', + details: { address: 'TF7j', network: 'TRC20' }, + }, +]; + describe('DepositForm', () => { it('renders English strings when language is en', async () => { await act(async () => { await i18n.changeLanguage('en'); }); - render(); + render(); expect(screen.getByText('Register deposit')).toBeInTheDocument(); @@ -30,7 +48,9 @@ describe('DepositForm', () => { await act(async () => { await i18n.changeLanguage('es'); }); - const { container } = render(); + const { container } = render( + , + ); fireEvent.submit(container.querySelector('form')); expect(await screen.findByRole('alert')).toHaveTextContent('Ingresá un monto válido'); }); @@ -40,7 +60,9 @@ describe('DepositForm', () => { await i18n.changeLanguage('es'); }); createInvestorRequest.mockResolvedValueOnce({ data: { id: 1 }, error: null }); - const { container } = render(); + const { container } = render( + , + ); fireEvent.change(screen.getByLabelText(/Monto/), { target: { value: '10' } }); @@ -60,19 +82,35 @@ describe('DepositForm', () => { ); }); - // Modal should appear with success message expect(await screen.findByText('Solicitud registrada')).toBeInTheDocument(); - // Modal should have an "Aceptar" button expect(screen.getByRole('button', { name: /Aceptar/i })).toBeInTheDocument(); }); it('shows service error', async () => { createInvestorRequest.mockResolvedValueOnce({ data: null, error: 'Fail' }); - const { container } = render(); + const { container } = render( + , + ); fireEvent.change(screen.getByLabelText(/Monto/), { target: { value: '10' } }); fireEvent.submit(container.querySelector('form')); expect(await screen.findByRole('alert')).toHaveTextContent('Fail'); }); + + it('derives method options from deposit options', () => { + render(); + + const selectButton = screen.getByLabelText(/Método/); + expect(selectButton).toBeInTheDocument(); + expect(selectButton.textContent).toContain('Efectivo ARS'); + }); + + it('falls back to hardcoded methods when no deposit options', () => { + render(); + + const selectButton = screen.getByLabelText(/Método/); + expect(selectButton).toBeInTheDocument(); + expect(selectButton.textContent).toContain('Efectivo ARS'); + }); }); diff --git a/src/hooks/useDepositOptions.js b/src/hooks/useDepositOptions.js new file mode 100644 index 0000000..dd6b058 --- /dev/null +++ b/src/hooks/useDepositOptions.js @@ -0,0 +1,26 @@ +import { useQuery } from '@tanstack/react-query'; +import { getDepositOptions } from '../services/api'; + +/** + * Hook para obtener las opciones de depósito activas desde el backend + * @returns {Object} { depositOptions, loading, error } + */ +export const useDepositOptions = () => { + const { data, isLoading, error } = useQuery({ + queryKey: ['depositOptions'], + queryFn: async () => { + const result = await getDepositOptions(); + if (result.error) { + throw new Error(result.error); + } + return result.data || []; + }, + staleTime: 1000 * 60 * 60, + }); + + return { + depositOptions: data || [], + loading: isLoading, + error: error?.message || null, + }; +}; diff --git a/src/hooks/useDepositOptions.test.jsx b/src/hooks/useDepositOptions.test.jsx new file mode 100644 index 0000000..03e71ca --- /dev/null +++ b/src/hooks/useDepositOptions.test.jsx @@ -0,0 +1,64 @@ +import { renderHook, waitFor } from '@testing-library/react'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { useDepositOptions } from './useDepositOptions'; +import { getDepositOptions } from '../services/api'; + +vi.mock('../services/api', () => ({ + getDepositOptions: vi.fn(), +})); + +const createWrapper = () => { + const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }); + const Wrapper = ({ children }) => ( + {children} + ); + Wrapper.displayName = 'QueryWrapper'; + return Wrapper; +}; + +describe('useDepositOptions', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('returns deposit options on success', async () => { + const mockData = [ + { id: '1', category: 'BANK_ARS', label: 'Galicia', currency: 'ARS', details: {} }, + ]; + getDepositOptions.mockResolvedValue({ data: mockData, error: null }); + + const { result } = renderHook(() => useDepositOptions(), { wrapper: createWrapper() }); + + await waitFor(() => { + expect(result.current.loading).toBe(false); + }); + + expect(result.current.depositOptions).toEqual(mockData); + expect(result.current.error).toBeNull(); + }); + + it('returns error on failure', async () => { + getDepositOptions.mockResolvedValue({ data: null, error: 'Network error' }); + + const { result } = renderHook(() => useDepositOptions(), { wrapper: createWrapper() }); + + await waitFor(() => { + expect(result.current.loading).toBe(false); + }); + + expect(result.current.error).toBe('Network error'); + expect(result.current.depositOptions).toEqual([]); + }); + + it('starts in loading state', () => { + getDepositOptions.mockImplementation(() => new Promise(() => {})); + + const { result } = renderHook(() => useDepositOptions(), { wrapper: createWrapper() }); + + expect(result.current.loading).toBe(true); + expect(result.current.depositOptions).toEqual([]); + }); +}); diff --git a/src/i18n.js b/src/i18n.js index 9595f6f..9873ad0 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -115,12 +115,36 @@ const resources = { }, deposits: { title: 'Depósitos', - subtitle: 'Usá estas direcciones para depositar fondos en tu portafolio', + subtitle: 'Consultá las opciones disponibles para depositar fondos en tu portafolio', warningTitle: 'Importante:', warningText: - 'Verificá siempre la red antes de enviar fondos. Enviar a una red incorrecta puede resultar en pérdida de fondos.', + 'Verificá siempre los datos antes de enviar fondos. Enviar a una dirección o cuenta incorrecta puede resultar en pérdida de fondos.', noWalletsTitle: 'No hay wallets disponibles', noWalletsMessage: 'No hay direcciones de depósito configuradas todavía.', + noOptionsTitle: 'No hay opciones disponibles', + noOptionsMessage: 'No hay opciones de depósito configuradas todavía.', + categories: { + CASH_ARS: 'Efectivo ARS', + CASH_USD: 'Efectivo USD', + BANK_ARS: 'Transferencia bancaria ARS', + LEMON: 'Lemon Cash', + CRYPTO: 'Cripto', + SWIFT: 'Transferencia internacional', + }, + detailLabels: { + bank_name: 'Banco', + holder: 'Titular', + cbu_cvu: 'CBU / CVU', + alias: 'Alias', + lemon_tag: 'Lemon Tag', + address: 'Dirección', + network: 'Red', + swift_code: 'Código SWIFT', + account_number: 'Nº de cuenta', + routing_number: 'Routing number', + bank_address: 'Dirección del banco', + instructions: 'Instrucciones', + }, copy: 'Copiar', copied: '✓ ¡Copiado!', processingHoursTitle: '⏰ Horario de procesamiento:', @@ -193,6 +217,7 @@ const resources = { cash_ars: 'Efectivo ARS', cash_usd: 'Efectivo USD', transfer_ars: 'Transferencia ARS', + lemon: 'Lemon Cash', swift: 'Transferencia SWIFT', crypto: 'Cripto USDT/USDC', }, @@ -370,12 +395,36 @@ const resources = { }, deposits: { title: 'Deposits', - subtitle: 'Use these addresses to deposit funds to your portfolio', + subtitle: 'Check the available options to deposit funds to your portfolio', warningTitle: 'Important:', warningText: - 'Always verify the network before sending funds. Sending to the wrong network may result in loss of funds.', + 'Always verify the details before sending funds. Sending to the wrong address or account may result in loss of funds.', noWalletsTitle: 'No wallets available', noWalletsMessage: 'No deposit addresses have been configured yet.', + noOptionsTitle: 'No options available', + noOptionsMessage: 'No deposit options have been configured yet.', + categories: { + CASH_ARS: 'Cash ARS', + CASH_USD: 'Cash USD', + BANK_ARS: 'Bank transfer ARS', + LEMON: 'Lemon Cash', + CRYPTO: 'Crypto', + SWIFT: 'International transfer', + }, + detailLabels: { + bank_name: 'Bank', + holder: 'Account holder', + cbu_cvu: 'CBU / CVU', + alias: 'Alias', + lemon_tag: 'Lemon Tag', + address: 'Address', + network: 'Network', + swift_code: 'SWIFT code', + account_number: 'Account number', + routing_number: 'Routing number', + bank_address: 'Bank address', + instructions: 'Instructions', + }, copy: 'Copy', copied: '✓ Copied!', processingHoursTitle: '⏰ Processing hours:', @@ -443,6 +492,7 @@ const resources = { cash_ars: 'Cash ARS', cash_usd: 'Cash USD', transfer_ars: 'Bank transfer ARS', + lemon: 'Lemon Cash', swift: 'SWIFT transfer', crypto: 'Crypto USDT/USDC', }, diff --git a/src/pages/WalletsPage.jsx b/src/pages/WalletsPage.jsx index cb45586..b2bbc69 100644 --- a/src/pages/WalletsPage.jsx +++ b/src/pages/WalletsPage.jsx @@ -1,14 +1,14 @@ -import { WalletList } from '../components/features/wallets/WalletList'; +import { DepositOptionsList } from '../components/features/deposits/DepositOptionsList'; import { useTranslation } from 'react-i18next'; import { useAuth } from '../hooks/useAuth'; -import { useWallets } from '../hooks/useWallets'; +import { useDepositOptions } from '../hooks/useDepositOptions'; import { DepositForm } from '../components/features/requests/DepositForm'; import { Spinner } from '../components/ui/Spinner'; export const WalletsPage = () => { const { t } = useTranslation(); const { user } = useAuth(); - const { wallets, loading, error } = useWallets(); + const { depositOptions, loading, error } = useDepositOptions(); return (
@@ -32,11 +32,11 @@ export const WalletsPage = () => {
{String(error)}
- ) : wallets.length === 0 ? null : ( - + ) : ( + )} - +
); }; diff --git a/src/pages/WalletsPage.test.jsx b/src/pages/WalletsPage.test.jsx index e5fe12c..33f5f73 100644 --- a/src/pages/WalletsPage.test.jsx +++ b/src/pages/WalletsPage.test.jsx @@ -6,11 +6,25 @@ vi.mock('../hooks/useAuth', () => ({ useAuth: () => ({ user: { email: 'test@example.com', displayName: 'Test' } }), })); -vi.mock('../hooks/useWallets', () => ({ - useWallets: () => ({ - wallets: [ - { network: 'USDT (TRC20)', address: 'abc', icon: '₮' }, - { network: 'USDC (TRC20)', address: 'def', icon: '$' }, +vi.mock('../hooks/useDepositOptions', () => ({ + useDepositOptions: () => ({ + depositOptions: [ + { + id: '1', + category: 'BANK_ARS', + label: 'Banco Galicia', + currency: 'ARS', + details: { bank_name: 'Galicia', holder: 'Winbit SRL', cbu_cvu: '0070000' }, + position: 1, + }, + { + id: '2', + category: 'CRYPTO', + label: 'USDT TRC20', + currency: 'USDT', + details: { address: 'TF7j33wo', network: 'TRC20' }, + position: 2, + }, ], loading: false, error: null, @@ -18,11 +32,17 @@ vi.mock('../hooks/useWallets', () => ({ })); describe('WalletsPage', () => { - it('renders heading and wallet cards', () => { + it('renders heading and deposit option cards', () => { render(); expect(screen.getByText('Depósitos')).toBeInTheDocument(); - expect(screen.getByRole('heading', { name: 'USDT (TRC20)' })).toBeInTheDocument(); - expect(screen.getByRole('heading', { name: 'USDC (TRC20)' })).toBeInTheDocument(); - expect(screen.getByText(/Verificá siempre la red/)).toBeInTheDocument(); + expect(screen.getByText('Banco Galicia')).toBeInTheDocument(); + expect(screen.getByText('USDT TRC20')).toBeInTheDocument(); + expect(screen.getByText(/Verificá siempre los datos/)).toBeInTheDocument(); + }); + + it('renders grouped by category', () => { + render(); + expect(screen.getAllByText('Transferencia bancaria ARS').length).toBeGreaterThan(0); + expect(screen.getAllByText('Cripto').length).toBeGreaterThan(0); }); }); diff --git a/src/services/api.js b/src/services/api.js index fa161a0..65b6af0 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -182,10 +182,25 @@ export const getWallets = async () => { }; /** - * Obtiene los métodos de pago disponibles desde el backend - * @param {string | null} requestType - 'DEPOSIT' o 'WITHDRAWAL' (opcional) + * Obtiene las opciones de depósito activas desde el backend * @returns {Promise<{data: array | null, error: string | null}>} */ +export const getDepositOptions = async () => { + try { + const url = `${API_BASE_URL}/api/public/deposit_options`; + const response = await silentFetch(url); + + if (!response.ok) { + throw new Error(`API Error: ${response.status}`); + } + + const result = await response.json(); + return { data: result.data, error: null }; + } catch (error) { + return { data: null, error: error.message }; + } +}; + /** * Crea una solicitud de depósito o retiro * @param {object} requestData - Datos de la solicitud