detail transaction disbursement
This commit is contained in:
@ -1,7 +1,10 @@
|
|||||||
|
// disbursement/history-transaction/blocks/DetailTransaction.tsx
|
||||||
|
import { KeenIcon } from '@/components';
|
||||||
import { useTransactionContext } from '../hooks/useTransactionContext';
|
import { useTransactionContext } from '../hooks/useTransactionContext';
|
||||||
import { useCallApi } from '@/hooks';
|
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 {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogBody,
|
DialogBody,
|
||||||
@ -10,10 +13,11 @@ import {
|
|||||||
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;
|
||||||
|
|
||||||
type StatusCode = 'W' | 'P' | 'F' | 'D' | 'Y';
|
type StatusCode = 'W' | 'O' | 'F' | 'D';
|
||||||
|
|
||||||
interface StatusInfo {
|
interface StatusInfo {
|
||||||
label: string;
|
label: string;
|
||||||
@ -22,11 +26,10 @@ interface StatusInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const statusMap: Record<StatusCode, StatusInfo> = {
|
const statusMap: Record<StatusCode, StatusInfo> = {
|
||||||
W: { label: 'Waiting', bg: 'bg-yellow-100', text: 'text-yellow-600' },
|
W: { label: 'Waiting Schedule', bg: 'bg-yellow-100', text: 'text-yellow-600' },
|
||||||
P: { label: 'Pending', 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' },
|
||||||
Y: { label: 'Active', bg: 'bg-green-100', text: 'text-green-600' },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const renderStatusBadge = (statusRaw: string | null | undefined) => {
|
export const renderStatusBadge = (statusRaw: string | null | undefined) => {
|
||||||
@ -49,7 +52,9 @@ const DetailTransaction = () => {
|
|||||||
const {
|
const {
|
||||||
showDetailDialog,
|
showDetailDialog,
|
||||||
setShowDetailDialog,
|
setShowDetailDialog,
|
||||||
selectedTransactionId
|
selectedTransactionId,
|
||||||
|
setShowDetailLogDialog,
|
||||||
|
setDetailLogData
|
||||||
} = useTransactionContext();
|
} = useTransactionContext();
|
||||||
|
|
||||||
const [transactionDetails, setTransactionDetails] = useState<any>(null);
|
const [transactionDetails, setTransactionDetails] = useState<any>(null);
|
||||||
@ -92,27 +97,39 @@ const DetailTransaction = () => {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr className="bg-gray-100">
|
<tr className="bg-gray-100">
|
||||||
<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">Username</th>
|
||||||
<th className="px-4 py-2 text-left text-sm text-gray-500">Name</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">Transfer Amount</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">Description</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">Process Date</th>
|
|
||||||
<th className="px-4 py-2 text-left text-sm text-gray-500">Response</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">Status</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">Invoice Number</th>
|
||||||
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Actions</th>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{transactionDetails?.log && transactionDetails?.log.length > 0 ? (
|
{transactionDetails?.log && transactionDetails?.log.length > 0 ? (
|
||||||
transactionDetails.log.map((log: { customer: any, amount: number, remark: string, reference: string, response_date: string, payment_response: string, status: string}, index: number) => (
|
transactionDetails.log.map((log: { id: number, customer: any, amount: number, remark: string, reference: string, request_date: string, payment_response: string, status: string}, index: number) => (
|
||||||
<tr key={index} className="border-t">
|
<tr key={index} className="border-t">
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{log.customer.username ?? '-'}</td>
|
<td className="px-4 py-2 text-sm text-gray-500">{log.customer.username ?? '-'}</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{log.customer.fullname ?? '-'}</td>
|
<td className="px-4 py-2 text-sm text-gray-500">{log.customer.fullname ?? '-'}</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{log.amount ?? '-'}</td>
|
<td className="px-4 py-2 text-sm text-gray-500">{log.amount ?? '-'}</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{log.remark ?? '-'}</td>
|
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{log.reference ?? '-'}</td>
|
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{log.response_date ?? '-'}</td>
|
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{log.payment_response ?? '-'}</td>
|
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{renderStatusBadge(log.status) ?? '-'}</td>
|
<td className="px-4 py-2 text-sm text-gray-500">{renderStatusBadge(log.status) ?? '-'}</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">{log.request_date && moment(log.request_date).isValid() ? moment(log.request_date).format('YYYY-MM-DD HH:mm:ss') : '-'}</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">{log.reference ?? '-'}</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">
|
||||||
|
<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>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -0,0 +1,131 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { useTransactionContext } from '../hooks/useTransactionContext';
|
||||||
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogBody } from '@/components/ui/dialog';
|
||||||
|
|
||||||
|
interface LogType {
|
||||||
|
id: string;
|
||||||
|
amount: number;
|
||||||
|
remark: string;
|
||||||
|
reference: string;
|
||||||
|
response_date: string;
|
||||||
|
status: string;
|
||||||
|
customer?: {
|
||||||
|
username: string;
|
||||||
|
fullname: string;
|
||||||
|
};
|
||||||
|
payment_response?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatusCode = 'W' | 'O' | 'F' | 'D';
|
||||||
|
|
||||||
|
interface StatusInfo {
|
||||||
|
label: string;
|
||||||
|
bg: string;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusMap: Record<StatusCode, StatusInfo> = {
|
||||||
|
W: { label: 'Waiting Schedule', bg: 'bg-yellow-100', text: 'text-yellow-600' },
|
||||||
|
O: { label: 'On Process', bg: 'bg-blue-100', text: 'text-blue-600' },
|
||||||
|
F: { label: 'Fail', bg: 'bg-red-100', text: 'text-red-600' },
|
||||||
|
D: { label: 'Done', bg: 'bg-green-100', text: 'text-green-600' },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const renderStatusBadge = (statusRaw: string | null | undefined) => {
|
||||||
|
const status = statusRaw as StatusCode;
|
||||||
|
const { label, bg, text } = statusMap[status] ?? {
|
||||||
|
label: 'Unknown',
|
||||||
|
bg: 'bg-gray-100',
|
||||||
|
text: 'text-gray-600',
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className={`px-2 py-1 text-xs font-semibold rounded-full ${bg} ${text}`}>
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const TransactionLogViewer = () => {
|
||||||
|
const {
|
||||||
|
showDetailLogDialog,
|
||||||
|
setShowDetailLogDialog,
|
||||||
|
detailLogData
|
||||||
|
} = useTransactionContext();
|
||||||
|
|
||||||
|
console.log('detailLogData: ', detailLogData)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={showDetailLogDialog} onOpenChange={setShowDetailLogDialog}>
|
||||||
|
<DialogContent className="container-fixed max-w-[1280px] w-full h-[90vh] flex flex-col p-6 overflow-hidden">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Detail Record</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogBody >
|
||||||
|
{/* Tab Content */}
|
||||||
|
{detailLogData && detailLogData != null ? (
|
||||||
|
<div className="py-4 overflow-y-auto">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="border rounded-lg overflow-x-auto">
|
||||||
|
<table className='min-w-full table-auto'>
|
||||||
|
<tbody>
|
||||||
|
<tr className='border-t'>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">ID</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">{detailLogData.customer.id}</td>
|
||||||
|
</tr>
|
||||||
|
<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">{detailLogData.customer.username}</td>
|
||||||
|
</tr>
|
||||||
|
<tr className='border-t'>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">Name</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">{detailLogData.customer.fullname}</td>
|
||||||
|
</tr>
|
||||||
|
<tr className='border-t'>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">Amount</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-gray-500">{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-500">{detailLogData.remark}</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-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 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"><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-500">{renderStatusBadge(detailLogData.status)}</td>
|
||||||
|
</tr>
|
||||||
|
<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">{detailLogData.request_date && moment(detailLogData.request_date).isValid() ? moment(detailLogData.request_date).format('YYYY-MM-DD 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-500">{detailLogData.response_date && moment(detailLogData.response_date).isValid() ? moment(detailLogData.response_date).format('YYYY-MM-DD HH:mm:ss') : '-'}</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-500">{detailLogData.reference}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (<div></div>)}
|
||||||
|
</DialogBody>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TransactionLogViewer;
|
||||||
@ -10,6 +10,7 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import DetailTransaction from '../blocks/DetailTransaction';
|
import DetailTransaction from '../blocks/DetailTransaction';
|
||||||
|
import TransactionLogViewer from '../blocks/DetailTransactionLog';
|
||||||
|
|
||||||
interface TransactionProps {
|
interface TransactionProps {
|
||||||
id: number;
|
id: number;
|
||||||
@ -31,6 +32,11 @@ interface ContextProps {
|
|||||||
setSelectedTransactionId: React.Dispatch<React.SetStateAction<number | null>>;
|
setSelectedTransactionId: React.Dispatch<React.SetStateAction<number | null>>;
|
||||||
showUploadBatchDialog: boolean;
|
showUploadBatchDialog: boolean;
|
||||||
handleUploadBatchDialog: (show: boolean) => void;
|
handleUploadBatchDialog: (show: boolean) => void;
|
||||||
|
showDetailLogDialog: boolean;
|
||||||
|
// handleDetailLogDialog: (show: boolean) => void;
|
||||||
|
setShowDetailLogDialog: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
setDetailLogData: React.Dispatch<React.SetStateAction<any | null>>;
|
||||||
|
detailLogData: any | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialProps: ContextProps = {
|
const initialProps: ContextProps = {
|
||||||
@ -41,6 +47,11 @@ const initialProps: ContextProps = {
|
|||||||
setSelectedTransactionId: () => { },
|
setSelectedTransactionId: () => { },
|
||||||
showUploadBatchDialog: false,
|
showUploadBatchDialog: false,
|
||||||
handleUploadBatchDialog: (show: boolean) => {},
|
handleUploadBatchDialog: (show: boolean) => {},
|
||||||
|
showDetailLogDialog: false,
|
||||||
|
// handleDetailLogDialog: (show: boolean) => {},
|
||||||
|
setShowDetailLogDialog: () => { },
|
||||||
|
setDetailLogData: () => {},
|
||||||
|
detailLogData: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
type StatusCode = 'W' | 'P' | 'F' | 'D' | 'Y';
|
type StatusCode = 'W' | 'P' | 'F' | 'D' | 'Y';
|
||||||
@ -67,6 +78,8 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
const [showDetailDialog, setShowDetailDialog] = useState(false);
|
const [showDetailDialog, setShowDetailDialog] = useState(false);
|
||||||
const [selectedTransactionId, setSelectedTransactionId] = useState<number | null>(null);
|
const [selectedTransactionId, setSelectedTransactionId] = useState<number | null>(null);
|
||||||
const [showUploadBatchDialog, setShowUploadBatchDialog] = useState(false);
|
const [showUploadBatchDialog, setShowUploadBatchDialog] = useState(false);
|
||||||
|
const [showDetailLogDialog, setShowDetailLogDialog] = useState(false);
|
||||||
|
const [detailLogData, setDetailLogData] = useState<any | null>(null);
|
||||||
const [transaction, setTransaction] = useState<TransactionProps[]>([]);
|
const [transaction, setTransaction] = useState<TransactionProps[]>([]);
|
||||||
const { GetData } = useCallApi();
|
const { GetData } = useCallApi();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -74,6 +87,9 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
const handleUploadBatchDialog = useCallback((show: boolean) => {
|
const handleUploadBatchDialog = useCallback((show: boolean) => {
|
||||||
setShowUploadBatchDialog(show);
|
setShowUploadBatchDialog(show);
|
||||||
}, []);
|
}, []);
|
||||||
|
// const handleDetailLogDialog = useCallback((show: boolean) => {
|
||||||
|
// setShowDetailLogDialog(show);
|
||||||
|
// }, []);
|
||||||
|
|
||||||
const columns = useMemo<ColumnDef<any>[]>(
|
const columns = useMemo<ColumnDef<any>[]>(
|
||||||
() => [
|
() => [
|
||||||
@ -262,11 +278,17 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
selectedTransactionId,
|
selectedTransactionId,
|
||||||
setSelectedTransactionId,
|
setSelectedTransactionId,
|
||||||
handleUploadBatchDialog,
|
handleUploadBatchDialog,
|
||||||
showUploadBatchDialog
|
showUploadBatchDialog,
|
||||||
|
// handleDetailLogDialog,
|
||||||
|
showDetailLogDialog,
|
||||||
|
setShowDetailLogDialog,
|
||||||
|
setDetailLogData,
|
||||||
|
detailLogData
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Toaster expand visibleToasts={9} duration={3000} />
|
<Toaster expand visibleToasts={9} duration={3000} />
|
||||||
<DetailTransaction />
|
<DetailTransaction />
|
||||||
|
<TransactionLogViewer />
|
||||||
|
|
||||||
<DataGridProvider
|
<DataGridProvider
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
import { createContext, useContext, useState } from "react";
|
||||||
|
|
||||||
|
interface LogDetail {
|
||||||
|
id: number;
|
||||||
|
username: string;
|
||||||
|
name: string;
|
||||||
|
amount: number;
|
||||||
|
remark: string;
|
||||||
|
inquiry_response: string;
|
||||||
|
payment_response: string;
|
||||||
|
response_message: string;
|
||||||
|
status: string;
|
||||||
|
process_date: string;
|
||||||
|
reference_number: string;
|
||||||
|
additional_info: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransactionDialogContextProps {
|
||||||
|
showLogDialog: boolean;
|
||||||
|
selectedLog: LogDetail | null;
|
||||||
|
setShowLogDialog: (val: boolean) => void;
|
||||||
|
setSelectedLog: (log: LogDetail | null) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TransactionDialogContext = createContext<TransactionDialogContextProps | undefined>(undefined);
|
||||||
|
|
||||||
|
export const TransactionDialogProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
const [showLogDialog, setShowLogDialog] = useState(false);
|
||||||
|
const [selectedLog, setSelectedLog] = useState<LogDetail | null>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TransactionDialogContext.Provider value={{ showLogDialog, setShowLogDialog, selectedLog, setSelectedLog }}>
|
||||||
|
{children}
|
||||||
|
</TransactionDialogContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useTransactionDialog = () => {
|
||||||
|
const context = useContext(TransactionDialogContext);
|
||||||
|
if (!context) throw new Error("useTransactionDialog must be used within TransactionDialogProvider");
|
||||||
|
return context;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user