This commit is contained in:
unknown
2025-05-06 17:10:00 +07:00
19 changed files with 588 additions and 314 deletions

Binary file not shown.

View File

@ -150,7 +150,7 @@ const Login = () => {
/>
<span className="checkbox-label">Remember me</span>
</label>
<Link
{/* <Link
to={
currentLayout?.name === 'auth-branded'
? '/auth/reset-password'
@ -159,7 +159,7 @@ const Login = () => {
className="text-2sm link shrink-0"
>
Forgot Password?
</Link>
</Link> */}
</div>
<button

View File

@ -53,7 +53,7 @@ const MemberActivity = ({ startdate, enddate }: Props) => {
const arcY = 50 - radius * Math.sin(arcAngle);
return (
<div className="p-6 bg-white rounded-lg shadow-md w-full max-w-xl">
<div className="p-6 bg-white rounded-lg shadow-md w-1/3">
<div className="flex justify-between items-center pb-3 mb-4">
<h2 className="text-lg font-semibold text-gray-700">Member Activity</h2>
</div>

View File

@ -46,7 +46,7 @@ const TransactionPieChart = ({ startdate, enddate }: Props) => {
];
return (
<div className="bg-white rounded-lg shadow-md p-6 w-full max-w-md">
<div className="bg-white rounded-lg shadow-md p-6 w-1/3">
<div className="flex justify-between items-center mb-4">
<h2 className="text-lg font-semibold text-gray-700">Transaction Chart</h2>
</div>

View File

@ -80,7 +80,7 @@ const TransactionValue = ({ startdate, enddate }: Props) => {
const maxValue = transactionData.length > 0 ? Math.max(...transactionData.map(item => item.value)) : 1;
return (
<div className="bg-white rounded-xl shadow p-6 w-full max-w-md">
<div className="bg-white rounded-xl shadow p-6 w-1/3">
<div className="flex justify-between items-center mb-4">
<h2 className="text-lg font-semibold text-gray-800">Transaction Value</h2>
</div>

View File

@ -3,6 +3,7 @@ import { useTransactionContext } from '../hooks/useTransactionContext';
import { Button } from '@/components/ui/button';
import { useCallback, useState, useEffect } from 'react';
import { toast } from 'sonner';
import { toAbsoluteUrl } from '@/utils';
const ListToolbar = () => {
const { table, reload } = useDataGrid();
@ -23,8 +24,8 @@ const ListToolbar = () => {
nextWeek.setDate(today.getDate() + 7);
settrxDate({
from: formatDate(today), // Set 'from' to today
to: formatDate(nextWeek), // Set 'to' to 7 days later
from: formatDate(today), // Set 'from' to today
to: formatDate(nextWeek) // Set 'to' to 7 days later
});
}, []);
@ -37,6 +38,15 @@ const ListToolbar = () => {
}
}, [trxDate, table]);
const handleExportData = () => {
const link = document.createElement('a');
link.href = toAbsoluteUrl('/media/file-templates/batch-sample.xlsx');
link.download = 'upload-batch-template.xlsx';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
useEffect(() => {
if (trxDate.from && trxDate.to) {
handleFilterData();
@ -55,6 +65,15 @@ const ListToolbar = () => {
>
Upload Batch
</Button>
<DefaultTooltip title={'Download Template'} placement={'top'}>
<Button onClick={handleExportData} className="h-7.5" variant={'outline'}>
<img
src={toAbsoluteUrl('/media/file-types/xls.svg')}
className="dark:hidden h-5"
alt="Export to Excel"
/>
</Button>
</DefaultTooltip>
<DefaultTooltip title={'Refresh'} placement={'top'}>
<Button variant="outline" className="h-7.5" onClick={() => reload()}>
<KeenIcon icon="arrows-circle" />

View File

@ -24,6 +24,7 @@ import { toast } from 'sonner';
import { useCallApi } from '@/hooks';
import { doSaveLogActivity } from '@/actions/GlobalActions';
import clsx from 'clsx';
import { RefreshCw } from 'lucide-react';
const API_URL = apiConfig.service_disbursement;
@ -32,17 +33,18 @@ const UploadBatchDialog = () => {
const { showUploadBatchDialog, handleUploadBatchDialog } = useTransactionContext();
const { reload } = useDataGrid();
const { PostData, PostDataFile, GetData } = useCallApi();
const [isSubmitting, setIsSubmitting] = useState(false);
const [alert, setAlert] = useState({
show: false,
message: ''
});
const initialState: {
execution_date: string,
execution_date: string;
file: File | null;
} = {
execution_date: '',
file: null
}
};
const [formField, setFormField] = useState(initialState);
const resetForm = () => {
@ -54,31 +56,33 @@ const UploadBatchDialog = () => {
const doUploadBatch = useCallback(
async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsSubmitting(true);
const formData = new FormData();
formData.append('execution_date', formField.execution_date);
if (formField.file) {
formData.append('file', formField.file);
}
try {
const response = await PostDataFile(`${API_URL}/upload-excel`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
if (response?.status) {
handleUploadBatchDialog(false);
resetForm();
reload();
const createActivity = {
module: 'Disbursement',
description: `Create New Disbursement => ${formField.file?.name}`,
action: 'C'
};
doSaveLogActivity(createActivity);
toast.success('Success Create Disbursement');
} else {
@ -88,6 +92,8 @@ const UploadBatchDialog = () => {
} catch (error) {
toast.error('Error uploading batch');
setAlert({ show: true, message: 'Something went wrong. Please try again.' });
} finally {
setIsSubmitting(false);
}
},
[formField]
@ -98,10 +104,7 @@ const UploadBatchDialog = () => {
console.log('Form data before submit:', formField);
if (
formField.execution_date.trim() === '' ||
formField.file === null
) {
if (formField.execution_date.trim() === '' || formField.file === null) {
setAlert({ show: true, message: 'Please fill in all required fields.' });
return;
}
@ -110,7 +113,7 @@ const UploadBatchDialog = () => {
// console.log(formField);
setAlert({ show: false, message: '' });
};
useEffect(() => {
if (showUploadBatchDialog === false) {
resetForm();
@ -150,7 +153,9 @@ const UploadBatchDialog = () => {
<div className="card-body grid gap-5 p-0">
<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">Execution Date</label>
<label className="form-label flex items-center gap-1 max-w-56">
Execution Date
</label>
<Input
className="input"
type="datetime-local"
@ -167,19 +172,23 @@ const UploadBatchDialog = () => {
<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">File</label>
<Input
className="input"
type="file"
autoComplete="off"
required
onChange={({ target }) =>
setFormField((prev) => ({ ...prev, file: target.files?.[0] ?? null }))
}
className="input"
type="file"
autoComplete="off"
required
onChange={({ target }) =>
setFormField((prev) => ({ ...prev, file: target.files?.[0] ?? null }))
}
/>
</div>
</div>
<div className="flex justify-end pt-2.5">
<Button className="btn btn-primary" type="submit">
Save Changes
<Button variant="default" type="submit" disabled={isSubmitting}>
{isSubmitting ? (
<RefreshCw className="animate-spin h-8 w-8 text-white mx-3" />
) : (
'Submit'
)}
</Button>
</div>
</div>

View File

@ -1,5 +1,5 @@
import { useState, useEffect } from 'react';
import { Dialog, DialogContent } from '@/components/ui/dialog';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import {
Select,
SelectContent,
@ -59,16 +59,12 @@ const EditDialog = () => {
if (response?.status) {
toast.success('Feedback reviewed successfully');
// Close the edit dialog
handleEditDialog(false, null);
// First close the detail dialog to reset its state
handleDetailDialog(false, null);
// Refresh the main data grid first
refreshData();
// Wait a tiny bit then reopen the detail with refreshed data
setTimeout(() => {
handleDetailDialog(true, selectedFeedback);
}, 300);
@ -97,7 +93,6 @@ const EditDialog = () => {
}
const data = response.data;
// console.log('Fetched data:', data);
setReviewNotes(data.review_notes || '');
@ -112,7 +107,11 @@ const EditDialog = () => {
return (
<Dialog open={showEditDialog} onOpenChange={(open) => handleEditDialog(open, null)}>
<DialogContent className="p-0 w-full sm:max-w-2xl bg-white rounded-lg overflow-hidden">
<DialogContent className="p-0 w-full sm:max-w-2xl bg-white rounded-lg overflow-hidden [&>button]:hidden">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<div className="relative">
<div className="p-6 flex items-center justify-between border-b">
<div>

View File

@ -65,7 +65,7 @@ const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialDa
const handleChange = async (e: any) => {
const { name, value } = e.target;
if (name === "file_selfie" || name === "photouser" || name === 'file_document_id' || name === "file_document_id_selfie" ||
name === "file_commercial_license" || name === 'photomerchant') { // FOR FILE ONLY
name === "file_commercial_license") { // FOR FILE ONLY
setFormData({ ...formData, [name]: e.target.files[0] });
} else if(name === "nationality") {
setFormData({ ...formData, [name]: value });
@ -258,7 +258,6 @@ const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialDa
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Merchant Address', 'merchantaddress', 'text', false, viewOnly): ''}
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Longitude Merchant', 'longitudemerchant', 'text', false, viewOnly): ''}
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Latitude Merchant', 'latitudemerchant', 'text', false, viewOnly): ''}
{(formData.id || dialogType === "create") ? generateImage(formData, handleChange, 'Photo Merchant', 'photomerchant', previewImg, setPreviewImg): ''}
{(formData.id || dialogType === "create") ? generateImage(formData, handleChange, 'File Selfie', 'file_selfie', previewImg, setPreviewImg): ''}
{(formData.id || dialogType === "create") ? generateImage(formData, handleChange, 'File Document', 'file_document_id', previewImg, setPreviewImg): ''}
{(formData.id || dialogType === "create") ? generateImage(formData, handleChange, 'File Document & Selfie', 'file_document_id_selfie', previewImg, setPreviewImg): ''}

View File

@ -10,8 +10,10 @@ import {
DialogHeader,
DialogTitle
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
const API_URL = apiConfig.transaction;
const API_URL_CURRENT_BALANCE = apiConfig.service_wallet;
const DetailApprovalTransaction = () => {
const { GetData } = useCallApi();
@ -51,6 +53,60 @@ const DetailApprovalTransaction = () => {
}
}, [showDetailDialog]);
const [currentBalance, setCurrentBalance] = useState<number | null>(null);
const [currentBalanceTransfer, setCurrentBalanceTransfer] = useState<number | null>(null);
useEffect(() => {
setCurrentBalance(null);
}, [transactionDetails]);
useEffect(() => {
setCurrentBalanceTransfer(null);
}, [transactionDetails]);
const getCurrentBallanceWallet = async (customerId: string) => {
try {
const responsecurrentbalance = await GetData(
`${API_URL_CURRENT_BALANCE}/dashboard/balance/account/${customerId}`,
{}
);
const match = responsecurrentbalance?.data?.find(
(item: any) => item.id_wallet === transactionDetails?.origin_wallet?.id
);
if (match) {
// console.log('Matched Wallet:', match); // Optional debug
return match.amount;
}
} catch (error) {
console.error('Error fetching current balance', error);
}
return null;
};
const getCurrentBallanceWalletTransfer = async (customerIdtransfer: string) => {
try {
const response = await GetData(
`${API_URL_CURRENT_BALANCE}/dashboard/balance/account/${customerIdtransfer}`,
{}
);
const match = response?.data?.find(
(item: any) => item.id_wallet === transactionDetails?.transfer?.destination_wallet?.id
);
if (match) {
// console.log('Matched Wallet:', match);
return match.amount;
}
} catch (error) {
console.error('Error fetching current balance', error);
}
return null;
};
return (
<Dialog open={showDetailDialog} onOpenChange={setShowDetailDialog}>
@ -146,14 +202,20 @@ const DetailApprovalTransaction = () => {
<div>
<p className="text-sm text-gray-500">Amount</p>
<p className="font-medium">{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(transactionDetails?.purchase?.amount) ?? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(transactionDetails?.transfer.amount)}</p>
<p className="font-medium">
{(transactionDetails?.purchase?.amount ?? transactionDetails?.transfer?.amount) != null
? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
transactionDetails.purchase?.amount ?? transactionDetails.transfer?.amount
)
: "-"}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Fee</p>
<p className="font-medium">
{transactionDetails?.kind === 'P'
? transactionDetails?.purchase.fee_amount.toLocaleString('en-US', { style: 'currency', currency: 'USD' })
: transactionDetails?.transfer.fee_amount.toLocaleString('en-US', { style: 'currency', currency: 'USD' })}
{(transactionDetails?.transfer?.fee_amount ?? transactionDetails?.purchase?.fee_amount ?? 0)
.toLocaleString('en-US', { style: 'currency', currency: 'USD' })}
</p>
</div>
<div>
@ -234,19 +296,33 @@ const DetailApprovalTransaction = () => {
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Amount</p>
<p className="font-medium">{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(transactionDetails?.purchase.amount)}</p>
<p className="font-medium">
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
transactionDetails?.purchase?.amount ?? transactionDetails?.transfer?.amount ?? 0
)}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Cashback</p>
<p className="font-medium">{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(transactionDetails?.purchase.cashback)}</p>
<p className="font-medium">
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
transactionDetails?.purchase?.cashback ?? transactionDetails?.transfer?.cashback ?? 0
)}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Cashback Point</p>
<p className="font-medium">{transactionDetails?.purchase.cashback_point}</p>
<p className="font-medium">
{transactionDetails?.purchase?.cashback_point ?? transactionDetails?.transfer?.cashback_point ?? 0}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Fee Amount</p>
<p className="font-medium">{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(transactionDetails?.purchase.fee_amount)}</p>
<p className="font-medium">
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
transactionDetails?.purchase?.fee_amount ?? transactionDetails?.transfer?.fee_amount ?? 0
)}
</p>
</div>
</div>
</div>
@ -471,7 +547,27 @@ const DetailApprovalTransaction = () => {
<p className="text-sm text-gray-500">Description</p>
<p className="font-medium">{transactionDetails?.origin_wallet.description}</p>
</div>
<div>
<p className="text-sm text-gray-500">Current Balance</p>
<p className="font-medium">
{currentBalance !== null && currentBalance !== undefined
? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(currentBalance)
: '-'}
</p>
</div>
</div>
<Button
className="btn btn-primary"
type="button"
onClick={async () => {
const balance = await getCurrentBallanceWallet(transactionDetails?.origin_customer.id);
if (balance !== null) {
setCurrentBalance(balance);
}
}}
>
Generate Current Balance
</Button>
</div>
)}
@ -482,16 +578,42 @@ const DetailApprovalTransaction = () => {
{!transactionDetails?.transfer ? (
<div className="text-center text-sm text-gray-500">No Data available</div>
) : (
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Name</p>
<p className="font-medium">{transactionDetails.transfer.destination_wallet.name}</p>
<>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Name</p>
<p className="font-medium">{transactionDetails.transfer.destination_wallet.name}</p>
</div>
<div>
<p className="text-sm text-gray-500">Description</p>
<p className="font-medium">{transactionDetails.transfer.destination_wallet.description}</p>
</div>
<div>
<p className="text-sm text-gray-500">Current Balance</p>
<p className="font-medium">
{currentBalanceTransfer !== null
? new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(currentBalanceTransfer)
: '-'}
</p>
</div>
</div>
<div>
<p className="text-sm text-gray-500">Description</p>
<p className="font-medium">{transactionDetails.transfer.destination_wallet.description}</p>
</div>
</div>
<Button
className="btn btn-primary"
type="button"
onClick={async () => {
const customerId = transactionDetails?.transfer?.destination_customer?.id;
const balance = await getCurrentBallanceWalletTransfer(customerId);
if (balance !== null) {
setCurrentBalanceTransfer(balance); // ✅ INI YANG BENAR
}
}}
>
Generate Current Balance
</Button>
</>
)}
</div>
)}
@ -540,8 +662,8 @@ const DetailApprovalTransaction = () => {
})
: ''}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.request_body}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.response_body}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.request_endpoint}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.response_body}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.request_endpoint}</td>
</tr>
))
) : (

View File

@ -40,9 +40,9 @@ const ListToolbar = () => {
// useEffect to set the default date values
useEffect(() => {
const today = new Date();
const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
// const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
settrxDate({
from: formatDate(firstDayOfMonth),
from: formatDate(today),
to: formatDate(today),
});
}, []);
@ -121,7 +121,7 @@ const ListToolbar = () => {
<KeenIcon icon="magnifier" />
<input
type="text"
placeholder="Search Transaction ID"
placeholder="Search Transaction ID, MSISDN, Or Full Name Customer"
value={searchValue}
onChange={(event) => setSearchValue(event.target.value)}
/>
@ -136,13 +136,13 @@ const ListToolbar = () => {
className="h-7.5"
onClick={() => {
const today = new Date();
const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
// const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
// Resetting all filters
setSearchValue('');
setStatusApproval('');
settrxDate({
from: formatDate(firstDayOfMonth),
from: formatDate(today),
to: formatDate(today),
});

View File

@ -270,16 +270,19 @@ const ApprovalTransactionProvider = ({ children }: { children: React.ReactNode }
});
}
// Constructing filter object with dynamic properties
const formattedFilter: any = {
"Transactions.transaction_date": {
from: `${startdate} 00:00:00`,
to: `${enddate} 23:59:59`,
},
to: `${enddate} 23:59:59`
}
};
if (transactioncode) {
formattedFilter["Transactions.code"] = { like: `%${transactioncode}%` };
formattedFilter["$or"] = [
{ "Transactions.code": { like: `%${transactioncode}%` } },
{ "Transactions.msisdn": { like: `${transactioncode}` } },
{ "origin_customer.fullname": { like: `${transactioncode}` } }
];
}
// Handle approval status filter

View File

@ -13,11 +13,15 @@ import {
DialogTitle
} from '@/components/ui/dialog';
import { useDataGrid } from '@/components';
import { Button } from '@/components/ui/button';
const API_URL = apiConfig.transaction;
const API_URL_CURRENT_BALANCE = apiConfig.service_wallet;
const DetailTransaction = () => {
const { GetData } = useCallApi();
// const { reload } = useDataGrid();
const {
showDetailDialog,
@ -67,6 +71,61 @@ const DetailTransaction = () => {
}
}, [showDetailDialog]);
const [currentBalance, setCurrentBalance] = useState<number | null>(null);
const [currentBalanceTransfer, setCurrentBalanceTransfer] = useState<number | null>(null);
useEffect(() => {
setCurrentBalance(null);
}, [transactionDetails]);
useEffect(() => {
setCurrentBalanceTransfer(null);
}, [transactionDetails]);
const getCurrentBallanceWallet = async (customerId: string) => {
try {
const responsecurrentbalance = await GetData(
`${API_URL_CURRENT_BALANCE}/dashboard/balance/account/${customerId}`,
{}
);
const match = responsecurrentbalance?.data?.find(
(item: any) => item.id_wallet === transactionDetails?.origin_wallet?.id
);
if (match) {
// console.log('Matched Wallet:', match); // Optional debug
return match.amount;
}
} catch (error) {
console.error('Error fetching current balance', error);
}
return null;
};
const getCurrentBallanceWalletTransfer = async (customerIdtransfer: string) => {
try {
const response = await GetData(
`${API_URL_CURRENT_BALANCE}/dashboard/balance/account/${customerIdtransfer}`,
{}
);
const match = response?.data?.find(
(item: any) => item.id_wallet === transactionDetails?.transfer?.destination_wallet?.id
);
if (match) {
// console.log('Matched Wallet:', match);
return match.amount;
}
} catch (error) {
console.error('Error fetching current balance', error);
}
return null;
};
return (
<Dialog open={showDetailDialog} onOpenChange={setShowDetailDialog}>
@ -162,14 +221,20 @@ const DetailTransaction = () => {
</div>
<div>
<p className="text-sm text-gray-500">Amount</p>
<p className="font-medium">{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(transactionDetails?.purchase?.amount) ?? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(transactionDetails?.transfer.amount)}</p>
<p className="font-medium">
{(transactionDetails?.purchase?.amount ?? transactionDetails?.transfer?.amount) != null
? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
transactionDetails.purchase?.amount ?? transactionDetails.transfer?.amount
)
: "-"}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Fee</p>
<p className="font-medium">
{transactionDetails?.kind === 'P'
? transactionDetails?.purchase.fee_amount.toLocaleString('en-US', { style: 'currency', currency: 'USD' })
: transactionDetails?.transfer.fee_amount.toLocaleString('en-US', { style: 'currency', currency: 'USD' })}
{(transactionDetails?.transfer?.fee_amount ?? transactionDetails?.purchase?.fee_amount ?? 0)
.toLocaleString('en-US', { style: 'currency', currency: 'USD' })}
</p>
</div>
<div>
@ -231,16 +296,6 @@ const DetailTransaction = () => {
</div>
</div>
<h3 className="font-semibold flex items-center">
Origin Wallet
<span className="ml-2 bg-blue-100 text-blue-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">
Wallet
</span>
</h3>
<div className="grid grid-cols-2 gap-4">
</div>
<h3 className="font-semibold flex items-center">
Purchase
<span className="ml-2 bg-blue-100 text-blue-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">
@ -250,69 +305,91 @@ const DetailTransaction = () => {
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Amount</p>
<p className="font-medium">{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(transactionDetails?.purchase.amount)}</p>
<p className="font-medium">
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
transactionDetails?.purchase?.amount ?? transactionDetails?.transfer?.amount ?? 0
)}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Cashback</p>
<p className="font-medium">{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(transactionDetails?.purchase.cashback)}</p>
<p className="font-medium">
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
transactionDetails?.purchase?.cashback ?? transactionDetails?.transfer?.cashback ?? 0
)}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Cashback Point</p>
<p className="font-medium">{transactionDetails?.purchase.cashback_point}</p>
<p className="font-medium">
{transactionDetails?.purchase?.cashback_point ?? transactionDetails?.transfer?.cashback_point ?? 0}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Fee Amount</p>
<p className="font-medium">{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(transactionDetails?.purchase.fee_amount)}</p>
</div>
</div>
</div>
)}
{activeTab === 'detail' && transactionDetails?.kind === 'P' && (
<div className="space-y-4">
<h3 className="font-semibold flex items-center">
Product Information
<span className="ml-2 bg-blue-100 text-blue-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">
Product Info
</span>
</h3>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Product Name</p>
<p className="font-medium">{transactionDetails?.purchase.product.name}</p>
</div>
<div>
<p className="text-sm text-gray-500">Price Cash</p>
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(transactionDetails?.purchase.product.price_cash)}
</div>
<div>
<p className="text-sm text-gray-500">Price Point</p>
<p className="font-medium">{transactionDetails?.purchase.product.price_point}</p>
</div>
<div>
<p className="text-sm text-gray-500">Product Type</p>
<p className="font-medium">{transactionDetails?.purchase.product.type}</p>
</div>
<div>
<p className="text-sm text-gray-500">Provider Name</p>
<p className="font-medium">{transactionDetails?.purchase.product.provider.description}</p>
</div>
<div>
<p className="text-sm text-gray-500">Provider Type</p>
<p className="font-medium">
{(() => {
let providertype;
if (transactionDetails?.purchase.product.provider.type === 'h2h') {
providertype = 'HOST TO HOST';
} else if (transactionDetails?.purchase.product.provider.type === 'agent') {
providertype = 'AGENT';
}
return providertype;
})()}
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
transactionDetails?.purchase?.fee_amount ?? transactionDetails?.transfer?.fee_amount ?? 0
)}
</p>
</div>
</div>
</div>
)}
{/* {activeTab === 'detail' && transactionDetails?.kind === 'P' && ( */}
<div className="space-y-4">
<h3 className="font-semibold flex items-center">
Product Information
<span className="ml-2 bg-blue-100 text-blue-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">
Product Info
</span>
</h3>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Product Name</p>
<p className="font-medium">
{transactionDetails?.purchase?.product?.name ?? "-"}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Price Cash</p>
<p className="font-medium">
{transactionDetails?.purchase?.product?.price_cash != null
? new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(transactionDetails.purchase.product.price_cash)
: "-"}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Price Point</p>
<p className="font-medium">
{transactionDetails?.purchase?.product?.price_point ?? "-"}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Product Type</p>
<p className="font-medium">
{transactionDetails?.purchase?.product?.type ?? "-"}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Provider Name</p>
<p className="font-medium">
{transactionDetails?.purchase?.product?.provider?.description ?? "-"}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Provider Type</p>
<p className="font-medium">
{transactionDetails?.purchase?.product?.provider?.type === "h2h"
? "HOST TO HOST"
: transactionDetails?.purchase?.product?.provider?.type === "agent"
? "AGENT"
: "-"}
</p>
</div>
</div>
</div>
{/* )} */}
{activeTab === 'detail' && transactionDetails?.kind != 'P' && transactionDetails?.transfer != null && (
<div className="space-y-4">
@ -348,7 +425,7 @@ const DetailTransaction = () => {
<div>
<p className="text-sm text-gray-500">Fee</p>
<p className="font-medium">
{transactionDetails?.transfer.fee_amount.toLocaleString('en-US', { style: 'currency', currency: 'USD' })}
{(transactionDetails?.transfer?.fee_amount ?? transactionDetails?.purchase?.fee_amount ?? 0).toLocaleString('en-US', { style: 'currency', currency: 'USD' })}
</p>
</div>
<div>
@ -482,7 +559,27 @@ const DetailTransaction = () => {
<p className="text-sm text-gray-500">Description</p>
<p className="font-medium">{transactionDetails?.origin_wallet.description}</p>
</div>
<div>
<p className="text-sm text-gray-500">Current Balance</p>
<p className="font-medium">
{currentBalance !== null && currentBalance !== undefined
? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(currentBalance)
: '-'}
</p>
</div>
</div>
<Button
className="btn btn-primary"
type="button"
onClick={async () => {
const balance = await getCurrentBallanceWallet(transactionDetails?.origin_customer.id);
if (balance !== null) {
setCurrentBalance(balance);
}
}}
>
Generate Current Balance
</Button>
</div>
)}
@ -493,16 +590,43 @@ const DetailTransaction = () => {
{!transactionDetails?.transfer ? (
<div className="text-sm text-gray-500">No Data available</div>
) : (
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Name</p>
<p className="font-medium">{transactionDetails.transfer.destination_wallet.name}</p>
<>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Name</p>
<p className="font-medium">{transactionDetails.transfer.destination_wallet.name}</p>
</div>
<div>
<p className="text-sm text-gray-500">Description</p>
<p className="font-medium">{transactionDetails.transfer.destination_wallet.description}</p>
</div>
<div>
<p className="text-sm text-gray-500">Current Balance</p>
<p className="font-medium">
{currentBalanceTransfer !== null
? new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(currentBalanceTransfer)
: '-'}
</p>
</div>
</div>
<div>
<p className="text-sm text-gray-500">Description</p>
<p className="font-medium">{transactionDetails.transfer.destination_wallet.description}</p>
</div>
</div>
<Button
className="btn btn-primary"
type="button"
onClick={async () => {
const customerId = transactionDetails?.transfer?.destination_customer?.id;
const balance = await getCurrentBallanceWalletTransfer(customerId);
if (balance !== null) {
setCurrentBalanceTransfer(balance); // ✅ INI YANG BENAR
}
}}
>
Generate Current Balance
</Button>
</>
)}
</div>
)}

View File

@ -61,9 +61,9 @@ const ListToolbar = () => {
useEffect(() => {
const today = new Date();
const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
// const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
settrxDate({
from: formatDate(firstDayOfMonth),
from: formatDate(today),
to: formatDate(today),
});
}, []);
@ -152,7 +152,7 @@ const ListToolbar = () => {
<KeenIcon icon="magnifier" />
<input
type="text"
placeholder="Search Transaction ID"
placeholder="Search Transaction ID, MSISDN, Or Full Name Customer"
value={searchValue}
onChange={(event) => setSearchValue(event.target.value)}
/>
@ -167,14 +167,14 @@ const ListToolbar = () => {
onClick={() => {
// Preserve trxDate values (from, to) and reset others
const today = new Date();
const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
// const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
// Only reset filters excluding from and to
setSearchValue('');
// setStatusValue('');
setTypeValue('');
settrxDate({
from: formatDate(firstDayOfMonth),
from: formatDate(today),
to: formatDate(today),
});

View File

@ -265,18 +265,19 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
});
}
// Build filter object dynamically
const formattedFilter: any = {
"Transactions.transaction_date": {
from: `${startdate} 00:00:00`,
to: `${enddate} 23:59:59`
}
};
if (transactioncode) {
formattedFilter["Transactions.code"] = {
like: `%${transactioncode}%`
};
formattedFilter["$or"] = [
{ "Transactions.code": { like: `%${transactioncode}%` } },
{ "Transactions.msisdn": { like: `${transactioncode}` } },
{ "origin_customer.fullname": { like: `${transactioncode}` } }
];
}
// if (transactionstatus) {
@ -286,7 +287,7 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
if (type) {
formattedFilter["Transactions.kind"] = type;
}
const response = await GetData(`${API_URL}/transaction/history`, {
limit,
page: page + 1,

View File

@ -11,7 +11,7 @@
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
<div className="flex justify-between w-full items-center">
<div className="flex w-[50%] gap-3 items-center">
<label className="input input-sm w-1/3">
{/* <label className="input input-sm w-1/3">
<KeenIcon icon="magnifier" />
<input
type="text"
@ -19,7 +19,7 @@
value={(table.getColumn('name')?.getFilterValue() as string) ?? ''}
onChange={(event) => table.getColumn('name')?.setFilterValue(event.target.value)}
/>
</label>
</label> */}
</div>
<div className="flex gap-3 items-center">
<Button

View File

@ -23,6 +23,29 @@ interface TransferType {
const API_URL = apiConfig.service_transaction;
const typeLabelMap: Record<string, string> = {
D: 'Disbursement',
O: 'Other',
CA: 'Change Group Emoney Customer to Agent',
AC: 'Change Group Emoney Agent to Customer',
PC: 'Change Group Point Agent to Customer',
PA: 'Change Group Point Customer to Agent',
CE: 'Return Customer Emoney',
AD: 'Return Agent Deposit',
AM: 'Return Agent Merchant',
AE: 'Return Agent Emoney',
R: 'Reward Point',
TE: 'Top Up Escrow',
TM: 'Top Up Master Agent',
TA: 'Top Up Agent',
PL: 'Purchase Loja',
DE: 'Disbursment Escrow',
DM: 'Disbursment Master Agent',
DA: 'Disbursment Agent',
WI: 'Withdraw Merchant',
IC: 'Income Merchant'
};
const ListToolbar = () => {
const { table, reload } = useDataGrid();
const { handleAddDialog } = useManageTransferTypeContext();
@ -31,68 +54,33 @@ const ListToolbar = () => {
const [searchValue, setSearchValue] = useState('');
const [statusTypes, setStatusTypes] = useState('');
const [walletOrigins, setWalletOrigin] = useState('');
const [walletDestinations, setWalletDestination] = useState('');
const [approval, setApproval] = useState('');
const unique = (arr: string[]) => Array.from(new Set(arr));
const types = unique(transferTypes.map((t) => t.type));
const origins = unique(transferTypes.map((t) => t.wallet_origin.name));
const destinations = unique(transferTypes.map((t) => t.wallet_destination.name));
const typeLabelMap: Record<string, string> = {
D: 'Disbursement',
O: 'Other',
CA: 'Change Group Emoney Customer to Agent',
AC: 'Change Group Emoney Agent to Customer',
PC: 'Change Group Point Agent to Customer',
PA: 'Change Group Point Customer to Agent',
CE: 'Return Customer Emoney',
AD: 'Return Agent Deposit',
AM: 'Return Agent Merchant',
AE: 'Return Agent Emoney',
R: 'Reward Point',
TE: 'Top Up Escrow',
TM: 'Top Up Master Agent',
TA: 'Top Up Agent',
PL: 'Purchase Loja',
DE: 'Disbursment Escrow',
DM: 'Disbursment Master Agent',
DA: 'Disbursment Agent',
WI: 'Withdraw Merchant',
IC: 'Income Merchant'
};
const approvalLabelMap: Record<string, string> = {
Y: 'Yes',
N: 'No'
};
const handleKeyDown = (event: React.KeyboardEvent) => {
if (event.key === 'Enter') {
table.getColumn('name')?.setFilterValue(searchValue);
table.setPageIndex(0);
}
};
useEffect(() => {
const timer = setTimeout(() => {
table.getColumn('name')?.setFilterValue(searchValue);
table.setPageIndex(0);
}, 200);
return () => clearTimeout(timer);
}, [searchValue]);
}, [searchValue, table]);
useEffect(() => {
table.getColumn('type')?.setFilterValue(statusTypes);
}, [statusTypes]);
useEffect(() => {
table
.getColumn('wallet_origin')
?.setFilterValue(walletOrigins === '__all__' ? '' : walletOrigins);
}, [walletOrigins]);
useEffect(() => {
table
.getColumn('wallet_destination')
?.setFilterValue(walletDestinations === '__all__' ? '' : walletDestinations);
}, [walletDestinations]);
}, [statusTypes, table]);
useEffect(() => {
table.getColumn('status_approval')?.setFilterValue(approval);
}, [approval]);
}, [approval, table]);
const fetchTransferTypes = async () => {
try {
@ -115,62 +103,89 @@ const ListToolbar = () => {
const handleClearFilter = () => {
setSearchValue('');
setStatusTypes('__all__');
setWalletOrigin('__all__');
setWalletDestination('__all__');
setApproval('__all__');
setStatusTypes('');
setApproval('');
table.getColumn('name')?.setFilterValue('');
table.getColumn('type')?.setFilterValue('');
table.getColumn('wallet_origin')?.setFilterValue('');
table.getColumn('wallet_destination')?.setFilterValue('');
table.getColumn('status_approval')?.setFilterValue('');
table.setPageIndex(0);
setTimeout(() => {
table.setPageIndex(0);
reload();
}, 0);
};
const renderSelect = (
placeholder: string,
value: string,
onChange: (val: string) => void,
options: string[],
labelMap?: Record<string, string>
) => (
<div className="min-w-[220px]">
<Select value={value === '' ? undefined : value} onValueChange={onChange}>
<SelectTrigger className="h-8 text-sm">
<SelectValue placeholder={placeholder} />
</SelectTrigger>
<SelectContent>
{options.map((opt) => (
<SelectItem key={opt} value={opt}>
{labelMap?.[opt] ?? opt}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
);
return (
<div className="card-header flex-wrap gap-2 border-b-0 px-5">
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
<div className="card-header flex-wrap border-b-0 px-5">
<div className="flex flex-wrap gap-1 w-full">
<div className="flex justify-between w-full items-center mb-3">
<div className="flex w-[34%] gap-2 items-center">
<label className="input input-sm w-full text-sm">
<KeenIcon icon="magnifier" />
<div className="flex flex-nowrap items-end gap-3 w-full overflow-x-auto">
{/* Fixed Search Input dengan icon yang selalu terlihat */}
<div className="relative w-48 shrink-0">
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 pointer-events-none ">
<KeenIcon icon="magnifier" />
</div>
<input
type="text"
placeholder="Search Transaction Type Name"
placeholder="Search Name"
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
className="input input-sm w-full pl-9 text-ellipsis overflow-hidden"
onKeyDown={handleKeyDown}
/>
</label>
</div>
<div className="min-w-[220px] shrink-0">
<Select
key={`type-filter-${statusTypes}`}
value={statusTypes}
onValueChange={setStatusTypes}
>
<SelectTrigger className="h-8">
<SelectValue placeholder="Select Status Transaction Type" className="text-ellipsis overflow-hidden" />
</SelectTrigger>
<SelectContent className="max-h-64 overflow-y-auto">
{types.map((opt) => (
<SelectItem key={opt} value={opt} className="truncate">
{typeLabelMap[opt] ?? opt}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="min-w-[220px] shrink-0">
<Select
key={`approval-filter-${approval}`}
value={approval}
onValueChange={setApproval}
>
<SelectTrigger className="h-8">
<SelectValue placeholder="Select Status Approval" className="text-ellipsis overflow-hidden" />
</SelectTrigger>
<SelectContent className="max-h-64 overflow-y-auto">
<SelectItem value="Y" className="truncate">
Yes
</SelectItem>
<SelectItem value="N" className="truncate">
No
</SelectItem>
</SelectContent>
</Select>
</div>
<DefaultTooltip title={'Reset Filter'} placement={'top'}>
<Button
variant="outline"
className="h-7.5 disabled:bg-gray-400 shrink-0"
onClick={handleClearFilter}
>
<KeenIcon icon="arrow-circle-left" />
</Button>
</DefaultTooltip>
</div>
<div className="flex gap-2 items-center">
<Button variant="outline" className="h-8 px-3 text-xs" onClick={handleClearFilter}>
Clear
</Button>
<div className="flex gap-2 items-center ml-2 shrink-0">
<Button
variant="outline"
className="h-8 px-3 text-xs"
@ -186,36 +201,9 @@ const ListToolbar = () => {
</div>
</div>
{/* Filter bar */}
<div className="flex flex-wrap gap-2 w-full">
{renderSelect(
'Select Status Transaction Type',
statusTypes,
setStatusTypes,
types,
typeLabelMap
)}
{renderSelect(
'Select Status Approval',
approval,
setApproval,
['Y', 'N'],
approvalLabelMap
)}
{/* {renderSelect('Select Origin Wallet', walletOrigins, setWalletOrigin, origins)}
{renderSelect(
'Select Destination Wallet',
walletDestinations,
setWalletDestination,
destinations
)} */}
{/* {renderSelect('Origin', walletOrigins, setWalletOrigin, origins)}
{renderSelect('Destination', walletDestinations, setWalletDestination, destinations)} */}
</div>
</div>
</div>
);
};
export default ListToolbar;
export default ListToolbar;

View File

@ -212,7 +212,7 @@ const ManageTransferTypeContextProvider = ({ children }: { children: React.React
header: ({ column }) => (
<DataGridColumnHeader title="Status Transaction Type" column={column} />
),
enableSorting: true,
enableSorting: false,
enableHiding: false,
meta: { headerClassName: 'w-[250px]' }
},
@ -241,7 +241,7 @@ const ManageTransferTypeContextProvider = ({ children }: { children: React.React
header: ({ column }) => (
<DataGridColumnHeader title="Status Kind" column={column} />
),
enableSorting: true,
enableSorting: false,
enableHiding: false,
meta: { headerClassName: 'w-[250px]' }
},

View File

@ -19,6 +19,13 @@ interface WalletProps {
name: string;
}
const getOneMonthsAgo = () => {
const today = new Date();
return new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
};
const formatDate = (date: Date): string => date.toISOString().split('T')[0];
const ListToolbar = () => {
const { table, reload } = useDataGrid();
const { GetData } = useCallApi();
@ -32,12 +39,10 @@ const ListToolbar = () => {
);
const [wallets, setWallets] = useState<WalletProps[]>([]);
const formatDate = (date: Date): string => date.toISOString().split('T')[0];
useEffect(() => {
const today = new Date();
const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
setDateRange({ from: formatDate(firstDayOfMonth), to: formatDate(today) });
const threeMonthsAgo = getOneMonthsAgo();
setDateRange({ from: formatDate(threeMonthsAgo), to: formatDate(today) });
}, []);
useEffect(() => {
@ -45,7 +50,6 @@ const ListToolbar = () => {
table.getColumn('msisdn')?.setFilterValue(searchValue);
table.setPageIndex(0);
}, 200);
return () => clearTimeout(timer);
}, [searchValue, table]);
@ -89,35 +93,50 @@ const ListToolbar = () => {
}, []);
const handleClearAllFilters = () => {
const today = new Date();
const oneMonthAgo = getOneMonthsAgo();
const resetDateRange = {
from: formatDate(oneMonthAgo),
to: formatDate(today)
};
setSearchValue('');
setWalletId('');
setDateRange(resetDateRange);
table.getColumn('msisdn')?.setFilterValue('');
table.getColumn('id_wallet')?.setFilterValue('');
table.getColumn('CreatedAt')?.setFilterValue(resetDateRange);
setTimeout(() => {
table.setPageIndex(0);
reload();
}, 0);
};
const handleRefresh = () => {
const today = new Date();
const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
setDateRange({
from: formatDate(firstDayOfMonth),
const threeMonthsAgo = getOneMonthsAgo();
const resetDateRange = {
from: formatDate(threeMonthsAgo),
to: formatDate(today)
});
};
table.getAllColumns().forEach((column) => {
if (column.id !== 'CreatedAt') {
column.setFilterValue(undefined);
}
});
setSearchValue('');
setWalletId('');
setDateRange(resetDateRange);
table.setColumnFilters([{ id: 'CreatedAt', value: resetDateRange }]);
table.setPageIndex(0);
table.getColumn('CreatedAt')?.setFilterValue({
from: formatDate(firstDayOfMonth),
to: formatDate(today)
});
reload();
};
return (
<div className="card-header flex-wrap gap-2 border-b-0 px-5">
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
<div className="flex justify-between w-full items-center">
<div className="flex gap-3 items-center flex-wrap">
<div className="flex flex-wrap items-end gap-3 w-full">
<label className="input input-sm w-[160px]">
From
<input
@ -136,24 +155,20 @@ const ListToolbar = () => {
/>
</label>
<label className="input input-sm w-1/3">
<label className="input input-sm w-[160px]">
<KeenIcon icon="magnifier" />
<input
type="text"
placeholder="Search MSISDN"
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
className="overflow-hidden text-ellipsis w-full"
/>
</label>
<div className="w-[220px]">
<Select
value={walletId}
onValueChange={(value) => {
setWalletId(value);
}}
>
<SelectTrigger>
<div className="w-[160px]">
<Select value={walletId} onValueChange={(value) => setWalletId(value)}>
<SelectTrigger className="h-[32px]">
<SelectValue placeholder="Select Wallet" />
</SelectTrigger>
<SelectContent>
@ -165,25 +180,20 @@ const ListToolbar = () => {
</SelectContent>
</Select>
</div>
<Button variant="outline" size="sm" onClick={handleClearAllFilters}>
Clear Filter
</Button>
<DefaultTooltip title={'Reset Filter'} placement={'top'}>
<Button
variant="outline"
className="h-8 disabled:bg-gray-400"
onClick={handleClearAllFilters}
>
<KeenIcon icon="arrow-circle-left" />
</Button>
</DefaultTooltip>
</div>
<div className="flex gap-3 items-center">
<DefaultTooltip title={'Refresh'} placement={'top'}>
<Button
variant="outline"
className="h-7.5"
onClick={() => {
setSearchValue('');
setWalletId('');
table.setColumnFilters((prev) => prev.filter((f) => f.id === 'CreatedAt'));
table.setPageIndex(0);
reload();
}}
>
<Button variant="outline" className="h-7.5" onClick={handleRefresh}>
<KeenIcon icon="arrows-circle" />
</Button>
</DefaultTooltip>
@ -194,4 +204,4 @@ const ListToolbar = () => {
);
};
export default ListToolbar;
export default ListToolbar;