fix(currency): update fields

This commit is contained in:
ghazall-ag
2025-12-30 22:23:55 +03:30
parent ecf6574e5d
commit 964965f0d1
4 changed files with 335 additions and 113 deletions

View File

@@ -22,7 +22,7 @@ const Sidebar = ({ isOpen, onToggle }) => {
{ name: 'Currency', href: '/currency', icon: DollarSign }, { name: 'Currency', href: '/currency', icon: DollarSign },
{ name: 'Location', href: '/location', icon: MapPin }, { name: 'Location', href: '/location', icon: MapPin },
{ name: 'Issuer', href: '/issuer', icon: Building2 }, { name: 'Issuer', href: '/issuer', icon: Building2 },
// { name: 'Settings', href: '/settings', icon: Settings }, { name: 'Settings', href: '/settings', icon: Settings },
]; ];
return ( return (

View File

@@ -1,7 +1,7 @@
import React, { useEffect, useMemo, useState, useCallback } from 'react'; import React, { useEffect, useMemo, useState, useCallback } from 'react';
import DataTable from '../components/DataTable'; import DataTable from '../components/DataTable';
import { currencyAPI } from '../services/api'; import { currencyAPI } from '../services/api';
import { Plus, Search, Power, Wrench, Settings, DollarSign, Shield } from 'lucide-react'; import { Plus, Search, Power, Wrench, Settings, DollarSign, Shield, AlertTriangle, CheckCircle2 } from 'lucide-react';
import { ToastContainer, toast } from 'react-toastify'; import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css'; import 'react-toastify/dist/ReactToastify.css';
import { getErrorMessage, getSuccessMessage } from '../utils/errorHandler'; import { getErrorMessage, getSuccessMessage } from '../utils/errorHandler';
@@ -49,12 +49,10 @@ const Currency = () => {
voucherLimits: { min: null, max: null }, voucherLimits: { min: null, max: null },
}); });
const [feesForm, setFeesForm] = useState({ const [feesForm, setFeesForm] = useState({
depositFee: { percent: 0, fixed: 0, minAmount: null, maxAmount: null }, depositLimits: { min: null, max: null },
cashOutFee: { percent: 0, fixed: 0, minAmount: null, maxAmount: null }, cashOutLimits: { min: null, max: null },
transferFee: { percent: 0, fixed: 0, minAmount: null, maxAmount: null }, transferLimits: { min: null, max: null },
exchangeFee: { percent: 0, fixed: 0, minAmount: null, maxAmount: null }, voucherLimits: { min: null, max: null },
generateVoucherFee: { percent: 0, fixed: 0, minAmount: null, maxAmount: null },
expireVoucherSystemFee: { percent: 0, fixed: 0, minAmount: null, maxAmount: null },
}); });
// دریافت ارزها // دریافت ارزها
@@ -96,7 +94,7 @@ const Currency = () => {
} }
// بررسی وجود ارز در لیست (برای جلوگیری از درخواست غیرضروری) // بررسی وجود ارز در لیست (برای جلوگیری از درخواست غیرضروری)
const exists = currencies.some(c => c.currencyCode?.code?.toUpperCase() === code); const exists = currencies.some(c => c.currencyCode?.toUpperCase() === code);
if (exists) { if (exists) {
toast.error(`Currency ${code} already exists`); toast.error(`Currency ${code} already exists`);
return; return;
@@ -127,8 +125,12 @@ const Currency = () => {
// --- فعال/غیرفعال کردن Maintenance --- // --- فعال/غیرفعال کردن Maintenance ---
const onEnableMaintenance = async (currencyCode, message = '') => { const onEnableMaintenance = async (currencyCode, message = '') => {
if (!message || message.trim() === '') {
toast.error('Maintenance message is required');
return;
}
try { try {
const result = await currencyAPI.enableMaintenance(currencyCode, message || null); const result = await currencyAPI.enableMaintenance(currencyCode, message);
await fetchCurrencies(); await fetchCurrencies();
setMaintenanceModal(null); setMaintenanceModal(null);
setMaintenanceMessage(''); setMaintenanceMessage('');
@@ -177,11 +179,25 @@ const Currency = () => {
// --- به‌روزرسانی Fees --- // --- به‌روزرسانی Fees ---
const onUpdateFees = async (currencyCode, fees) => { const onUpdateFees = async (currencyCode, fees) => {
try { try {
const result = await currencyAPI.updateFees(currencyCode, fees); // API PUT expects: depositFee, cashOutFee, transferFee, exchangeFee, generateVoucherByAgentFee, generateVoucherByUserFee, expireVoucherSystemFee
// But GET returns: depositLimits, cashOutLimits, transferLimits, voucherLimits
// Map our structure to API expected structure
const payload = {
depositFee: fees.depositLimits || { min: null, max: null },
cashOutFee: fees.cashOutLimits || { min: null, max: null },
transferFee: fees.transferLimits || { min: null, max: null },
exchangeFee: { min: null, max: null }, // Required by API
generateVoucherByAgentFee: fees.voucherLimits || { min: null, max: null }, // Map voucherLimits to generateVoucherByAgentFee
generateVoucherByUserFee: { min: null, max: null }, // Required by API
expireVoucherSystemFee: { min: null, max: null }, // Required by API
};
console.log('🔵 Currency - onUpdateFees payload:', payload);
const result = await currencyAPI.updateFees(currencyCode, payload);
await fetchCurrencies(); await fetchCurrencies();
setFeesModal(null); setFeesModal(null);
toast.success(getSuccessMessage(result) || 'Fees updated successfully'); toast.success(getSuccessMessage(result) || 'Fees updated successfully');
} catch (err) { } catch (err) {
console.error('🔴 Currency - onUpdateFees error:', err);
toast.error(getErrorMessage(err)); toast.error(getErrorMessage(err));
} }
}; };
@@ -190,19 +206,11 @@ const Currency = () => {
const columns = useMemo(() => [ const columns = useMemo(() => [
{ {
key: 'currencyCode', key: 'currencyCode',
header: 'Currency', header: 'Currency Code',
render: (val) => ( render: (val) => (
<div> <span className="font-semibold font-mono">{val || '—'}</span>
<div className="font-semibold">{val?.code || '—'}</div>
<div className="text-xs text-gray-500">{val?.name || ''}</div>
</div>
), ),
}, },
{
key: 'currencyCode',
header: 'Symbol',
render: (val) => <span className="font-mono">{val?.symbol || '—'}</span>,
},
{ {
key: 'isActive', key: 'isActive',
header: 'Active', header: 'Active',
@@ -216,8 +224,18 @@ const Currency = () => {
key: 'isUnderMaintenance', key: 'isUnderMaintenance',
header: 'Maintenance', header: 'Maintenance',
render: (val, row) => ( render: (val, row) => (
<span className={`px-2 py-1 rounded text-xs ${val ? 'bg-yellow-100 text-yellow-800' : 'bg-gray-100 text-gray-800'}`}> <span className={`inline-flex items-center gap-1 px-2 py-1 rounded text-xs ${val ? 'bg-yellow-100 text-yellow-800' : 'bg-gray-100 text-gray-800'}`}>
{val ? '⚠️ Yes' : '✓ No'} {val ? (
<>
<AlertTriangle className="h-3 w-3" />
<span>Yes</span>
</>
) : (
<>
<CheckCircle2 className="h-3 w-3" />
<span>No</span>
</>
)}
</span> </span>
), ),
}, },
@@ -234,7 +252,7 @@ const Currency = () => {
key: 'actions', key: 'actions',
header: 'Actions', header: 'Actions',
render: (_val, row) => { render: (_val, row) => {
const code = row.currencyCode?.code; const code = row.currencyCode;
if (!code) return '—'; if (!code) return '—';
return ( return (
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
@@ -290,16 +308,32 @@ const Currency = () => {
<Settings className="h-3 w-3 mr-1" /> Limits <Settings className="h-3 w-3 mr-1" /> Limits
</button> </button>
<button <button
onClick={() => { onClick={async () => {
setFeesModal(row); setFeesModal(row);
setFeesForm(row.fees || { try {
depositFee: { percent: 0, fixed: 0, minAmount: null, maxAmount: null }, // Fetch fees data from API
cashOutFee: { percent: 0, fixed: 0, minAmount: null, maxAmount: null }, const feesData = await currencyAPI.getFees(row.currencyCode);
transferFee: { percent: 0, fixed: 0, minAmount: null, maxAmount: null }, // API returns: depositLimits, cashOutLimits, transferLimits, voucherLimits
exchangeFee: { percent: 0, fixed: 0, minAmount: null, maxAmount: null }, const allowedFields = ['depositLimits', 'cashOutLimits', 'transferLimits', 'voucherLimits'];
generateVoucherFee: { percent: 0, fixed: 0, minAmount: null, maxAmount: null }, const filteredFees = allowedFields.reduce((acc, key) => {
expireVoucherSystemFee: { percent: 0, fixed: 0, minAmount: null, maxAmount: null }, if (feesData && feesData[key]) {
}); acc[key] = feesData[key];
} else {
acc[key] = { min: null, max: null };
}
return acc;
}, {});
setFeesForm(filteredFees);
} catch (err) {
console.error('Error fetching fees:', err);
// Fallback to default structure
setFeesForm({
depositLimits: { min: null, max: null },
cashOutLimits: { min: null, max: null },
transferLimits: { min: null, max: null },
voucherLimits: { min: null, max: null },
});
}
}} }}
className="inline-flex items-center px-2 py-1 text-xs rounded-md bg-indigo-100 text-indigo-700 hover:opacity-90" className="inline-flex items-center px-2 py-1 text-xs rounded-md bg-indigo-100 text-indigo-700 hover:opacity-90"
title="Fees" title="Fees"
@@ -352,7 +386,7 @@ const Currency = () => {
// --- مودال Maintenance --- // --- مودال Maintenance ---
const renderMaintenanceModal = () => { const renderMaintenanceModal = () => {
if (!maintenanceModal) return null; if (!maintenanceModal) return null;
const code = maintenanceModal.currencyCode?.code; const code = maintenanceModal.currencyCode;
return ( return (
<> <>
@@ -367,19 +401,23 @@ const Currency = () => {
</h3> </h3>
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<label className="block text-sm font-medium mb-2">Maintenance Message (optional)</label> <label className="block text-sm font-medium mb-2">
Maintenance Message <span className="text-red-500">*</span>
</label>
<textarea <textarea
value={maintenanceMessage} value={maintenanceMessage}
onChange={(e) => setMaintenanceMessage(e.target.value)} onChange={(e) => setMaintenanceMessage(e.target.value)}
className="w-full p-2 border rounded-lg" className="w-full p-2 border rounded-lg"
rows={3} rows={3}
placeholder="Enter maintenance message..." placeholder="Enter maintenance message (required)..."
required
/> />
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2">
<button <button
onClick={() => onEnableMaintenance(code, maintenanceMessage)} onClick={() => onEnableMaintenance(code, maintenanceMessage)}
className="flex-1 px-4 py-2 bg-yellow-500 text-white rounded-lg hover:opacity-90" disabled={!maintenanceMessage || maintenanceMessage.trim() === ''}
className="flex-1 px-4 py-2 bg-yellow-500 text-white rounded-lg hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed"
> >
Enable Maintenance Enable Maintenance
</button> </button>
@@ -404,7 +442,7 @@ const Currency = () => {
// --- مودال Permissions --- // --- مودال Permissions ---
const renderPermissionsModal = () => { const renderPermissionsModal = () => {
if (!permissionsModal) return null; if (!permissionsModal) return null;
const code = permissionsModal.currencyCode?.code; const code = permissionsModal.currencyCode;
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
@@ -443,7 +481,7 @@ const Currency = () => {
// --- مودال Limits --- // --- مودال Limits ---
const renderLimitsModal = () => { const renderLimitsModal = () => {
if (!limitsModal) return null; if (!limitsModal) return null;
const code = limitsModal.currencyCode?.code; const code = limitsModal.currencyCode;
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
@@ -508,19 +546,19 @@ const Currency = () => {
// --- مودال Fees --- // --- مودال Fees ---
const renderFeesModal = () => { const renderFeesModal = () => {
if (!feesModal) return null; if (!feesModal) return null;
const code = feesModal.currencyCode?.code; const code = feesModal.currencyCode;
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
onUpdateFees(code, feesForm); onUpdateFees(code, feesForm);
}; };
const updateFee = (category, field, value) => { const updateFee = (category, type, value) => {
setFeesForm({ setFeesForm({
...feesForm, ...feesForm,
[category]: { [category]: {
...feesForm[category], ...feesForm[category],
[field]: value === '' ? null : parseFloat(value) || null, [type]: value === '' ? null : parseFloat(value) || null,
}, },
}); });
}; };
@@ -529,60 +567,38 @@ const Currency = () => {
<> <>
<div className="fixed inset-0 bg-black bg-opacity-50 z-40" onClick={() => setFeesModal(null)} /> <div className="fixed inset-0 bg-black bg-opacity-50 z-40" onClick={() => setFeesModal(null)} />
<div className="fixed inset-0 z-50 flex items-center justify-center p-4"> <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<div className="w-full max-w-3xl card p-6 relative bg-white rounded-2xl shadow-lg max-h-[90vh] overflow-y-auto"> <div className="w-full max-w-2xl card p-6 relative bg-white rounded-2xl shadow-lg max-h-[90vh] overflow-y-auto">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Fees: {code}</h3> <h3 className="text-lg font-semibold text-gray-900 mb-4">Fees: {code}</h3>
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-4">
{Object.keys(feesForm).map((category) => ( {Object.keys(feesForm).map((category) => {
return (
<div key={category} className="border rounded-lg p-3"> <div key={category} className="border rounded-lg p-3">
<h4 className="font-medium mb-2">{category.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase())}</h4> <h4 className="font-medium mb-2">{category}</h4>
<div className="grid grid-cols-2 gap-2"> <div className="grid grid-cols-2 gap-2">
<div> <div>
<label className="block text-xs text-gray-600 mb-1">Percent</label> <label className="block text-xs text-gray-600 mb-1">Min</label>
<input <input
type="number" type="number"
step="0.01" value={feesForm[category].min ?? ''}
value={feesForm[category].percent ?? ''} onChange={(e) => updateFee(category, 'min', e.target.value)}
onChange={(e) => updateFee(category, 'percent', e.target.value)}
className="w-full p-2 border rounded" className="w-full p-2 border rounded"
placeholder="Percent" placeholder="Min"
/> />
</div> </div>
<div> <div>
<label className="block text-xs text-gray-600 mb-1">Fixed</label> <label className="block text-xs text-gray-600 mb-1">Max</label>
<input <input
type="number" type="number"
step="0.01" value={feesForm[category].max ?? ''}
value={feesForm[category].fixed ?? ''} onChange={(e) => updateFee(category, 'max', e.target.value)}
onChange={(e) => updateFee(category, 'fixed', e.target.value)}
className="w-full p-2 border rounded" className="w-full p-2 border rounded"
placeholder="Fixed" placeholder="Max"
/>
</div>
<div>
<label className="block text-xs text-gray-600 mb-1">Min Amount</label>
<input
type="number"
step="0.01"
value={feesForm[category].minAmount ?? ''}
onChange={(e) => updateFee(category, 'minAmount', e.target.value)}
className="w-full p-2 border rounded"
placeholder="Min Amount"
/>
</div>
<div>
<label className="block text-xs text-gray-600 mb-1">Max Amount</label>
<input
type="number"
step="0.01"
value={feesForm[category].maxAmount ?? ''}
onChange={(e) => updateFee(category, 'maxAmount', e.target.value)}
className="w-full p-2 border rounded"
placeholder="Max Amount"
/> />
</div> </div>
</div> </div>
</div> </div>
))} );
})}
<div className="flex justify-end gap-x-2 mt-4"> <div className="flex justify-end gap-x-2 mt-4">
<button type="button" onClick={() => setFeesModal(null)} className="px-4 py-2 border rounded-lg">Cancel</button> <button type="button" onClick={() => setFeesModal(null)} className="px-4 py-2 border rounded-lg">Cancel</button>
<button type="submit" className="btn-primary px-4 py-2 rounded-lg">Save</button> <button type="submit" className="btn-primary px-4 py-2 rounded-lg">Save</button>
@@ -640,9 +656,8 @@ const Currency = () => {
data={currencies.filter(c => { data={currencies.filter(c => {
if (!filter) return true; if (!filter) return true;
const search = filter.toLowerCase(); const search = filter.toLowerCase();
const code = c.currencyCode?.code?.toLowerCase() || ''; const code = c.currencyCode?.toLowerCase() || '';
const name = c.currencyCode?.name?.toLowerCase() || ''; return code.includes(search);
return code.includes(search) || name.includes(search);
})} })}
columns={columns} columns={columns}
searchable={false} searchable={false}

View File

@@ -6,52 +6,199 @@ import api from './apiClient';
export const currencyAPI = { export const currencyAPI = {
// GET /api/v1/Currency // GET /api/v1/Currency
async list() { async list() {
console.log('🔵 Currency API - list request');
const res = await api.get('/api/v1/Currency', { skipAuthRedirect: true }); const res = await api.get('/api/v1/Currency', { skipAuthRedirect: true });
return res?.data?.data || []; console.log('🟢 Currency API - list response:', res?.data);
// Response structure: { data: [{ currencyCode, isActive, permissions, limits, fees, ... }, ...] }
return Array.isArray(res?.data?.data) ? res.data.data : (Array.isArray(res?.data) ? res.data : []);
},
// GET /api/v1/Currency/{currencyCode}
async getByCode(currencyCode) {
if (!currencyCode) {
throw new Error('Currency code is required');
}
const res = await api.get(`/api/v1/Currency/${encodeURIComponent(currencyCode)}`, {
skipAuthRedirect: true
});
return res?.data?.data || res?.data;
}, },
// POST /api/v1/Currency // POST /api/v1/Currency
async create(currencyCode) { async create(currencyCode) {
const payload = { currencyCode: String(currencyCode || '') }; if (!currencyCode) {
throw new Error('Currency code is required');
}
const payload = { currencyCode: String(currencyCode).trim() };
const res = await api.post('/api/v1/Currency', payload, { skipAuthRedirect: true }); const res = await api.post('/api/v1/Currency', payload, { skipAuthRedirect: true });
return res?.data; return res?.data;
}, },
// PATCH /api/v1/Currency/{currencyCode}/ToggleActivation // PATCH /api/v1/Currency/{currencyCode}/ToggleActivation
async toggleActivation(currencyCode) { async toggleActivation(currencyCode) {
if (!currencyCode) {
throw new Error('Currency code is required');
}
const res = await api.patch(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/ToggleActivation`, null, { skipAuthRedirect: true }); const res = await api.patch(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/ToggleActivation`, null, { skipAuthRedirect: true });
return res?.data; return res?.data;
}, },
// PATCH /api/v1/Currency/{currencyCode}/Maintenance/Enable // PATCH /api/v1/Currency/{currencyCode}/Maintenance/Enable
async enableMaintenance(currencyCode, maintenanceMessage = null) { async enableMaintenance(currencyCode, maintenanceMessage) {
const payload = maintenanceMessage ? { maintenanceMessage } : {}; if (!currencyCode) {
const res = await api.patch(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/Maintenance/Enable`, payload, { skipAuthRedirect: true }); throw new Error('Currency code is required');
return res?.data; }
if (!maintenanceMessage || String(maintenanceMessage).trim() === '') {
throw new Error('Maintenance message is required');
}
// API expects "Description" field (PascalCase)
const payload = { Description: String(maintenanceMessage).trim() };
console.log('🔵 Currency API - enableMaintenance request:', {
currencyCode,
payload
});
try {
const res = await api.patch(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/Maintenance/Enable`, payload, { skipAuthRedirect: true });
console.log('🟢 Currency API - enableMaintenance response:', res?.data);
return res?.data;
} catch (error) {
console.error('🔴 Currency API - enableMaintenance error:', {
currencyCode,
payload,
status: error?.response?.status,
data: error?.response?.data,
error: error?.response?.data || error?.message
});
throw error;
}
}, },
// PATCH /api/v1/Currency/{currencyCode}/Maintenance/Disable // PATCH /api/v1/Currency/{currencyCode}/Maintenance/Disable
async disableMaintenance(currencyCode) { async disableMaintenance(currencyCode) {
if (!currencyCode) {
throw new Error('Currency code is required');
}
const res = await api.patch(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/Maintenance/Disable`, null, { skipAuthRedirect: true }); const res = await api.patch(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/Maintenance/Disable`, null, { skipAuthRedirect: true });
return res?.data; return res?.data;
}, },
// GET /api/v1/Currency/{currencyCode}/Permissions
async getPermissions(currencyCode) {
if (!currencyCode) {
throw new Error('Currency code is required');
}
try {
const res = await api.get(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/Permissions`, {
skipAuthRedirect: true
});
// Response structure: { statusCode, isSuccess, code, message, errors, data: {...} }
// Return the data field from the response
return res?.data?.data || null;
} catch (error) {
console.error('🔴 Currency API - getPermissions error:', {
currencyCode,
status: error?.response?.status,
data: error?.response?.data,
error: error?.response?.data || error?.message
});
throw error;
}
},
// PUT /api/v1/Currency/{currencyCode}/Permissions // PUT /api/v1/Currency/{currencyCode}/Permissions
async updatePermissions(currencyCode, permissions) { async updatePermissions(currencyCode, permissions) {
if (!currencyCode) {
throw new Error('Currency code is required');
}
const res = await api.put(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/Permissions`, permissions, { skipAuthRedirect: true }); const res = await api.put(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/Permissions`, permissions, { skipAuthRedirect: true });
return res?.data; return res?.data;
}, },
// GET /api/v1/Currency/{currencyCode}/Limits
async getLimits(currencyCode) {
if (!currencyCode) {
throw new Error('Currency code is required');
}
try {
const res = await api.get(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/Limits`, {
skipAuthRedirect: true
});
// Response structure: { statusCode, isSuccess, code, message, errors, data: { depositLimits, cashOutLimits, transferLimits, voucherLimits } }
// Return the data field from the response
return res?.data?.data || null;
} catch (error) {
console.error('🔴 Currency API - getLimits error:', {
currencyCode,
status: error?.response?.status,
data: error?.response?.data,
error: error?.response?.data || error?.message
});
throw error;
}
},
// PUT /api/v1/Currency/{currencyCode}/Limits // PUT /api/v1/Currency/{currencyCode}/Limits
async updateLimits(currencyCode, limits) { async updateLimits(currencyCode, limits) {
if (!currencyCode) {
throw new Error('Currency code is required');
}
const res = await api.put(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/Limits`, limits, { skipAuthRedirect: true }); const res = await api.put(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/Limits`, limits, { skipAuthRedirect: true });
return res?.data; return res?.data;
}, },
// GET /api/v1/Currency/{currencyCode}/Fees
async getFees(currencyCode) {
if (!currencyCode) {
throw new Error('Currency code is required');
}
try {
console.log('🔵 Currency API - getFees request:', { currencyCode });
const res = await api.get(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/Fees`, {
skipAuthRedirect: true
});
console.log('🟢 Currency API - getFees response:', res?.data);
// Response structure: { statusCode, isSuccess, code, message, errors, data: { depositLimits, cashOutLimits, transferLimits, voucherLimits } }
// Return the data field from the response
return res?.data?.data || null;
} catch (error) {
console.error('🔴 Currency API - getFees error:', {
currencyCode,
status: error?.response?.status,
data: error?.response?.data,
error: error?.response?.data || error?.message
});
throw error;
}
},
// PUT /api/v1/Currency/{currencyCode}/Fees // PUT /api/v1/Currency/{currencyCode}/Fees
async updateFees(currencyCode, fees) { async updateFees(currencyCode, fees) {
const res = await api.put(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/Fees`, fees, { skipAuthRedirect: true }); if (!currencyCode) {
return res?.data; throw new Error('Currency code is required');
}
if (!fees) {
throw new Error('Fees data is required');
}
try {
console.log('🔵 Currency API - updateFees request:', {
currencyCode,
payload: fees
});
const res = await api.put(`/api/v1/Currency/${encodeURIComponent(currencyCode)}/Fees`, fees, {
skipAuthRedirect: true
});
console.log('🟢 Currency API - updateFees response:', res?.data);
return res?.data;
} catch (error) {
console.error('🔴 Currency API - updateFees error:', {
currencyCode,
payload: fees,
status: error?.response?.status,
data: error?.response?.data,
error: error?.response?.data || error?.message
});
throw error;
}
}, },
}; };

View File

@@ -376,6 +376,9 @@ export const issuerAPI = {
// GET /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement // GET /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement
async getTopUpAgentManagement(issuerId) { async getTopUpAgentManagement(issuerId) {
if (!issuerId) {
throw new Error('Issuer ID is required');
}
try { try {
const res = await api.get(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement`, { const res = await api.get(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement`, {
skipAuthRedirect: true skipAuthRedirect: true
@@ -388,9 +391,15 @@ export const issuerAPI = {
}, },
// PUT /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement // PUT /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement
async updateTopUpAgentManagement(issuerId, maxAgents) { async updateTopUpAgentManagement(issuerId, settings) {
if (!issuerId) {
throw new Error('Issuer ID is required');
}
try { try {
const payload = { maxAgents: Number(maxAgents) || 0 }; // Support both old format (just maxAgents) and new format (settings object)
const payload = typeof settings === 'number'
? { maxAgents: Number(settings) || 0 }
: { maxAgents: Number(settings?.maxAgents) || 0, ...settings };
const res = await api.put(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement`, payload, { const res = await api.put(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement`, payload, {
skipAuthRedirect: true skipAuthRedirect: true
}); });
@@ -403,11 +412,14 @@ export const issuerAPI = {
// GET /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency // GET /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency
async getTopUpAgentManagementCurrencies(issuerId) { async getTopUpAgentManagementCurrencies(issuerId) {
if (!issuerId) {
throw new Error('Issuer ID is required');
}
try { try {
const res = await api.get(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency`, { const res = await api.get(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency`, {
skipAuthRedirect: true skipAuthRedirect: true
}); });
return Array.isArray(res?.data?.data) ? res?.data?.data : []; return Array.isArray(res?.data?.data) ? res?.data?.data : (Array.isArray(res?.data) ? res?.data : []);
} catch (error) { } catch (error) {
console.error('🔴 TopUpAgentManagement Currencies GET error:', error); console.error('🔴 TopUpAgentManagement Currencies GET error:', error);
throw error; throw error;
@@ -416,11 +428,17 @@ export const issuerAPI = {
// GET /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency/{currencyCode} // GET /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency/{currencyCode}
async getTopUpAgentManagementCurrency(issuerId, currencyCode) { async getTopUpAgentManagementCurrency(issuerId, currencyCode) {
if (!issuerId) {
throw new Error('Issuer ID is required');
}
if (!currencyCode) {
throw new Error('Currency code is required');
}
try { try {
const res = await api.get(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency/${encodeURIComponent(currencyCode)}`, { const res = await api.get(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency/${encodeURIComponent(currencyCode)}`, {
skipAuthRedirect: true skipAuthRedirect: true
}); });
return res?.data?.data || null; return res?.data?.data || res?.data || null;
} catch (error) { } catch (error) {
console.error('🔴 TopUpAgentManagement Currency GET error:', error); console.error('🔴 TopUpAgentManagement Currency GET error:', error);
throw error; throw error;
@@ -428,27 +446,33 @@ export const issuerAPI = {
}, },
// POST /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency/{currencyCode} // POST /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency/{currencyCode}
async createTopUpAgentManagementCurrency(issuerId, currencyCode, currencyData) { async createTopUpAgentManagementCurrency(issuerId, currencyCode, currencyData = {}) {
if (!issuerId) {
throw new Error('Issuer ID is required');
}
if (!currencyCode) {
throw new Error('Currency code is required');
}
try { try {
const payload = { const payload = {
voucherGeneratingFee: { voucherGeneratingFee: {
percent: Number(currencyData.voucherGeneratingFee?.percent) || 0, percent: Number(currencyData.voucherGeneratingFee?.percent) || 0,
fixed: Number(currencyData.voucherGeneratingFee?.fixed) || 0, fixed: Number(currencyData.voucherGeneratingFee?.fixed) || 0,
minAmount: Number(currencyData.voucherGeneratingFee?.minAmount) || 0, minAmount: currencyData.voucherGeneratingFee?.minAmount != null ? Number(currencyData.voucherGeneratingFee.minAmount) : null,
maxAmount: Number(currencyData.voucherGeneratingFee?.maxAmount) || 0 maxAmount: currencyData.voucherGeneratingFee?.maxAmount != null ? Number(currencyData.voucherGeneratingFee.maxAmount) : null
}, },
agentMarketingFee: { agentMarketingFee: {
percent: Number(currencyData.agentMarketingFee?.percent) || 0, percent: Number(currencyData.agentMarketingFee?.percent) || 0,
fixed: Number(currencyData.agentMarketingFee?.fixed) || 0, fixed: Number(currencyData.agentMarketingFee?.fixed) || 0,
minAmount: Number(currencyData.agentMarketingFee?.minAmount) || 0, minAmount: currencyData.agentMarketingFee?.minAmount != null ? Number(currencyData.agentMarketingFee.minAmount) : null,
maxAmount: Number(currencyData.agentMarketingFee?.maxAmount) || 0 maxAmount: currencyData.agentMarketingFee?.maxAmount != null ? Number(currencyData.agentMarketingFee.maxAmount) : null
}, },
agentWalletMaxBalanceLimit: { agentWalletMaxBalanceLimit: {
min: Number(currencyData.agentWalletMaxBalanceLimit?.min) || 0, min: currencyData.agentWalletMaxBalanceLimit?.min != null ? Number(currencyData.agentWalletMaxBalanceLimit.min) : null,
max: Number(currencyData.agentWalletMaxBalanceLimit?.max) || 0 max: currencyData.agentWalletMaxBalanceLimit?.max != null ? Number(currencyData.agentWalletMaxBalanceLimit.max) : null
}, },
agentWalletDepositMonthlyLimit: { agentWalletDepositMonthlyLimit: {
amount: Number(currencyData.agentWalletDepositMonthlyLimit?.amount) || 0 amount: currencyData.agentWalletDepositMonthlyLimit?.amount != null ? Number(currencyData.agentWalletDepositMonthlyLimit.amount) : null
} }
}; };
const res = await api.post(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency/${encodeURIComponent(currencyCode)}`, payload, { const res = await api.post(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency/${encodeURIComponent(currencyCode)}`, payload, {
@@ -463,26 +487,35 @@ export const issuerAPI = {
// PUT /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency/{currencyCode} // PUT /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency/{currencyCode}
async updateTopUpAgentManagementCurrency(issuerId, currencyCode, currencyData) { async updateTopUpAgentManagementCurrency(issuerId, currencyCode, currencyData) {
if (!issuerId) {
throw new Error('Issuer ID is required');
}
if (!currencyCode) {
throw new Error('Currency code is required');
}
if (!currencyData) {
throw new Error('Currency data is required');
}
try { try {
const payload = { const payload = {
voucherGeneratingFee: { voucherGeneratingFee: {
percent: Number(currencyData.voucherGeneratingFee?.percent) || 0, percent: Number(currencyData.voucherGeneratingFee?.percent) || 0,
fixed: Number(currencyData.voucherGeneratingFee?.fixed) || 0, fixed: Number(currencyData.voucherGeneratingFee?.fixed) || 0,
minAmount: Number(currencyData.voucherGeneratingFee?.minAmount) || 0, minAmount: currencyData.voucherGeneratingFee?.minAmount != null ? Number(currencyData.voucherGeneratingFee.minAmount) : null,
maxAmount: Number(currencyData.voucherGeneratingFee?.maxAmount) || 0 maxAmount: currencyData.voucherGeneratingFee?.maxAmount != null ? Number(currencyData.voucherGeneratingFee.maxAmount) : null
}, },
agentMarketingFee: { agentMarketingFee: {
percent: Number(currencyData.agentMarketingFee?.percent) || 0, percent: Number(currencyData.agentMarketingFee?.percent) || 0,
fixed: Number(currencyData.agentMarketingFee?.fixed) || 0, fixed: Number(currencyData.agentMarketingFee?.fixed) || 0,
minAmount: Number(currencyData.agentMarketingFee?.minAmount) || 0, minAmount: currencyData.agentMarketingFee?.minAmount != null ? Number(currencyData.agentMarketingFee.minAmount) : null,
maxAmount: Number(currencyData.agentMarketingFee?.maxAmount) || 0 maxAmount: currencyData.agentMarketingFee?.maxAmount != null ? Number(currencyData.agentMarketingFee.maxAmount) : null
}, },
agentWalletMaxBalanceLimit: { agentWalletMaxBalanceLimit: {
min: Number(currencyData.agentWalletMaxBalanceLimit?.min) || 0, min: currencyData.agentWalletMaxBalanceLimit?.min != null ? Number(currencyData.agentWalletMaxBalanceLimit.min) : null,
max: Number(currencyData.agentWalletMaxBalanceLimit?.max) || 0 max: currencyData.agentWalletMaxBalanceLimit?.max != null ? Number(currencyData.agentWalletMaxBalanceLimit.max) : null
}, },
agentWalletDepositMonthlyLimit: { agentWalletDepositMonthlyLimit: {
amount: Number(currencyData.agentWalletDepositMonthlyLimit?.amount) || 0 amount: currencyData.agentWalletDepositMonthlyLimit?.amount != null ? Number(currencyData.agentWalletDepositMonthlyLimit.amount) : null
} }
}; };
const res = await api.put(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency/${encodeURIComponent(currencyCode)}`, payload, { const res = await api.put(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency/${encodeURIComponent(currencyCode)}`, payload, {
@@ -497,6 +530,12 @@ export const issuerAPI = {
// DELETE /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency/{currencyCode} // DELETE /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency/{currencyCode}
async deleteTopUpAgentManagementCurrency(issuerId, currencyCode) { async deleteTopUpAgentManagementCurrency(issuerId, currencyCode) {
if (!issuerId) {
throw new Error('Issuer ID is required');
}
if (!currencyCode) {
throw new Error('Currency code is required');
}
try { try {
const res = await api.delete(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency/${encodeURIComponent(currencyCode)}`, { const res = await api.delete(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency/${encodeURIComponent(currencyCode)}`, {
skipAuthRedirect: true skipAuthRedirect: true
@@ -512,11 +551,17 @@ export const issuerAPI = {
// GET /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency/{currencyCode}/Wallet/Balance // GET /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency/{currencyCode}/Wallet/Balance
async getTopUpAgentManagementWalletBalance(issuerId, currencyCode) { async getTopUpAgentManagementWalletBalance(issuerId, currencyCode) {
if (!issuerId) {
throw new Error('Issuer ID is required');
}
if (!currencyCode) {
throw new Error('Currency code is required');
}
try { try {
const res = await api.get(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency/${encodeURIComponent(currencyCode)}/Wallet/Balance`, { const res = await api.get(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency/${encodeURIComponent(currencyCode)}/Wallet/Balance`, {
skipAuthRedirect: true skipAuthRedirect: true
}); });
return res?.data?.data || null; return res?.data?.data || res?.data || null;
} catch (error) { } catch (error) {
console.error('🔴 TopUpAgentManagement Wallet Balance GET error:', error); console.error('🔴 TopUpAgentManagement Wallet Balance GET error:', error);
throw error; throw error;
@@ -525,12 +570,21 @@ export const issuerAPI = {
// POST /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency/{currencyCode}/Wallet/Deposit // POST /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency/{currencyCode}/Wallet/Deposit
async depositTopUpAgentManagementWallet(issuerId, currencyCode, amount) { async depositTopUpAgentManagementWallet(issuerId, currencyCode, amount) {
if (!issuerId) {
throw new Error('Issuer ID is required');
}
if (!currencyCode) {
throw new Error('Currency code is required');
}
if (amount == null || amount === '') {
throw new Error('Amount is required');
}
try { try {
const payload = { amount: Number(amount) || 0 }; const payload = { amount: Number(amount) || 0 };
const res = await api.post(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency/${encodeURIComponent(currencyCode)}/Wallet/Deposit`, payload, { const res = await api.post(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency/${encodeURIComponent(currencyCode)}/Wallet/Deposit`, payload, {
skipAuthRedirect: true skipAuthRedirect: true
}); });
return res?.data; return res?.data;
} catch (error) { } catch (error) {
console.error('🔴 TopUpAgentManagement Wallet Deposit POST error:', error); console.error('🔴 TopUpAgentManagement Wallet Deposit POST error:', error);
throw error; throw error;
@@ -539,11 +593,17 @@ export const issuerAPI = {
// GET /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency/{currencyCode}/Wallet/Transactions // GET /api/v1/issuer/{issuerId}/Capability/TopUpAgentManagement/Currency/{currencyCode}/Wallet/Transactions
async getTopUpAgentManagementWalletTransactions(issuerId, currencyCode) { async getTopUpAgentManagementWalletTransactions(issuerId, currencyCode) {
if (!issuerId) {
throw new Error('Issuer ID is required');
}
if (!currencyCode) {
throw new Error('Currency code is required');
}
try { try {
const res = await api.get(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency/${encodeURIComponent(currencyCode)}/Wallet/Transactions`, { const res = await api.get(`/api/v1/issuer/${encodeURIComponent(issuerId)}/Capability/TopUpAgentManagement/Currency/${encodeURIComponent(currencyCode)}/Wallet/Transactions`, {
skipAuthRedirect: true skipAuthRedirect: true
}); });
return Array.isArray(res?.data?.data) ? res?.data?.data : []; return Array.isArray(res?.data?.data) ? res?.data?.data : (Array.isArray(res?.data) ? res?.data : []);
} catch (error) { } catch (error) {
console.error('🔴 TopUpAgentManagement Wallet Transactions GET error:', error); console.error('🔴 TopUpAgentManagement Wallet Transactions GET error:', error);
throw error; throw error;