disbursement fixing
This commit is contained in:
@ -5,15 +5,15 @@ import { useCallApi } from '@/hooks';
|
|||||||
import { apiConfig } from '@/config/api.config';
|
import { apiConfig } from '@/config/api.config';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import { getAuth } from '@/auth';
|
||||||
|
import { toast } from 'sonner';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogBody,
|
DialogBody,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle
|
DialogTitle
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import TransactionLogViewer from './DetailTransactionLog';
|
|
||||||
|
|
||||||
const API_URL = apiConfig.service_disbursement;
|
const API_URL = apiConfig.service_disbursement;
|
||||||
|
|
||||||
@ -57,6 +57,7 @@ const DetailTransaction = () => {
|
|||||||
|
|
||||||
const [transactionDetails, setTransactionDetails] = useState<any>(null);
|
const [transactionDetails, setTransactionDetails] = useState<any>(null);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [isExporting, setIsExporting] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchTransactionDetails = async () => {
|
const fetchTransactionDetails = async () => {
|
||||||
@ -69,7 +70,6 @@ const DetailTransaction = () => {
|
|||||||
id: selectedTransactionId
|
id: selectedTransactionId
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// console.log(response?.data);
|
|
||||||
setTransactionDetails(response?.data);
|
setTransactionDetails(response?.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching transaction', error);
|
console.error('Error fetching transaction', error);
|
||||||
@ -84,8 +84,6 @@ const DetailTransaction = () => {
|
|||||||
}
|
}
|
||||||
}, [showDetailDialog, selectedTransactionId, GetData]);
|
}, [showDetailDialog, selectedTransactionId, GetData]);
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState('detail'); // 'detail', 'log', 'approve'
|
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
setTransactionDetails(null);
|
setTransactionDetails(null);
|
||||||
};
|
};
|
||||||
@ -96,109 +94,230 @@ const DetailTransaction = () => {
|
|||||||
}
|
}
|
||||||
}, [showDetailDialog]);
|
}, [showDetailDialog]);
|
||||||
|
|
||||||
|
const handleExport = async () => {
|
||||||
|
if (!selectedTransactionId || !transactionDetails) return;
|
||||||
|
|
||||||
|
setIsExporting(true);
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`${API_URL}/transaction/export?id_disbursment=${selectedTransactionId}`,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${getAuth()?.access_token}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch file');
|
||||||
|
}
|
||||||
|
|
||||||
|
const blob = await response.blob();
|
||||||
|
const contentDisposition = response.headers.get('content-disposition');
|
||||||
|
|
||||||
|
const executionDate = transactionDetails.execution_date;
|
||||||
|
const formattedDate = executionDate
|
||||||
|
? moment(executionDate).format('YYYYMMDD_HHmm')
|
||||||
|
: moment().format('YYYYMMDD_HHmm');
|
||||||
|
|
||||||
|
let filename = `transaction_${formattedDate}.xlsx`;
|
||||||
|
|
||||||
|
if (contentDisposition) {
|
||||||
|
const filenameMatch = contentDisposition.match(/filename\*?=(?:UTF-8'')?"?([^;"\n]*)"?/);
|
||||||
|
if (filenameMatch && filenameMatch[1]) {
|
||||||
|
filename = decodeURIComponent(filenameMatch[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
a.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
|
||||||
|
toast.success('Export successful');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error exporting transaction:', error);
|
||||||
|
toast.error('Failed to export transaction');
|
||||||
|
} finally {
|
||||||
|
setIsExporting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={showDetailDialog} onOpenChange={setShowDetailDialog}>
|
<Dialog open={showDetailDialog} onOpenChange={setShowDetailDialog}>
|
||||||
<DialogContent className="container-fixed max-w-[1024px] flex flex-col p-5 overflow-hidden">
|
<DialogContent className="container-fixed max-w-[1280px] w-full h-[90vh] flex flex-col p-6 overflow-hidden">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Transaction Details</DialogTitle>
|
<DialogTitle className="text-2xl font-bold">Transaction Details</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<DialogBody>
|
<DialogBody>
|
||||||
{/* Tab Content */}
|
<div className="py-4 overflow-y-auto max-h-[600px] space-y-6">
|
||||||
<div className="py-4 overflow-y-auto max-h-[400px]">
|
{/* Summary Info */}
|
||||||
<div className="space-y-4">
|
{transactionDetails && (
|
||||||
<div className="border rounded-lg overflow-x-auto">
|
<div className="grid grid-cols-2 gap-y-4 gap-x-6 border rounded-lg p-4 bg-gray-50">
|
||||||
{isLoading ? (
|
<div>
|
||||||
<div className="flex flex-col items-center justify-center p-8">
|
<p className="text-sm text-gray-500">Filename Upload</p>
|
||||||
<div className="animate-pulse flex space-x-4 w-full">
|
<p className="text-base text-gray-800">{transactionDetails.file_name ?? '-'}</p>
|
||||||
<div className="flex-1 space-y-4 py-1">
|
</div>
|
||||||
<div className="h-4 bg-gray-200 rounded w-3/4"></div>
|
<div>
|
||||||
<div className="space-y-2">
|
<p className="text-sm text-gray-500">Total Amount</p>
|
||||||
<div className="h-4 bg-gray-200 rounded"></div>
|
<p className="text-base text-gray-800">{transactionDetails.amount ?? '-'}</p>
|
||||||
<div className="h-4 bg-gray-200 rounded w-5/6"></div>
|
</div>
|
||||||
</div>
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Total Record</p>
|
||||||
|
<p className="text-base text-gray-800">
|
||||||
|
{transactionDetails.total_record ?? '-'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Success Record</p>
|
||||||
|
<p className="text-base text-gray-800">
|
||||||
|
{transactionDetails.total_success ?? '-'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Fail Record</p>
|
||||||
|
<p className="text-base text-gray-800">{transactionDetails.total_fail ?? '-'}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Pending Record</p>
|
||||||
|
<p className="text-base text-gray-800">
|
||||||
|
{transactionDetails.total_pending ?? '-'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Status</p>
|
||||||
|
<p className="text-base text-gray-800">
|
||||||
|
{renderStatusBadge(transactionDetails.status)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Execution Date</p>
|
||||||
|
<p className="text-base text-gray-800">
|
||||||
|
{transactionDetails.execution_date
|
||||||
|
? moment(transactionDetails.execution_date).format('DD/MM/YYYY HH:mm')
|
||||||
|
: '-'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Done Date</p>
|
||||||
|
<p className="text-base text-gray-800">
|
||||||
|
{transactionDetails.done_date
|
||||||
|
? moment(transactionDetails.done_date).format('DD/MM/YYYY HH:mm')
|
||||||
|
: '-'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Log Table */}
|
||||||
|
<div className="border rounded-lg overflow-x-auto">
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="flex flex-col items-center justify-center p-8">
|
||||||
|
<div className="animate-pulse flex space-x-4 w-full">
|
||||||
|
<div className="flex-1 space-y-4 py-1">
|
||||||
|
<div className="h-4 bg-gray-200 rounded w-3/4"></div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="h-4 bg-gray-200 rounded"></div>
|
||||||
|
<div className="h-4 bg-gray-200 rounded w-5/6"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-4 text-gray-500">Loading Logs Details...</p>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
<p className="mt-4 text-gray-500">Loading Logs Details...</p>
|
||||||
<table className="min-w-full table-auto">
|
</div>
|
||||||
<thead>
|
) : (
|
||||||
<tr className="bg-gray-100">
|
<table className="min-w-full table-auto">
|
||||||
<th className="px-4 py-2 text-left text-sm text-gray-500">Username</th>
|
<thead>
|
||||||
<th className="px-4 py-2 text-left text-sm text-gray-500">Fullname</th>
|
<tr className="bg-gray-100">
|
||||||
<th className="px-4 py-2 text-left text-sm text-gray-500">Amount</th>
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Username</th>
|
||||||
<th className="px-4 py-2 text-left text-sm text-gray-500">Status</th>
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Fullname</th>
|
||||||
<th className="px-4 py-2 text-left text-sm text-gray-500">Process Date</th>
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Amount</th>
|
||||||
<th className="px-4 py-2 text-left text-sm text-gray-500">
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Status</th>
|
||||||
Invoice Number
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Process Date</th>
|
||||||
</th>
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Invoice Number</th>
|
||||||
<th className="px-4 py-2 text-left text-sm text-gray-500">Actions</th>
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Remark 1</th>
|
||||||
</tr>
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Remark 2</th>
|
||||||
</thead>
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Remark 3</th>
|
||||||
<tbody>
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Actions</th>
|
||||||
{transactionDetails?.log && transactionDetails.log.length > 0 ? (
|
</tr>
|
||||||
transactionDetails.log.map(
|
</thead>
|
||||||
(
|
<tbody>
|
||||||
log: {
|
{transactionDetails?.log && transactionDetails.log.length > 0 ? (
|
||||||
id: number;
|
transactionDetails.log.map((log: any, index: number) => (
|
||||||
customer: any;
|
<tr key={index} className="border-t">
|
||||||
amount: number;
|
<td className="px-4 py-2 text-sm text-gray-500">
|
||||||
remark: string;
|
{log.customer?.username ?? 'Not Found'}
|
||||||
reference: string;
|
</td>
|
||||||
request_date: string;
|
<td className="px-4 py-2 text-sm text-gray-500">
|
||||||
payment_response: string;
|
{log.customer?.fullname ?? 'Not Found'}
|
||||||
status: string;
|
</td>
|
||||||
},
|
<td className="px-4 py-2 text-sm text-gray-500">{log.amount ?? '-'}</td>
|
||||||
index: number
|
<td className="px-4 py-2 text-sm text-gray-500">
|
||||||
) => (
|
{renderStatusBadge(log.status) ?? '-'}
|
||||||
<tr key={index} className="border-t">
|
</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">
|
<td className="px-4 py-2 text-sm text-gray-500">
|
||||||
{log.customer?.username ?? 'Not Found'}
|
{log.request_date && moment(log.request_date).isValid()
|
||||||
</td>
|
? moment(log.request_date).format('DD/MM/YYYY HH:mm')
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">
|
: '-'}
|
||||||
{log.customer?.fullname ?? 'Not Found'}
|
</td>
|
||||||
</td>
|
<td className="px-4 py-2 text-sm text-gray-500">
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">
|
{log.reference ?? '-'}
|
||||||
{log.amount ?? '-'}
|
</td>
|
||||||
</td>
|
<td className="px-4 py-2 text-sm text-gray-500">{log.remark_1 ?? '-'}</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">
|
<td className="px-4 py-2 text-sm text-gray-500">{log.remark_2 ?? '-'}</td>
|
||||||
{renderStatusBadge(log.status) ?? '-'}
|
<td className="px-4 py-2 text-sm text-gray-500">{log.remark_3 ?? '-'}</td>
|
||||||
</td>
|
<td className="px-4 py-2 text-sm text-gray-500">
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">
|
<button
|
||||||
{log.request_date && moment(log.request_date).isValid()
|
className="btn btn-sm btn-icon btn-clear btn-light"
|
||||||
? moment(log.request_date).format('DD/MM/YYYY HH:mm:ss')
|
onClick={() => {
|
||||||
: '-'}
|
setDetailLogData(log);
|
||||||
</td>
|
setShowDetailLogDialog(true);
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">
|
}}
|
||||||
{log.reference ?? '-'}
|
>
|
||||||
</td>
|
<KeenIcon icon="eye" />
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">
|
</button>
|
||||||
<div key={`actions-${log.id}`}>
|
|
||||||
<button
|
|
||||||
className="btn btn-sm btn-icon btn-clear btn-light"
|
|
||||||
onClick={() => {
|
|
||||||
setDetailLogData(log);
|
|
||||||
setShowDetailLogDialog(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<KeenIcon icon="eye" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<tr>
|
|
||||||
<td colSpan={8} className="px-4 py-2 text-center text-sm text-gray-500">
|
|
||||||
No logs available
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
))
|
||||||
</tbody>
|
) : (
|
||||||
</table>
|
<tr>
|
||||||
|
<td colSpan={10} className="px-4 py-2 text-center text-sm text-gray-500">
|
||||||
|
No logs available
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Export Button - Moved to bottom right after table */}
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<button
|
||||||
|
className={`px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors flex items-center ${isExporting ? 'opacity-50 pointer-events-none' : ''}`}
|
||||||
|
onClick={() => {
|
||||||
|
if (!isExporting) {
|
||||||
|
handleExport();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isExporting ? (
|
||||||
|
<>
|
||||||
|
<span className="animate-spin mr-2">
|
||||||
|
<KeenIcon icon="spinner" />
|
||||||
|
</span>
|
||||||
|
Exporting...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<KeenIcon icon="download" className="mr-2" />
|
||||||
|
Export
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DialogBody>
|
</DialogBody>
|
||||||
@ -207,4 +326,4 @@ const DetailTransaction = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DetailTransaction;
|
export default DetailTransaction;
|
||||||
@ -1,21 +1,13 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { useTransactionContext } from '../hooks/useTransactionContext';
|
import { useTransactionContext } from '../hooks/useTransactionContext';
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogBody } from '@/components/ui/dialog';
|
import {
|
||||||
|
Dialog,
|
||||||
interface LogType {
|
DialogContent,
|
||||||
id: string;
|
DialogHeader,
|
||||||
amount: number;
|
DialogTitle,
|
||||||
remark: string;
|
DialogBody
|
||||||
reference: string;
|
} from '@/components/ui/dialog';
|
||||||
response_date: string;
|
|
||||||
status: string;
|
|
||||||
customer?: {
|
|
||||||
username: string;
|
|
||||||
fullname: string;
|
|
||||||
};
|
|
||||||
payment_response?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type StatusCode = 'W' | 'O' | 'F' | 'D';
|
type StatusCode = 'W' | 'O' | 'F' | 'D';
|
||||||
|
|
||||||
@ -29,97 +21,171 @@ const statusMap: Record<StatusCode, StatusInfo> = {
|
|||||||
W: { label: 'Waiting Schedule', bg: 'bg-yellow-100', text: 'text-yellow-600' },
|
W: { label: 'Waiting Schedule', bg: 'bg-yellow-100', text: 'text-yellow-600' },
|
||||||
O: { label: 'On Process', bg: 'bg-blue-100', text: 'text-blue-600' },
|
O: { label: 'On Process', bg: 'bg-blue-100', text: 'text-blue-600' },
|
||||||
F: { label: 'Fail', bg: 'bg-red-100', text: 'text-red-600' },
|
F: { label: 'Fail', bg: 'bg-red-100', text: 'text-red-600' },
|
||||||
D: { label: 'Done', bg: 'bg-green-100', text: 'text-green-600' },
|
D: { label: 'Done', bg: 'bg-green-100', text: 'text-green-600' }
|
||||||
};
|
};
|
||||||
|
|
||||||
export const renderStatusBadge = (statusRaw: string | null | undefined) => {
|
export const renderStatusBadge = (statusRaw: string | null | undefined) => {
|
||||||
const status = statusRaw as StatusCode;
|
const status = statusRaw as StatusCode;
|
||||||
const { label, bg, text } = statusMap[status] ?? {
|
const { label, bg, text } = statusMap[status] ?? {
|
||||||
label: 'Unknown',
|
label: 'Unknown',
|
||||||
bg: 'bg-gray-100',
|
bg: 'bg-gray-100',
|
||||||
text: 'text-gray-600',
|
text: 'text-gray-600'
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span className={`px-2 py-1 text-xs font-semibold rounded-full ${bg} ${text}`}>
|
|
||||||
{label}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className={`px-2 py-1 text-xs font-semibold rounded-full ${bg} ${text}`}>{label}</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const TransactionLogViewer = () => {
|
const TransactionLogViewer = () => {
|
||||||
const {
|
const { showDetailLogDialog, setShowDetailLogDialog, detailLogData } = useTransactionContext();
|
||||||
showDetailLogDialog,
|
|
||||||
setShowDetailLogDialog,
|
|
||||||
detailLogData
|
|
||||||
} = useTransactionContext();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={showDetailLogDialog} onOpenChange={setShowDetailLogDialog}>
|
<Dialog open={showDetailLogDialog} onOpenChange={setShowDetailLogDialog}>
|
||||||
<DialogContent className="container-fixed max-w-[1280px] w-full h-[90vh] flex flex-col p-6 overflow-hidden">
|
<DialogContent className="container-fixed max-w-[1280px] w-full h-[90vh] flex flex-col p-6 overflow-hidden">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Detail Record</DialogTitle>
|
<DialogTitle>Detail Record</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<DialogBody >
|
<DialogBody className="overflow-y-auto max-h-[70vh] scroll-smooth scrollbar-thin scrollbar-thumb-gray-400 scrollbar-track-gray-100">
|
||||||
{/* Tab Content */}
|
{detailLogData ? (
|
||||||
{detailLogData && detailLogData != null ? (
|
<div className="py-4 overflow-y-auto">
|
||||||
<div className="py-4 overflow-y-auto">
|
<div className="space-y-4">
|
||||||
<div className="space-y-4">
|
<div className="border rounded-lg overflow-x-auto">
|
||||||
<div className="border rounded-lg overflow-x-auto">
|
<table className="min-w-full table-auto">
|
||||||
<table className='min-w-full table-auto'>
|
<tbody>
|
||||||
<tbody>
|
<tr className="border-t">
|
||||||
<tr className='border-t'>
|
<td className="px-4 py-2 text-sm text-gray-500">Log ID</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">ID</td>
|
<td className="px-4 py-2 text-sm text-gray-800">{detailLogData.id}</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{detailLogData.customer.id}</td>
|
</tr>
|
||||||
</tr>
|
{detailLogData.customer && (
|
||||||
<tr className='border-t'>
|
<>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">Username</td>
|
<tr className="border-t">
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{detailLogData.customer.username}</td>
|
<td className="px-4 py-2 text-sm text-gray-500">Customer ID</td>
|
||||||
</tr>
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
<tr className='border-t'>
|
{detailLogData.customer.id}
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">Name</td>
|
</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{detailLogData.customer.fullname}</td>
|
</tr>
|
||||||
</tr>
|
<tr className="border-t">
|
||||||
<tr className='border-t'>
|
<td className="px-4 py-2 text-sm text-gray-500">Username</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">Amount</td>
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{detailLogData.amount}</td>
|
{detailLogData.customer.username}
|
||||||
</tr>
|
</td>
|
||||||
<tr className='border-t'>
|
</tr>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">Remark</td>
|
<tr className="border-t">
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{detailLogData.remark}</td>
|
<td className="px-4 py-2 text-sm text-gray-500">Fullname</td>
|
||||||
</tr>
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
<tr className='border-t'>
|
{detailLogData.customer.fullname}
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">Payment Request</td>
|
</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500"><pre className="whitespace-pre-wrap break-words max-w-full overflow-auto bg-gray-100 p-2 rounded text-sm">{detailLogData.payment_request}</pre></td>
|
</tr>
|
||||||
</tr>
|
{detailLogData.customer.email && (
|
||||||
<tr className='border-t'>
|
<tr className="border-t">
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">Payment Response</td>
|
<td className="px-4 py-2 text-sm text-gray-500">Email</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500"><pre className="whitespace-pre-wrap break-words max-w-full overflow-auto bg-gray-100 p-2 rounded text-sm">{detailLogData.payment_response}</pre></td>
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
</tr>
|
{detailLogData.customer.email}
|
||||||
<tr className='border-t'>
|
</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">Status</td>
|
</tr>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{renderStatusBadge(detailLogData.status)}</td>
|
)}
|
||||||
</tr>
|
{detailLogData.customer.bank_name && (
|
||||||
<tr className='border-t'>
|
<tr className="border-t">
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">Prosess Date</td>
|
<td className="px-4 py-2 text-sm text-gray-500">Bank Name</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{detailLogData.request_date && moment(detailLogData.request_date).isValid() ? moment(detailLogData.request_date).format('DD/MM/YYYY HH:mm:ss') : '-'}</td>
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
</tr>
|
{detailLogData.customer.bank_name}
|
||||||
<tr className='border-t'>
|
</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">Response Date</td>
|
</tr>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{detailLogData.response_date && moment(detailLogData.response_date).isValid() ? moment(detailLogData.response_date).format('DD/MM/YYYY HH:mm:ss') : '-'}</td>
|
)}
|
||||||
</tr>
|
{detailLogData.customer.bank_account && (
|
||||||
<tr className='border-t'>
|
<tr className="border-t">
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">Reference Number</td>
|
<td className="px-4 py-2 text-sm text-gray-500">Bank Account</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{detailLogData.reference}</td>
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
</tr>
|
{detailLogData.customer.bank_account}
|
||||||
</tbody>
|
</td>
|
||||||
</table>
|
</tr>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</>
|
||||||
</div>
|
)}
|
||||||
) : (<div></div>)}
|
|
||||||
</DialogBody>
|
<tr className="border-t">
|
||||||
</DialogContent>
|
<td className="px-4 py-2 text-sm text-gray-500">Amount</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-800">{detailLogData.amount}</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-t">
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">Remark</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-800">{detailLogData.remark}</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-t">
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">Remark 1</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
|
{detailLogData.remark_1}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-t">
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">Remark 2</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
|
{detailLogData.remark_2}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-t">
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">Remark 3</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
|
{detailLogData.remark_3}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-t">
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">Reference Number</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
|
{detailLogData.reference ?? '-'}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-t">
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">Payment Request</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
|
<pre className="whitespace-pre-wrap break-words max-w-full overflow-auto bg-gray-100 p-2 rounded text-sm">
|
||||||
|
{detailLogData.payment_request ?? '-'}
|
||||||
|
</pre>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-t">
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">Payment Response</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
|
<pre className="whitespace-pre-wrap break-words max-w-full overflow-auto bg-gray-100 p-2 rounded text-sm">
|
||||||
|
{detailLogData.payment_response ?? '-'}
|
||||||
|
</pre>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-t">
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">Status</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
|
{renderStatusBadge(detailLogData.status)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-t">
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">Request Date</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
|
{detailLogData.request_date &&
|
||||||
|
moment(detailLogData.request_date).isValid()
|
||||||
|
? moment(detailLogData.request_date).format('DD/MM/YYYY HH:mm:ss')
|
||||||
|
: '-'}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-t">
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">Response Date</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-800">
|
||||||
|
{detailLogData.response_date &&
|
||||||
|
moment(detailLogData.response_date).isValid()
|
||||||
|
? moment(detailLogData.response_date).format('DD/MM/YYYY HH:mm:ss')
|
||||||
|
: '-'}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="text-center text-sm text-gray-400 py-10">No data available</div>
|
||||||
|
)}
|
||||||
|
</DialogBody>
|
||||||
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -27,23 +27,28 @@ import clsx from 'clsx';
|
|||||||
import { RefreshCw } from 'lucide-react';
|
import { RefreshCw } from 'lucide-react';
|
||||||
|
|
||||||
const API_URL = apiConfig.service_disbursement;
|
const API_URL = apiConfig.service_disbursement;
|
||||||
|
const API_URL_TRANSACTION = apiConfig.service_transaction;
|
||||||
|
interface TransferType {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
const UploadBatchDialog = () => {
|
const UploadBatchDialog = () => {
|
||||||
const parentRef = useRef<any | null>(null);
|
const parentRef = useRef<any | null>(null);
|
||||||
const { showUploadBatchDialog, handleUploadBatchDialog } = useTransactionContext();
|
const { showUploadBatchDialog, handleUploadBatchDialog } = useTransactionContext();
|
||||||
const { reload } = useDataGrid();
|
const { reload } = useDataGrid();
|
||||||
const { PostData, PostDataFile, GetData } = useCallApi();
|
const { PostDataFile, GetData } = useCallApi();
|
||||||
|
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const [alert, setAlert] = useState({
|
const [alert, setAlert] = useState({ show: false, message: '' });
|
||||||
show: false,
|
|
||||||
message: ''
|
const [transferTypes, setTransferTypes] = useState<TransferType[]>([]);
|
||||||
});
|
|
||||||
const initialState: {
|
const initialState = {
|
||||||
execution_date: string;
|
|
||||||
file: File | null;
|
|
||||||
} = {
|
|
||||||
execution_date: '',
|
execution_date: '',
|
||||||
file: null
|
file: null as File | null,
|
||||||
|
id_transaction_type: ''
|
||||||
};
|
};
|
||||||
const [formField, setFormField] = useState(initialState);
|
const [formField, setFormField] = useState(initialState);
|
||||||
|
|
||||||
@ -52,14 +57,17 @@ const UploadBatchDialog = () => {
|
|||||||
setAlert({ show: false, message: '' });
|
setAlert({ show: false, message: '' });
|
||||||
};
|
};
|
||||||
|
|
||||||
/* actions */
|
|
||||||
const doUploadBatch = useCallback(
|
const doUploadBatch = useCallback(
|
||||||
async (e: React.FormEvent<HTMLFormElement>) => {
|
async (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('execution_date', formField.execution_date);
|
const localDate = new Date(formField.execution_date);
|
||||||
|
const newDate = localDate.toISOString();
|
||||||
|
|
||||||
|
formData.append('execution_date', newDate);
|
||||||
|
formData.append('id_transaction_type', formField.id_transaction_type);
|
||||||
|
|
||||||
if (formField.file) {
|
if (formField.file) {
|
||||||
formData.append('file', formField.file);
|
formData.append('file', formField.file);
|
||||||
@ -67,9 +75,7 @@ const UploadBatchDialog = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await PostDataFile(`${API_URL}/upload-excel`, formData, {
|
const response = await PostDataFile(`${API_URL}/upload-excel`, formData, {
|
||||||
headers: {
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
'Content-Type': 'multipart/form-data'
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response?.status) {
|
if (response?.status) {
|
||||||
@ -102,26 +108,52 @@ const UploadBatchDialog = () => {
|
|||||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
console.log('Form data before submit:', formField);
|
if (
|
||||||
|
formField.execution_date.trim() === '' ||
|
||||||
if (formField.execution_date.trim() === '' || formField.file === null) {
|
formField.file === null ||
|
||||||
|
formField.id_transaction_type.trim() === ''
|
||||||
|
) {
|
||||||
setAlert({ show: true, message: 'Please fill in all required fields.' });
|
setAlert({ show: true, message: 'Please fill in all required fields.' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
doUploadBatch(e);
|
doUploadBatch(e);
|
||||||
// console.log(formField);
|
|
||||||
setAlert({ show: false, message: '' });
|
setAlert({ show: false, message: '' });
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (showUploadBatchDialog === false) {
|
if (showUploadBatchDialog === false) {
|
||||||
resetForm();
|
resetForm();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchTransferTypes = async () => {
|
||||||
|
try {
|
||||||
|
const response = await GetData(`${API_URL_TRANSACTION}/transactiontype/list`, {
|
||||||
|
limit: 100,
|
||||||
|
page: 1,
|
||||||
|
with_deleted: false,
|
||||||
|
order_field: 'id',
|
||||||
|
order_direction: 'ASC',
|
||||||
|
filter: JSON.stringify({})
|
||||||
|
});
|
||||||
|
|
||||||
|
const validTypes = ['DM', 'DA', 'DE'];
|
||||||
|
const records: TransferType[] = response?.data?.list ?? [];
|
||||||
|
|
||||||
|
const filtered = records.filter((item) => validTypes.includes(item.type));
|
||||||
|
|
||||||
|
setTransferTypes(filtered);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch transfer types', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchTransferTypes();
|
||||||
}, [showUploadBatchDialog]);
|
}, [showUploadBatchDialog]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={showUploadBatchDialog} onOpenChange={(open) => handleUploadBatchDialog(open)}>
|
<Dialog open={showUploadBatchDialog} onOpenChange={handleUploadBatchDialog}>
|
||||||
<DialogContent className="container-fixed max-w-[768px] flex flex-col p-5 overflow-hidden [&>button]:hidden">
|
<DialogContent className="container-fixed max-w-[768px] flex flex-col p-5 overflow-hidden [&>button]:hidden">
|
||||||
<DialogHeader className="p-0 border-0">
|
<DialogHeader className="p-0 border-0">
|
||||||
<DialogTitle></DialogTitle>
|
<DialogTitle></DialogTitle>
|
||||||
@ -129,7 +161,6 @@ const UploadBatchDialog = () => {
|
|||||||
<div className="flex items-center justify-between flex-wrap grow">
|
<div className="flex items-center justify-between flex-wrap grow">
|
||||||
<div className="flex flex-col justify-center">
|
<div className="flex flex-col justify-center">
|
||||||
<h1 className="text-xl font-semibold leading-none text-gray-900">Upload Batch</h1>
|
<h1 className="text-xl font-semibold leading-none text-gray-900">Upload Batch</h1>
|
||||||
<div className="flex items-center gap-2 text-sm font-normal text-gray-700"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="cursor-pointer hover:opacity-100 opacity-50"
|
className="cursor-pointer hover:opacity-100 opacity-50"
|
||||||
@ -149,7 +180,7 @@ const UploadBatchDialog = () => {
|
|||||||
<h3>{alert.message}</h3>
|
<h3>{alert.message}</h3>
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
<form action="" onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="card-body grid gap-5 p-0">
|
<div className="card-body grid gap-5 p-0">
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||||
@ -182,6 +213,31 @@ const UploadBatchDialog = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="w-full">
|
||||||
|
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||||
|
<label className="form-label flex items-center gap-1 max-w-56">
|
||||||
|
Transfer Type
|
||||||
|
</label>
|
||||||
|
<Select
|
||||||
|
required
|
||||||
|
value={formField.id_transaction_type}
|
||||||
|
onValueChange={(val) =>
|
||||||
|
setFormField((prev) => ({ ...prev, id_transaction_type: val }))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="w-full lg:w-[420px]">
|
||||||
|
<SelectValue placeholder="Select Transfer Type" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{transferTypes.map((item) => (
|
||||||
|
<SelectItem key={item.id} value={item.id}>
|
||||||
|
{item.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="flex justify-end pt-2.5">
|
<div className="flex justify-end pt-2.5">
|
||||||
<Button variant="default" type="submit" disabled={isSubmitting}>
|
<Button variant="default" type="submit" disabled={isSubmitting}>
|
||||||
{isSubmitting ? (
|
{isSubmitting ? (
|
||||||
|
|||||||
@ -36,22 +36,22 @@ interface ContextProps {
|
|||||||
// handleDetailLogDialog: (show: boolean) => void;
|
// handleDetailLogDialog: (show: boolean) => void;
|
||||||
setShowDetailLogDialog: React.Dispatch<React.SetStateAction<boolean>>;
|
setShowDetailLogDialog: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
setDetailLogData: React.Dispatch<React.SetStateAction<any | null>>;
|
setDetailLogData: React.Dispatch<React.SetStateAction<any | null>>;
|
||||||
detailLogData: any | null
|
detailLogData: any | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialProps: ContextProps = {
|
const initialProps: ContextProps = {
|
||||||
getTransactionLists: async () => ({ data: [], totalCount: 0 }),
|
getTransactionLists: async () => ({ data: [], totalCount: 0 }),
|
||||||
showDetailDialog: false,
|
showDetailDialog: false,
|
||||||
setShowDetailDialog: () => { },
|
setShowDetailDialog: () => {},
|
||||||
selectedTransactionId: null,
|
selectedTransactionId: null,
|
||||||
setSelectedTransactionId: () => { },
|
setSelectedTransactionId: () => {},
|
||||||
showUploadBatchDialog: false,
|
showUploadBatchDialog: false,
|
||||||
handleUploadBatchDialog: (show: boolean) => {},
|
handleUploadBatchDialog: (show: boolean) => {},
|
||||||
showDetailLogDialog: false,
|
showDetailLogDialog: false,
|
||||||
// handleDetailLogDialog: (show: boolean) => {},
|
// handleDetailLogDialog: (show: boolean) => {},
|
||||||
setShowDetailLogDialog: () => { },
|
setShowDetailLogDialog: () => {},
|
||||||
setDetailLogData: () => {},
|
setDetailLogData: () => {},
|
||||||
detailLogData: null,
|
detailLogData: null
|
||||||
};
|
};
|
||||||
|
|
||||||
type StatusCode = 'W' | 'P' | 'F' | 'D' | 'Y';
|
type StatusCode = 'W' | 'P' | 'F' | 'D' | 'Y';
|
||||||
@ -67,10 +67,9 @@ const statusMap: Record<StatusCode, StatusInfo> = {
|
|||||||
P: { label: 'Pending', bg: 'bg-blue-100', text: 'text-blue-600' },
|
P: { label: 'Pending', bg: 'bg-blue-100', text: 'text-blue-600' },
|
||||||
F: { label: 'Fail', bg: 'bg-red-100', text: 'text-red-600' },
|
F: { label: 'Fail', bg: 'bg-red-100', text: 'text-red-600' },
|
||||||
D: { label: 'Done', bg: 'bg-green-100', text: 'text-green-600' },
|
D: { label: 'Done', bg: 'bg-green-100', text: 'text-green-600' },
|
||||||
Y: { label: 'Active', bg: 'bg-green-100', text: 'text-green-600' },
|
Y: { label: 'Active', bg: 'bg-green-100', text: 'text-green-600' }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const ManageTransactionContext = createContext<ContextProps>(initialProps);
|
const ManageTransactionContext = createContext<ContextProps>(initialProps);
|
||||||
const API_URL = apiConfig.service_disbursement;
|
const API_URL = apiConfig.service_disbursement;
|
||||||
|
|
||||||
@ -83,7 +82,7 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
const [transaction, setTransaction] = useState<TransactionProps[]>([]);
|
const [transaction, setTransaction] = useState<TransactionProps[]>([]);
|
||||||
const { GetData } = useCallApi();
|
const { GetData } = useCallApi();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const handleUploadBatchDialog = useCallback((show: boolean) => {
|
const handleUploadBatchDialog = useCallback((show: boolean) => {
|
||||||
setShowUploadBatchDialog(show);
|
setShowUploadBatchDialog(show);
|
||||||
}, []);
|
}, []);
|
||||||
@ -100,19 +99,21 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
meta: {
|
meta: {
|
||||||
headerClassName: 'w-[250px]'
|
headerClassName: 'w-[250px]'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorFn: (row) => {
|
accessorFn: (row) => {
|
||||||
return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(row.amount);
|
return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
|
||||||
|
row.amount
|
||||||
|
);
|
||||||
},
|
},
|
||||||
id: 'amount',
|
id: 'amount',
|
||||||
header: ({ column }) => <DataGridColumnHeader title="Amount" column={column} />,
|
header: ({ column }) => <DataGridColumnHeader title="Amount" column={column} />,
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
meta: {
|
meta: {
|
||||||
headerClassName: 'w-[250px]',
|
headerClassName: 'w-[250px]'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'total_record',
|
accessorKey: 'total_record',
|
||||||
@ -121,7 +122,7 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
meta: {
|
meta: {
|
||||||
headerClassName: 'w-[250px]'
|
headerClassName: 'w-[250px]'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'total_success',
|
accessorKey: 'total_success',
|
||||||
@ -130,7 +131,7 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
meta: {
|
meta: {
|
||||||
headerClassName: 'w-[250px]'
|
headerClassName: 'w-[250px]'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'total_fail',
|
accessorKey: 'total_fail',
|
||||||
@ -139,7 +140,7 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
meta: {
|
meta: {
|
||||||
headerClassName: 'w-[250px]'
|
headerClassName: 'w-[250px]'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'total_pending',
|
accessorKey: 'total_pending',
|
||||||
@ -148,7 +149,7 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
meta: {
|
meta: {
|
||||||
headerClassName: 'w-[250px]'
|
headerClassName: 'w-[250px]'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorFn: (row) => row.status,
|
accessorFn: (row) => row.status,
|
||||||
@ -161,21 +162,19 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
const { label, bg, text } = statusMap[status] ?? {
|
const { label, bg, text } = statusMap[status] ?? {
|
||||||
label: 'Unknown',
|
label: 'Unknown',
|
||||||
bg: 'bg-gray-100',
|
bg: 'bg-gray-100',
|
||||||
text: 'text-gray-600',
|
text: 'text-gray-600'
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span className={`px-2 py-1 text-xs font-semibold rounded-full ${bg} ${text}`}>
|
||||||
className={`px-2 py-1 text-xs font-semibold rounded-full ${bg} ${text}`}
|
|
||||||
>
|
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
headerClassName: 'w-[100px]',
|
headerClassName: 'w-[100px]',
|
||||||
cellClassName: 'text-center',
|
cellClassName: 'text-center'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorFn: (row) => row.execution_date,
|
accessorFn: (row) => row.execution_date,
|
||||||
@ -183,7 +182,7 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
header: ({ column }) => <DataGridColumnHeader title="Execution Date" column={column} />,
|
header: ({ column }) => <DataGridColumnHeader title="Execution Date" column={column} />,
|
||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
cell: ({ row }) => moment(row.original.execution_date).format('DD/MM/YYYY HH:mm:ss')
|
cell: ({ row }) => moment(row.original.execution_date).format('DD/MM/YYYY HH:mm')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorFn: (row) => row.done_date,
|
accessorFn: (row) => row.done_date,
|
||||||
@ -191,7 +190,8 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
header: ({ column }) => <DataGridColumnHeader title="Done Date" column={column} />,
|
header: ({ column }) => <DataGridColumnHeader title="Done Date" column={column} />,
|
||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
cell: ({ row }) => row.original.done_date ? moment(row.original.done_date).format('DD/MM/YYYY HH:mm:ss') : ''
|
cell: ({ row }) =>
|
||||||
|
row.original.done_date ? moment(row.original.done_date).format('DD/MM/YYYY HH:mm') : ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'actions',
|
id: 'actions',
|
||||||
@ -220,7 +220,8 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[]);
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
const getTransactionLists = async (page: number, limit: number, sorting: any, filter: any) => {
|
const getTransactionLists = async (page: number, limit: number, sorting: any, filter: any) => {
|
||||||
try {
|
try {
|
||||||
@ -240,15 +241,13 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
enddate = filter[0].value.to;
|
enddate = filter[0].value.to;
|
||||||
}
|
}
|
||||||
|
|
||||||
formattedFilter = {
|
formattedFilter = {};
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await GetData(`${API_URL}/transaction/history`, {
|
const response = await GetData(`${API_URL}/transaction/history`, {
|
||||||
limit,
|
limit,
|
||||||
page: page + 1,
|
page: page + 1,
|
||||||
with_deleted: false,
|
with_deleted: false,
|
||||||
order_field: "execution_date",
|
order_field: 'execution_date',
|
||||||
order_direction: 'DESC',
|
order_direction: 'DESC',
|
||||||
filter: JSON.stringify(formattedFilter)
|
filter: JSON.stringify(formattedFilter)
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user