diff --git a/src/pages/account/manage-account/ManageAccount.tsx b/src/pages/account/manage-account/ManageAccount.tsx index f116edd..402befa 100644 --- a/src/pages/account/manage-account/ManageAccount.tsx +++ b/src/pages/account/manage-account/ManageAccount.tsx @@ -93,6 +93,7 @@ const ManageAccount = () => { console.log('Callback update')} onDelete={() => console.log('Callback delete')} /> diff --git a/src/pages/disbursement/history-transaction/blocks/DetailTransaction.tsx b/src/pages/disbursement/history-transaction/blocks/DetailTransaction.tsx index dfa33ec..5f5ff7b 100644 --- a/src/pages/disbursement/history-transaction/blocks/DetailTransaction.tsx +++ b/src/pages/disbursement/history-transaction/blocks/DetailTransaction.tsx @@ -33,19 +33,19 @@ const statusMap: Record = { }; 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 ( - - {label} - - ); - }; + const status = statusRaw as StatusCode; + const { label, bg, text } = statusMap[status] ?? { + label: 'Unknown', + bg: 'bg-gray-100', + text: 'text-gray-600', + }; + + return ( + + {label} + + ); +}; const DetailTransaction = () => { const { GetData } = useCallApi(); @@ -61,7 +61,6 @@ const DetailTransaction = () => { useEffect(() => { const fetchTransactionDetails = async () => { - console.log(selectedTransactionId) if (selectedTransactionId) { try { const response = await GetData(`${API_URL}/transaction/history/${selectedTransactionId}`, { @@ -114,7 +113,7 @@ const DetailTransaction = () => { {log.customer.fullname ?? '-'} {log.amount ?? '-'} {renderStatusBadge(log.status) ?? '-'} - {log.request_date && moment(log.request_date).isValid() ? moment(log.request_date).format('YYYY-MM-DD HH:mm:ss') : '-'} + {log.request_date && moment(log.request_date).isValid() ? moment(log.request_date).format('DD/MM/YYYY HH:mm:ss') : '-'} {log.reference ?? '-'}
diff --git a/src/pages/disbursement/history-transaction/blocks/DetailTransactionLog.tsx b/src/pages/disbursement/history-transaction/blocks/DetailTransactionLog.tsx index 7b26b05..3c24c1a 100644 --- a/src/pages/disbursement/history-transaction/blocks/DetailTransactionLog.tsx +++ b/src/pages/disbursement/history-transaction/blocks/DetailTransactionLog.tsx @@ -54,10 +54,6 @@ const TransactionLogViewer = () => { detailLogData } = useTransactionContext(); - console.log('detailLogData: ', detailLogData) - - - return ( @@ -106,11 +102,11 @@ const TransactionLogViewer = () => { Prosess Date - {detailLogData.request_date && moment(detailLogData.request_date).isValid() ? moment(detailLogData.request_date).format('YYYY-MM-DD HH:mm:ss') : '-'} + {detailLogData.request_date && moment(detailLogData.request_date).isValid() ? moment(detailLogData.request_date).format('DD/MM/YYYY HH:mm:ss') : '-'} Response Date - {detailLogData.response_date && moment(detailLogData.response_date).isValid() ? moment(detailLogData.response_date).format('YYYY-MM-DD HH:mm:ss') : '-'} + {detailLogData.response_date && moment(detailLogData.response_date).isValid() ? moment(detailLogData.response_date).format('DD/MM/YYYY HH:mm:ss') : '-'} Reference Number diff --git a/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx b/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx index b1807a1..8c4862b 100644 --- a/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx +++ b/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx @@ -93,15 +93,6 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => { const columns = useMemo[]>( () => [ - // { - // accessorKey: 'transaction_date', - // header: ({ column }) => , - // enableSorting: false, - // enableHiding: false, - // meta: { - // headerClassName: 'w-[250px]' - // } - // }, { accessorKey: 'file_name', header: ({ column }) => , @@ -192,7 +183,7 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => { header: ({ column }) => , enableSorting: true, enableHiding: false, - cell: ({ row }) => moment(row.original.execution_date).format('YYYY-MM-DD HH:mm:ss') + cell: ({ row }) => moment(row.original.execution_date).format('DD/MM/YYYY HH:mm:ss') }, { accessorFn: (row) => row.done_date, @@ -200,7 +191,7 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => { header: ({ column }) => , enableSorting: true, enableHiding: false, - cell: ({ row }) => row.original.done_date ? moment(row.original.done_date).format('YYYY-MM-DD HH:mm:ss') : '' + cell: ({ row }) => row.original.done_date ? moment(row.original.done_date).format('DD/MM/YYYY HH:mm:ss') : '' }, { id: 'actions', diff --git a/src/pages/groups/ManageGroups.tsx b/src/pages/groups/ManageGroups.tsx index 7704ad1..55ae1eb 100644 --- a/src/pages/groups/ManageGroups.tsx +++ b/src/pages/groups/ManageGroups.tsx @@ -89,6 +89,10 @@ const ManageGroups = () => { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + if (!formData.groupName) return toast.warning(`Group name can not be empty!`) + if (!formData.status) return toast.warning(`Status can not be empty!`) + if (!formData.description) return toast.warning(`Description can not be empty!`) + setIsDialogOpen(false); setDialogOpen(true); }; @@ -198,7 +202,7 @@ const ManageGroups = () => {
- Create New Group + {dialogType==='create'?"Create New Group":"Update Group"} + + +
+
+ ) : (
)} + + + + ); +}; + +export default DetailDialog; + +function generateInput(formData: any, handleChange: any, label: string, name: string, type: string, required: boolean, disabled: boolean) { + function generateDate(isoString: string) { + return isoString.slice(0, 10); // "2000-01-18" + } + + type Code = 'W' | 'Y' | 'N' | 'T' | 'P' | 'D' | 'L'; + interface Reason { + label: string; + } + + const statusMap: Record = { + W: { label: 'Waiting Approval' }, + Y: { label: 'Approve' }, + N: { label: 'Reject' }, + T: { label: 'Tidak lagi menggunakan layanan' }, + P: { label: 'Privasi dan keamanan' }, + D: { label: 'Akun ganda' }, + L: { label: 'Lainnya' } + }; + if (name == 'reason_deletion' || name == 'status_approve') { + const status = formData[name] as Code + const fixStatus = statusMap[status] ?? { label: formData[name] } + formData[name] = fixStatus.label + } + return ( + <> +
+
+ + +
+
+ + ) +} \ No newline at end of file diff --git a/src/pages/members/kyc-delete-member/blocks/ListToolbar.tsx b/src/pages/members/kyc-delete-member/blocks/ListToolbar.tsx new file mode 100644 index 0000000..013a15c --- /dev/null +++ b/src/pages/members/kyc-delete-member/blocks/ListToolbar.tsx @@ -0,0 +1,61 @@ +import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components'; +import { Button } from '@/components/ui/button'; +import { useCallback, useState, useEffect } from 'react'; +import { toast } from 'sonner'; + +const ListToolbar = () => { + const { table, reload } = useDataGrid(); + + // Set the initial state for trxDate + const [trxDate, settrxDate] = useState({ from: '', to: '' }); + + // Function to format date to YYYY-MM-DD + const formatDate = (date: Date): string => { + return date.toISOString().split('T')[0]; + }; + + // useEffect to set the default date values + useEffect(() => { + const today = new Date(); + const nextWeek = new Date(today); + nextWeek.setDate(today.getDate() + 7); + + settrxDate({ + from: formatDate(today), // Set 'from' to today + to: formatDate(nextWeek), // Set 'to' to 7 days later + }); + }, []); + + const handleFilterData = useCallback(() => { + try { + table.getColumn('transaction_date')?.setFilterValue(trxDate); + } catch (error) { + toast.error('Error applying filter'); + console.error('Error applying filter:', error); + } + }, [trxDate, table]); + + useEffect(() => { + if (trxDate.from && trxDate.to) { + handleFilterData(); + } + }, [trxDate]); + + return ( +
+
+
+
+ + + +
+
+
+
+ ); +}; + +export default ListToolbar; diff --git a/src/pages/members/kyc-delete-member/hooks/ManageKycDeletionContext.tsx b/src/pages/members/kyc-delete-member/hooks/ManageKycDeletionContext.tsx new file mode 100644 index 0000000..c0742fd --- /dev/null +++ b/src/pages/members/kyc-delete-member/hooks/ManageKycDeletionContext.tsx @@ -0,0 +1,294 @@ +import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components'; +import { Toaster } from '@/components/ui/sonner'; +import { toast } from 'sonner'; +import { apiConfig } from '@/config/api.config'; +import { ColumnDef } from '@tanstack/react-table'; +import { createContext, useCallback, useMemo, useState } from 'react'; +import ListToolbar from '../blocks/ListToolbar'; +import { useCallApi } from '@/hooks'; +import moment from 'moment'; +import DetailDialog from '../blocks/DetailDialog'; + +interface ManageKycDeletionProps { + id: string; + customers_id: string; + group_id: string; + username: string; + fullname: string; + email: string; + status: string; + created_at: Date; +} + +interface ContextProps { + getKycDeletionList: ( + limit: number, + page: number, + with_deleted: boolean, + order_field: any, + order_direction: any, + filter: any + ) => Promise<{ data: ManageKycDeletionProps[]; totalCount: number } | undefined>; + showDetailDialog: boolean; + setShowDetailDialog: React.Dispatch>; + handleDetailDialog: (show: boolean, selected_user: string | null) => void; + showAddDialog: boolean; + handleAddDialog: (show: boolean) => void; + selectedIdCustomer: string | null; + detailKyc: any | null; + setDetailKyc: React.Dispatch>; + handleApproveReject: (customerDeletionId: string, status_approve: string) => {}; +} + +const initialProps: ContextProps = { + getKycDeletionList: async () => ({ data: [], totalCount: 0 }), + showDetailDialog: false, + handleDetailDialog: () => {}, + showAddDialog: false, + handleAddDialog: () => {}, + selectedIdCustomer: null, + setShowDetailDialog: () => { }, + detailKyc: async () => {}, + setDetailKyc: () => { }, + handleApproveReject: () => ({customerDeletionId: '0', status_approve: 'Y'}), +}; + +const ManageKycDeletionContext = createContext(initialProps); +const API_URL = apiConfig.service_customer; + +type StatusCode = 'W' | 'Y' | 'N' | 'T'; + +interface StatusInfo { + label: string; + bg: string; + text: string; +} + +const statusMap: Record = { + W: { label: 'Waiting Approval', bg: 'bg-yellow-100', text: 'text-yellow-600' }, + T: { label: 'No Need', bg: 'bg-blue-100', text: 'text-blue-600' }, + N: { label: 'Reject', bg: 'bg-red-100', text: 'text-red-600' }, + Y: { label: 'Approve', 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 ( + + {label} + + ); +}; + +// const { reload } = useDataGrid(); + +const ManageKycDeletionContextProvider = ({ children }: { children: React.ReactNode }) => { + const [showDetailDialog, setShowDetailDialog] = useState(false); + const [showAddDialog, setShowAddDialog] = useState(false); + const [selectedIdCustomer, setSelectedIdCustomer] = useState(null); + const [detailKyc, setDetailKyc] = useState(); + const [manageKyc, setManageKyc] = useState([]); + const { GetData, PostData } = useCallApi(); + + const getKycDeletionList = async (page: number, limit: number, sorting: any, filter: any) => { + try { + let startdate; + let enddate; + let formattedFilter; + + if (filter == undefined || filter.length == 0) { + const today = new Date(); + const nextWeek = new Date(); + nextWeek.setDate(today.getDate() + 7); + + startdate = today.toISOString().split('T')[0]; + enddate = nextWeek.toISOString().split('T')[0]; + } else if (filter != undefined || filter.length != 0) { + startdate = filter[0].value.from; + enddate = filter[0].value.to; + } + + formattedFilter = { + + }; + + const response = await GetData(`${API_URL}/customer_deletion/list`, { + limit, + page: page + 1, + with_deleted: false, + order_field: "id", + order_direction: 'DESC', + filter: JSON.stringify(formattedFilter) + }); + + setManageKyc(response?.data.list); + return { data: response?.data.list, totalCount: response?.data.total_count }; + } catch (error) { + console.error('Error fetching transaction', error); + } + }; + + const handleDetailDialog = useCallback(async (show: boolean, selected_id_customer: string | null) => { + if (show == true) { + setSelectedIdCustomer(show ? selected_id_customer : null); + let detailCustomer = await GetData(`${API_URL}/customer_deletion/detail/${selected_id_customer}`, {}) + setDetailKyc(detailCustomer?.data) + } + setShowDetailDialog(show); + }, []); + + const handleAddDialog = useCallback((show: boolean) => { + setShowAddDialog(show); + }, []); + + const handleApproveReject = useCallback(async (customerDeletionId: string, status_approve: string) => { + console.log(customerDeletionId, status_approve) + try { + let approveReject = await PostData(`${API_URL}/customer_deletion/update_status/${customerDeletionId}`, { + status_approve + }) + + if (approveReject?.status == true) { + toast.success('Success update status approval') + handleDetailDialog(false, null) + } else { + toast.warning(`${approveReject?.message}`) + handleDetailDialog(false, null) + } + } catch (error) { + toast.warning('Failed to approve/reject') + handleDetailDialog(false, null) + } + }, []) + + const columns = useMemo[]>( + () => [ + { + accessorFn: (row) => row.id, + id: 'id', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[100px]' + }, + }, + { + accessorFn: (row) => row.created_at, + id: 'created_at', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + }, + cell: ({ row }) => moment(row.original.created_at).format('DD/MM/YYYY HH:mm:ss') + }, + { + accessorFn: (row) => row.username, + id: 'username', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[350px]' + } + }, + { + accessorFn: (row) => row.fullname, + id: 'fullname', + header: ({ column }) => , + enableSorting: true, + meta: { + headerClassName: 'w-[350px]' + } + }, + { + accessorFn: (row) => row.registered_email, + id: 'email', + header: ({ column }) => , + enableSorting: true, + meta: { + headerClassName: 'w-[350px]' + } + }, + { + accessorFn: (row) => row.status_approve, + id: 'status_approve', + header: ({ column }) => , + enableSorting: true, + meta: { + headerClassName: 'w-[350px]' + }, + cell: ({row}) => renderStatusBadge(row.original.status_approve) + }, + { + id: 'actions', + enableSorting: false, + header: ({ column }) => , + cell: (data: any) => { + const row = data.row.original; + + return ( + <> + + + ); + }, + meta: { + headerClassName: 'w-[100px]', + cellClassName: 'text-center' + } + } + ], + [handleDetailDialog] + ); + + return ( + + + + + } + layout={{ card: true }} + sorting={[{ id: 'name', desc: false }]} + serverSide={true} + onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => + getKycDeletionList(pageIndex, pageSize, sorting, columnFilters) + } + > + {children} + + + ); +}; + +export { ManageKycDeletionContextProvider, ManageKycDeletionContext }; +export type { ManageKycDeletionProps }; diff --git a/src/pages/members/kyc-delete-member/hooks/index.tsx b/src/pages/members/kyc-delete-member/hooks/index.tsx new file mode 100644 index 0000000..0d10e8a --- /dev/null +++ b/src/pages/members/kyc-delete-member/hooks/index.tsx @@ -0,0 +1,2 @@ +export * from './ManageKycDeletionContext'; +export * from './useManageKycDeletionContext'; diff --git a/src/pages/members/kyc-delete-member/hooks/useManageKycDeletionContext.tsx b/src/pages/members/kyc-delete-member/hooks/useManageKycDeletionContext.tsx new file mode 100644 index 0000000..f3a20c9 --- /dev/null +++ b/src/pages/members/kyc-delete-member/hooks/useManageKycDeletionContext.tsx @@ -0,0 +1,12 @@ +import { useContext } from 'react'; +import { ManageKycDeletionContext } from './ManageKycDeletionContext'; + +const useManageKycDeletionContext = () => { + const context = useContext(ManageKycDeletionContext); + + if (!context) throw new Error('useManageKycDeletionContext must be used within AuthProvider'); + + return context; +}; + +export { useManageKycDeletionContext }; diff --git a/src/pages/members/kyc/Kyc.tsx b/src/pages/members/kyc/Kyc.tsx index 3245347..b3ecff9 100644 --- a/src/pages/members/kyc/Kyc.tsx +++ b/src/pages/members/kyc/Kyc.tsx @@ -135,7 +135,6 @@ const Kyc = () => { delete updateData.suco_name; delete updateData.aldeia_id; delete updateData.aldeia_name; - delete updateData.group_id; delete updateData.group_name; delete updateData.group_description; delete updateData.group_status; @@ -160,15 +159,16 @@ const Kyc = () => { if (updateData.isneedapproval == 1&&destinationGroup === "Premium") await axios.post(`${BASE_URL}/customer/approve`, { customerid: customerId, description: updateData.approval_description_premium}); if (updateData.isneedapproval == 1&&destinationGroup === "Agent") await axios.post(`${BASE_URL}/customer/approve`, { customerid: customerId, description: updateData.approval_description_agent}); } - await fetchCustomers(); setDialogOpen(false); setIsDialogOpen(false); toast.success(`Success Update & ${dialogType} Kyc Member`); } catch (error: any) { + if (error?.response?.data?.error) error.message = error?.response?.data?.error setDialogOpen(false); setIsDialogOpen(false); toast.error(error.message); } finally { + await fetchCustomers(); setLoading(false) } }; diff --git a/src/pages/members/manage-members/Columns.tsx b/src/pages/members/manage-members/Columns.tsx index 6c83c0b..983178e 100644 --- a/src/pages/members/manage-members/Columns.tsx +++ b/src/pages/members/manage-members/Columns.tsx @@ -180,5 +180,6 @@ export const initialMember = { aldeia: '', profession: '', approval_description_premium: '', - approval_description_agent: '' + approval_description_agent: '', + group_id: '' }; diff --git a/src/pages/members/manage-members/ManageMembers.tsx b/src/pages/members/manage-members/ManageMembers.tsx index 9710ce1..6d9cdd2 100644 --- a/src/pages/members/manage-members/ManageMembers.tsx +++ b/src/pages/members/manage-members/ManageMembers.tsx @@ -120,7 +120,6 @@ const ManageMembers = () => { delete updateData.suco_name; delete updateData.aldeia_id; delete updateData.aldeia_name; - delete updateData.group_id; delete updateData.group_name; delete updateData.group_description; delete updateData.group_status; diff --git a/src/pages/members/manage-members/blocks/AdmAccess.tsx b/src/pages/members/manage-members/blocks/AdmAccess.tsx index 0c4ceec..bf735c3 100644 --- a/src/pages/members/manage-members/blocks/AdmAccess.tsx +++ b/src/pages/members/manage-members/blocks/AdmAccess.tsx @@ -21,33 +21,33 @@ import ConfirmDialog from '@/components/confirm'; const BASE_URL_CUSTOMER = apiConfig.service_customer; // ACCESS ADM -export default function AdmAccess({page,formData,handleClose,fetchCustomers,viewOnly,setViewOnly}: any) { +export default function AdmAccess({page,groups,formData,handleClose,fetchCustomers,viewOnly,setViewOnly}: any) { const [dialogOpen, setDialogOpen] = useState(false); const [dialogType, setDialogType] = useState(''); const [changeGroup, setChangeGroup] = useState(''); const [changeGroupD, setChangeGroupD] = useState(false); - const [groups, setGroups] = useState([]); + // const [groups, setGroups] = useState([]); useEffect(() => { - fetchGroups(); + // fetchGroups(); }, []); - const fetchGroups = async () => { - try { - let getGroups = await axios.get(`${BASE_URL_CUSTOMER}/groups/list`, { - params: { - limit: 50, - page: 1, - with_deleted: false, - order_field: 'name', - order_direction: 'ASC' - } - }); - setGroups(getGroups.data.data.list); - } catch (error: any) { - toast.error(error.message); - } - }; + // const fetchGroups = async () => { + // try { + // let getGroups = await axios.get(`${BASE_URL_CUSTOMER}/groups/list`, { + // params: { + // limit: 50, + // page: 1, + // with_deleted: false, + // order_field: 'name', + // order_direction: 'ASC' + // } + // }); + // setGroups(getGroups.data.data.list); + // } catch (error: any) { + // toast.error(error.message); + // } + // }; const handleYes = async () => { try { @@ -119,6 +119,7 @@ export default function AdmAccess({page,formData,handleClose,fetchCustomers,view function btnConfirmDialog(status: boolean) { setDialogOpen(status); } + if (!formData.id) return ''; if (page !== 'kyc') { return ( diff --git a/src/pages/members/manage-members/blocks/DetailMember.tsx b/src/pages/members/manage-members/blocks/DetailMember.tsx index 8749113..4eac2e1 100644 --- a/src/pages/members/manage-members/blocks/DetailMember.tsx +++ b/src/pages/members/manage-members/blocks/DetailMember.tsx @@ -24,6 +24,7 @@ import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; const BASE_URL_MASTER_DATA = apiConfig.service_master_data; const URL_NATIONALITY = apiConfig.nationality; +const BASE_URL_CUSTOMER = apiConfig.service_customer; import { initialMember } from "../Columns"; import AdmAccess from './AdmAccess'; import CustomerWallet from './CustomerWallet'; @@ -38,10 +39,8 @@ const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialDa const [aldeias, setAldeias] = useState([]); const [postoAdm, setPostoAdm] = useState([]); const [sucos, setSucos] = useState([]); + const [groups, setGroups] = useState([]); const [genders] = useState([ { name: 'Male',id: 'M' }, { name: 'Female',id: 'F' }]) - const [status] = useState([ - { name: 'Active',id: 'Y' }, { name: 'Inactive',id: 'N' }, { name: 'Suspend PIN',id: 'P' }, { name: 'Suspend OTP',id: 'O' } - ]) const [banks] = useState([ { name: 'BNCTL',id: 'BNCTL' }, { name: 'BRI',id: 'BRI' }, { name: 'BNU',id: 'BNU' }, { name: 'Mandiri',id: 'Mandiri' } ]) @@ -53,7 +52,7 @@ const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialDa useEffect(() => { setFormData(initialData || {}); - // fetchMasterData() + fetchMasterData() }, [initialData]); const handleChange = async (e: any) => { @@ -121,48 +120,59 @@ const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialDa async function fetchMasterData() { try { - let getMunicipios = await axios.get(`${BASE_URL_MASTER_DATA}/municipios/list`, { - params: { - limit: 50, - page: 1, - with_deleted: false, - order_field: 'name', - order_direction: 'ASC', - } + let getGroups = await axios.get(`${BASE_URL_CUSTOMER}/groups/list`, { + params: { + limit: 50, + page: 1, + with_deleted: false, + order_field: 'name', + order_direction: 'ASC' + } }); - setMunicipios(getMunicipios.data.data.list) - let getPostoAdms = await axios.get(`${BASE_URL_MASTER_DATA}/postoadms/list`, { - params: { - limit: 50, - page: 1, - with_deleted: false, - order_field: 'name', - order_direction: 'ASC', - } - }); - setPostoAdm(getPostoAdms.data.data.list) - let getSucos = await axios.get(`${BASE_URL_MASTER_DATA}/sucos/list`, { - params: { - limit: 50, - page: 1, - with_deleted: false, - order_field: 'name', - order_direction: 'ASC', - } - }); - setSucos(getSucos.data.data.list) - let getAldeias = await axios.get(`${BASE_URL_MASTER_DATA}/aldeias/list`, { - params: { - limit: 50, - page: 1, - with_deleted: false, - order_field: 'name', - order_direction: 'ASC', - } - }); - setAldeias(getAldeias.data.data.list) - } catch (error) { + setGroups(getGroups.data.data.list); + // let getMunicipios = await axios.get(`${BASE_URL_MASTER_DATA}/municipios/list`, { + // params: { + // limit: 50, + // page: 1, + // with_deleted: false, + // order_field: 'name', + // order_direction: 'ASC', + // } + // }); + // setMunicipios(getMunicipios.data.data.list) + // let getPostoAdms = await axios.get(`${BASE_URL_MASTER_DATA}/postoadms/list`, { + // params: { + // limit: 50, + // page: 1, + // with_deleted: false, + // order_field: 'name', + // order_direction: 'ASC', + // } + // }); + // setPostoAdm(getPostoAdms.data.data.list) + // let getSucos = await axios.get(`${BASE_URL_MASTER_DATA}/sucos/list`, { + // params: { + // limit: 50, + // page: 1, + // with_deleted: false, + // order_field: 'name', + // order_direction: 'ASC', + // } + // }); + // setSucos(getSucos.data.data.list) + // let getAldeias = await axios.get(`${BASE_URL_MASTER_DATA}/aldeias/list`, { + // params: { + // limit: 50, + // page: 1, + // with_deleted: false, + // order_field: 'name', + // order_direction: 'ASC', + // } + // }); + // setAldeias(getAldeias.data.data.list) + } catch (error:any) { console.log(error); + toast.error(error.message) } } @@ -200,7 +210,7 @@ const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialDa handleAddDialog(open)}> - Member - View/Edit + Member - {dialogType ==='create' ? "Create" : "View/Edit"} @@ -213,20 +223,21 @@ const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialDa
- {(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Group', 'group_name', 'text', true, true): ''} + {(formData.id || dialogType === "create") ? generateList(formData, handleChange, groups, 'group_id', 'Group', true, dialogType === "update"||false): ''} + {/* {(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Group', 'group_name', 'text', true, true): ''} */} {(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Username', 'username', 'text', true, dialogType==='create'?false:true): ''} {(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Full Name', 'fullname', 'text', true, viewOnly): ''} {(formData.id || dialogType === "create") ? generateImage(formData, handleChange, 'Photo', 'photouser'): ''} - {(formData.id || dialogType === "create") ? generateList(formData, handleChange, genders, 'gender', 'Gender', null, true): ''} + {(formData.id || dialogType === "create") ? generateList(formData, handleChange, genders, 'gender', 'Gender', true, false): ''} {(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Date of Birth', 'date_birth', 'date', true, viewOnly): ''} - {(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Phone Number', 'msisdn', 'text', true, viewOnly): ''} + {(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Phone Number', 'msisdn', 'text', true, dialogType === "update"||false): ''} {(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Email', 'email', 'text', true, viewOnly): ''} {/* {generateList(formData, handleChange, status, 'status', 'Status', null, true)} */} - {(formData.id || dialogType === "create") ? generateList(formData, handleChange, profession, 'profession', 'Profession', null, false): ''} - {(formData.id || dialogType === "create") ? generateList(formData, handleChange, municipios, 'municipio', 'Municipio', null, false): ''} - {(formData.id || dialogType === "create") ? generateList(formData, handleChange, postoAdm, 'posto_adms', 'Posto', 'posto_adms_id', false): ''} - {(formData.id || dialogType === "create") ? generateList(formData, handleChange, sucos, 'suco', 'Suco', 'sucos_id', false): ''} - {(formData.id || dialogType === "create") ? generateList(formData, handleChange, aldeias, 'aldeia', 'Aldeia', null, false): ''} + {(formData.id || dialogType === "create") ? generateList(formData, handleChange, profession, 'profession', 'Profession', false, false): ''} + {(formData.id || dialogType === "create") ? generateList(formData, handleChange, municipios, 'municipio', 'Municipio', false, false): ''} + {(formData.id || dialogType === "create") ? generateList(formData, handleChange, postoAdm, 'posto_adms', 'Posto', false, false): ''} + {(formData.id || dialogType === "create") ? generateList(formData, handleChange, sucos, 'suco', 'Suco', false, false): ''} + {(formData.id || dialogType === "create") ? generateList(formData, handleChange, aldeias, 'aldeia', 'Aldeia', false, false): ''} {(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Nationality', 'nationality', 'text', true, viewOnly): ''}
@@ -257,7 +268,7 @@ const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialDa {(formData.id || dialogType === "create") ? generateImage(formData, handleChange, 'File Document & Selfie', 'file_document_id_selfie'): ''} {(formData.id || dialogType === "create") ? generateImage(formData, handleChange, 'File Commercial License', 'file_commercial_license'): ''} - {(formData.id || dialogType === "create") ? generateList(formData, handleChange, banks, 'bank_name', 'Bank Name', null, false): ''} + {(formData.id || dialogType === "create") ? generateList(formData, handleChange, banks, 'bank_name', 'Bank Name', false, false): ''} {(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Bank Account', 'bank_account', 'text', false, viewOnly): ''} {(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'iBank Number', 'ibank_number', 'text', false, viewOnly): ''} @@ -265,7 +276,7 @@ const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialDa {(formData.id&&(!page||formData.destinationGroup === "Agent")) ? generateInput(formData, handleChange, 'Approval Agent Description', 'approval_description_agent', 'text', false, viewOnly): ''} {(formData.id && page!=='kyc') ? ( - + ): ""} {(formData.id && page!=='kyc') ? ( @@ -323,14 +334,14 @@ function generateInput(formData:any, handleChange:any, label:string, name:string } // ON DEV (DI SELECT MASI HILANG) -function generateList(formData:any, handleChange: any, list:any, name:string, label: string, difId: any, required: boolean) { +function generateList(formData:any, handleChange: any, list:any, name:string, label: string, required: boolean, disabled: boolean) { return ( <>
- (handleChange({ target : { name, value: e }}))}> diff --git a/src/pages/transaction/approval-transaction/blocks/DetailApprovalTransaction.tsx b/src/pages/transaction/approval-transaction/blocks/DetailApprovalTransaction.tsx index e032a94..848eb7c 100644 --- a/src/pages/transaction/approval-transaction/blocks/DetailApprovalTransaction.tsx +++ b/src/pages/transaction/approval-transaction/blocks/DetailApprovalTransaction.tsx @@ -30,7 +30,7 @@ const DetailApprovalTransaction = () => { const response = await GetData(`${API_URL}/transaction/history/detail/${selectedTransactionId}`, { id: selectedTransactionId }); - // console.log(response?.data); + console.log(response?.data); setTransactionDetails(response?.data); } catch (error) { console.error('Error fetching transaction', error); @@ -190,7 +190,7 @@ const DetailApprovalTransaction = () => {

Name

-

{transactionDetails?.type.name}

+

{transactionDetails?.type?.name || '-'}

@@ -346,9 +346,11 @@ const DetailApprovalTransaction = () => {

Description

{transactionDetails?.description}

+

Name

-

{transactionDetails?.type.name}

+

{transactionDetails?.type?.name || '-'}

+

Reference

diff --git a/src/pages/transaction/approval-transaction/blocks/ListToolbar.tsx b/src/pages/transaction/approval-transaction/blocks/ListToolbar.tsx index 3a82db3..366530e 100644 --- a/src/pages/transaction/approval-transaction/blocks/ListToolbar.tsx +++ b/src/pages/transaction/approval-transaction/blocks/ListToolbar.tsx @@ -70,6 +70,23 @@ const ListToolbar = () => { name="to" /> + + +
diff --git a/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx b/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx index 55db0a0..6d774d7 100644 --- a/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx +++ b/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx @@ -189,7 +189,7 @@ const DetailTransaction = () => {

Name

-

{transactionDetails?.type.name}

+

{transactionDetails?.type?.name || '-'}

@@ -347,7 +347,7 @@ const DetailTransaction = () => {

Name

-

{transactionDetails?.type.name}

+

{transactionDetails?.type?.name || '-'}

Reference

diff --git a/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx b/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx index e0a15c3..7fcc065 100644 --- a/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx +++ b/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx @@ -70,6 +70,24 @@ const ListToolbar = () => { name="to" /> + + + +
diff --git a/src/pages/transfer/transferfee/blocks/AddDialog.tsx b/src/pages/transfer/transferfee/blocks/AddDialog.tsx index 45df2a9..6b73eed 100644 --- a/src/pages/transfer/transferfee/blocks/AddDialog.tsx +++ b/src/pages/transfer/transferfee/blocks/AddDialog.tsx @@ -63,14 +63,16 @@ const AddFeeDialog = () => { const parentRef = useRef(null); const { reload } = useDataGrid(); const { PostData, PutData, GetData } = useCallApi(); - const { - showAddFeeDialog, - handleAddFeeDialog, - handleEditFeeDialog, + const { + showAddFeeDialog, + handleAddFeeDialog, + handleEditFeeDialog, selectedTransferFee, transactionTypeId } = useManageTransferFeeContext(); - + const [customerSearchTerm, setCustomerSearchTerm] = useState(''); + const [open, setOpen] = useState(false); + const [alert, setAlert] = useState({ show: false, message: '' @@ -80,13 +82,13 @@ const AddFeeDialog = () => { const [customers, setCustomers] = useState([]); const [defaultCustomer, setDefaultCustomer] = useState(''); const [transactionTypeName, setTransactionTypeName] = useState(''); - + const [isLoadingTransactionType, setIsLoadingTransactionType] = useState(false); const [isLoadingWallets, setIsLoadingWallets] = useState(false); const [isLoadingCustomers, setIsLoadingCustomers] = useState(false); const customersWithNames = customers.map((customer) => ({ id: customer.id, - name: customer.username, + name: customer.username })); const initialState = { name: '', @@ -111,6 +113,7 @@ const AddFeeDialog = () => { credit_destination_account: '' }; + const [formField, setFormField] = useState(initialState); const resetForm = () => { @@ -125,15 +128,18 @@ const AddFeeDialog = () => { useEffect(() => { if (showAddFeeDialog && transactionTypeId) { setIsLoadingTransactionType(true); - - setFormField(prev => ({ + + setFormField((prev) => ({ ...prev, transaction_type: transactionTypeId })); - + const getTransactionTypeDetails = async () => { try { - const response = await GetData(`${API_URL}/transactiontype/getdata/${transactionTypeId}`, {}); + const response = await GetData( + `${API_URL}/transactiontype/getdata/${transactionTypeId}`, + {} + ); if (response?.status && response?.data) { setTransactionTypeName(response.data.name); } @@ -143,17 +149,24 @@ const AddFeeDialog = () => { setIsLoadingTransactionType(false); } }; - + getTransactionTypeDetails(); } }, [showAddFeeDialog, transactionTypeId, GetData]); - + + useEffect(() => { + if (!showAddFeeDialog) { + setCustomerSearchTerm(''); + setOpen(false); + } + }, [showAddFeeDialog]); + useEffect(() => { const created_time = new Date(); const formattedTime = created_time.toISOString().slice(0, 19).replace('T', ' '); if (showAddFeeDialog) { - setFormField(prev => ({ + setFormField((prev) => ({ ...prev, created_by: parsedUser?.username, created_at: formattedTime @@ -193,7 +206,7 @@ const AddFeeDialog = () => { page: 1, with_deleted: false, order_field: 'Wallets.name', - order_direction: 'ASC', + order_direction: 'ASC' }; try { const response = await GetData(`${API_URL_MASTER_DATA}/wallet/list`, params); @@ -209,7 +222,7 @@ const AddFeeDialog = () => { setIsLoadingWallets(false); } }, [GetData]); - + useEffect(() => { if (!showAddFeeDialog) return; fetchWallets(); @@ -231,7 +244,7 @@ const AddFeeDialog = () => { }); const customerList = response?.data.list || []; setCustomers(customerList); - + if (customerList.length > 0) { setDefaultCustomer(customerList[0].id); } @@ -250,9 +263,9 @@ const AddFeeDialog = () => { async (e: React.FormEvent) => { e.preventDefault(); setIsSubmitting(true); - + const requiredFields = [ - 'name', + 'name', 'description', 'period_start', 'period_end', @@ -265,44 +278,44 @@ const AddFeeDialog = () => { 'credit_to', 'credit_destination_account' ]; - + for (const field of requiredFields) { if (formField[field as keyof typeof formField] === '') { - setAlert({ - show: true, - message: `All required fields must be filled out. Missing: ${field.replace(/_/g, ' ')}` + setAlert({ + show: true, + message: `All required fields must be filled out. Missing: ${field.replace(/_/g, ' ')}` }); setIsSubmitting(false); return; } } - + if (formField.credit_to === 'I' && !formField.credit_destination) { - setAlert({ - show: true, - message: 'Credit destination is required when Input Customer is selected' + setAlert({ + show: true, + message: 'Credit destination is required when Input Customer is selected' }); setIsSubmitting(false); return; } setAlert({ show: false, message: '' }); - + const payload = { ...formField }; - + if (formField.credit_to !== 'I') { payload.credit_destination = '00000000-0000-0000-0000-000000000000'; } - + try { const response = await PostData(`${API_URL}/transactionfees/create`, payload); - + if (response?.status) { toast.success('Successfully created transfer fee'); reload(); resetForm(); handleAddFeeDialog(false); - + const createActivity = { module: 'Manage Transfer Fee', description: `Create Transfer Fee => ${formField.name}`, @@ -323,18 +336,14 @@ const AddFeeDialog = () => { ); const renderSelectWithLoading = ( - value: string, + value: string, onChangeHandler: (value: string) => void, - options: {id: string, name: string}[] | null, + options: { id: string; name: string }[] | null, placeholder: string, isLoading: boolean ) => { return ( - {isLoading ? (
@@ -346,11 +355,12 @@ const AddFeeDialog = () => { )} - {options && options.map((option) => ( - - {option.name} - - ))} + {options && + options.map((option) => ( + + {option.name} + + ))} ); @@ -389,7 +399,9 @@ const AddFeeDialog = () => {
- + {transactionTypeId ? (
{ formField.transaction_type, (transaction_type) => setFormField((prev) => ({ ...prev, transaction_type })), transactionTypes, - "Select Transaction Type", + 'Select Transaction Type', isLoadingTransactionType ) )}
- + { />
- + { />
- + { />
- + { />
- +
- + {renderSelectWithLoading( formField.deduct_from_account, (value) => setFormField({ ...formField, deduct_from_account: value }), wallets, - "Select Wallet", + 'Select Wallet', isLoadingWallets )}
- + setCustomerSearchTerm(e.target.value)} + autoComplete="off" + onClick={(e) => e.stopPropagation()} + autoFocus + /> +
+
+ {customers + .filter( + (customer) => + customer.username + .toLowerCase() + .includes(customerSearchTerm.toLowerCase()) || + customer.msisdn.includes(customerSearchTerm) + ) + .map((customer) => ( +
{ + setFormField({ + ...formField, + credit_destination: customer.id + }); + setOpen(false); + }} + > + {customer.username} +
+ ))} + {customers.filter( + (customer) => + customer.username + .toLowerCase() + .includes(customerSearchTerm.toLowerCase()) || + customer.msisdn.includes(customerSearchTerm) + ).length === 0 && ( +
+ No customer found +
+ )} +
+
+ )} +
)}
- + {renderSelectWithLoading( formField.credit_destination_account, (value) => setFormField({ ...formField, credit_destination_account: value }), wallets, - "Select Wallet", + 'Select Wallet', isLoadingWallets )}
- +
- +
- + + + {isLoading ? ( +
+
+ Loading... +
+ ) : ( + + )} +
+ + {options && + options.map((option) => ( + + {option.name} + + ))} + + + ); + }; return ( - { - if (!open) { - handleCloseDialog(); - } - }}> + { + if (!open) { + handleCloseDialog(); + } + }} + > @@ -349,328 +422,421 @@ const EditFeeDialog = () => {
)} -
-
-
-
- - - setFormField((prev) => ({ ...prev, name: target.value })) - } - /> + {isLoadingTransferFee ? ( +
+
+
+
+
+
+
+
- -
- - - setFormField((prev) => ({ ...prev, description: target.value })) - } - /> -
- -
- - { - setFormField((prev) => ({ - ...prev, - minimum_amount: values.floatValue || 0 - })); - }} - placeholder="Enter Minimum Amount" - /> -
- -
- - { - setFormField((prev) => ({ - ...prev, - maximum_amount: values.floatValue || 0 - })); - }} - placeholder="Enter Maximum Amount" - /> -
- -
- - - setFormField((prev) => ({ ...prev, period_start: target.value })) - } - /> -
- -
- - - setFormField((prev) => ({ ...prev, period_end: target.value })) - } - /> -
- -
- - { - setFormField((prev) => ({ - ...prev, - deduct_amount: values.floatValue || 0 - })); - }} - placeholder="Enter Deduct Amount" - /> -
- -
- - { - setFormField((prev) => ({ - ...prev, - deduct_percentage: values.floatValue || 0 - })); - }} - placeholder="Enter Deduct Percentage" - /> -
- -
- - { - setFormField((prev) => ({ - ...prev, - fee_amount: values.floatValue || 0 - })); - }} - placeholder="Enter Fee Amount" - /> -
- -
- - -
- -
- - -
- -
- - -
- - {formField.credit_to === 'I' && ( +
+

Loading transfer fee details...

+
+ ) : ( + +
+
- + +
+ + {isLoadingTransactionType && ( +
+
+
+ Loading transaction type... +
+
+ )} + +
+
+ +
+ + + setFormField((prev) => ({ ...prev, name: target.value })) + } + /> +
+ +
+ + + setFormField((prev) => ({ ...prev, description: target.value })) + } + /> +
+ +
+ + { + setFormField((prev) => ({ + ...prev, + minimum_amount: values.floatValue || 0 + })); + }} + placeholder="Enter Minimum Amount" + /> +
+ +
+ + { + setFormField((prev) => ({ + ...prev, + maximum_amount: values.floatValue || 0 + })); + }} + placeholder="Enter Maximum Amount" + /> +
+ +
+ + + setFormField((prev) => ({ ...prev, period_start: target.value })) + } + /> +
+ +
+ + + setFormField((prev) => ({ ...prev, period_end: target.value })) + } + /> +
+ +
+ + { + setFormField((prev) => ({ + ...prev, + deduct_amount: values.floatValue || 0 + })); + }} + placeholder="Enter Deduct Amount" + /> +
+ +
+ + { + setFormField((prev) => ({ + ...prev, + deduct_percentage: values.floatValue || 0 + })); + }} + placeholder="Enter Deduct Percentage" + /> +
+ +
+ + { + setFormField((prev) => ({ + ...prev, + fee_amount: values.floatValue || 0 + })); + }} + placeholder="Enter Fee Amount" + /> +
+ +
+
- )} - -
- - + +
+ + {renderSelectWithLoading( + formField.deduct_from_account, + (value) => setFormField({ ...formField, deduct_from_account: value }), + wallets, + 'Select Wallet', + isLoadingWallets + )} +
+ +
+ + +
+ + {formField.credit_to === 'I' && ( +
+ +
+
setOpen(!open)} + > + + {customers.find(customer => customer.id === formField.credit_destination)?.username || 'Search customer...'} + + +
+ + {open && ( +
+
+ setCustomerSearchTerm(e.target.value)} + autoComplete="off" + onClick={(e) => e.stopPropagation()} + autoFocus + /> +
+
+ {customers + .filter(customer => + customer.username.toLowerCase().includes(customerSearchTerm.toLowerCase()) || + customer.msisdn.includes(customerSearchTerm) + ) + .map(customer => ( +
{ + setFormField({ + ...formField, + credit_destination: customer.id + }); + setOpen(false); + }} + > + {customer.username}
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- + ))} + {customers.filter(customer => + customer.username.toLowerCase().includes(customerSearchTerm.toLowerCase()) || + customer.msisdn.includes(customerSearchTerm) + ).length === 0 && ( +
No customer found
+ )} +
+
+ )} +
+
+)} + + + + +
+ + {renderSelectWithLoading( + formField.credit_destination_account, + (value) => setFormField({ ...formField, credit_destination_account: value }), + wallets, + 'Select Wallet', + isLoadingWallets + )} +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
-
- + + )}
); }; -export { EditFeeDialog }; \ No newline at end of file +export {EditFeeDialog}; \ No newline at end of file diff --git a/src/pages/transfer/transferfee/hooks/ManageTransferFeeContext.tsx b/src/pages/transfer/transferfee/hooks/ManageTransferFeeContext.tsx index 15b650f..9d78802 100644 --- a/src/pages/transfer/transferfee/hooks/ManageTransferFeeContext.tsx +++ b/src/pages/transfer/transferfee/hooks/ManageTransferFeeContext.tsx @@ -1,391 +1,400 @@ - import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components'; - import { Toaster } from '@/components/ui/sonner'; - import { apiConfig } from '@/config/api.config'; - import { useCallApi } from '@/hooks'; - import { ColumnDef } from '@tanstack/react-table'; - import { createContext, useCallback, useEffect, useMemo, useState } from 'react'; - import ListToolbar from '../blocks/ListToolBar'; - import DeleteDialog from '../blocks/DeleteDialog'; - import { EditFeeDialog } from '../blocks/EditDialog'; - import { useManageTransferTypeContext } from '../../transfertype/hooks/useManageTransferTypeContext'; +import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components'; +import { Toaster } from '@/components/ui/sonner'; +import { apiConfig } from '@/config/api.config'; +import { useCallApi } from '@/hooks'; +import { ColumnDef } from '@tanstack/react-table'; +import { createContext, useCallback, useEffect, useMemo, useState } from 'react'; +import ListToolbar from '../blocks/ListToolBar'; +import DeleteDialog from '../blocks/DeleteDialog'; +import { EditFeeDialog } from '../blocks/EditDialog'; +import { useManageTransferTypeContext } from '../../transfertype/hooks/useManageTransferTypeContext'; - interface ContextProps { - showEditFeeDialog: boolean; - handleEditFeeDialog: (show: boolean, selected_TransferFee: string | null) => void; - showAddFeeDialog: boolean; - handleAddFeeDialog: (show: boolean) => void; - handleDeleteFeeDialog: (show: boolean, selected_TransferFee: string | null) => void; - showDeleteFeeDialog: boolean; - selectedTransferFee: string | null; - transactionTypeId: string | null; +interface ContextProps { + showEditFeeDialog: boolean; + handleEditFeeDialog: (show: boolean, selected_TransferFee: string | null) => void; + showAddFeeDialog: boolean; + handleAddFeeDialog: (show: boolean) => void; + handleDeleteFeeDialog: (show: boolean, selected_TransferFee: string | null) => void; + showDeleteFeeDialog: boolean; + selectedTransferFee: string | null; + transactionTypeId: string | null; +} - } +const initialProps: ContextProps = { + showEditFeeDialog: false, + handleEditFeeDialog: () => {}, + showAddFeeDialog: false, + handleAddFeeDialog: () => {}, + showDeleteFeeDialog: false, + handleDeleteFeeDialog: () => {}, + selectedTransferFee: null, + transactionTypeId: null +}; - const initialProps: ContextProps = { - showEditFeeDialog: false, - handleEditFeeDialog: () => {}, - showAddFeeDialog: false, - handleAddFeeDialog: () => {}, - showDeleteFeeDialog: false, - handleDeleteFeeDialog: () => {}, - selectedTransferFee: null, - transactionTypeId: null, +interface TransferFeeProps { + name: string; + description: string; + minimum_amount: number; + maximum_amount: number; + period_start: string; + period_end: string; + deduct_amount: number; + deduct_percentage: number; + transaction_type: string; + status: string; + status_include: string; + fee: number; +} - }; +const ManageTransferFeeContext = createContext(initialProps); +const API_URL = apiConfig.service_transaction; - interface TransferFeeProps { - name: string; - description: string; - minimum_amount: number; - maximum_amount:number; - period_start:string; - period_end:string; - deduct_amount: number; - deduct_percentage: number; - transaction_type: string; - status: string; - status_include: string; - fee: number; - } +const ManageTransferFeeContextProvider = ({ + children, + transactionTypeId = null +}: { + children: React.ReactNode; + transactionTypeId?: string | null; +}) => { + const [showEditFeeDialog, setShowEditFeeDialog] = useState(false); + const [showAddFeeDialog, setShowAddFeeDialog] = useState(false); + const [showDeleteFeeDialog, setShowDeleteFeeDialog] = useState(false); + const { showAddDialog, handleAddDialog, selectedTransferType } = useManageTransferTypeContext(); + const [selectedTransferFee, setSelectedTransferFee] = useState(null); + const { GetData } = useCallApi(); + const handleEditFeeDialog = useCallback((show: boolean, selected_TransferFee: string | null) => { + setSelectedTransferFee(show ? selected_TransferFee : null); + setShowEditFeeDialog(show); + }, []); - const ManageTransferFeeContext = createContext(initialProps); - const API_URL = apiConfig.service_transaction; + const handleAddFeeDialog = useCallback((show: boolean) => { + setShowAddFeeDialog(show); + }, []); - const ManageTransferFeeContextProvider = ({ children, transactionTypeId = null }: { children: React.ReactNode; transactionTypeId?: string | null }) => { - const [showEditFeeDialog, setShowEditFeeDialog] = useState(false); - const [showAddFeeDialog, setShowAddFeeDialog] = useState(false); - const [showDeleteFeeDialog, setShowDeleteFeeDialog] = useState(false); - const { showAddDialog, handleAddDialog, selectedTransferType } = useManageTransferTypeContext(); - const [selectedTransferFee, setSelectedTransferFee] = useState(null); - const { GetData } = useCallApi(); - const handleEditFeeDialog = useCallback((show: boolean, selected_TransferFee: string | null) => { - setSelectedTransferFee(show ? selected_TransferFee : null); - setShowEditFeeDialog(show); - }, []); - - const handleAddFeeDialog = useCallback((show: boolean) => { - setShowAddFeeDialog(show); - }, []); - - const handleDeleteFeeDialog = useCallback((show: boolean, selected_TransferFee: string | null) => { + const handleDeleteFeeDialog = useCallback( + (show: boolean, selected_TransferFee: string | null) => { setSelectedTransferFee(show ? selected_TransferFee : null); setShowDeleteFeeDialog(show); - }, []); - const doGetTransferFeeListData = async ( - page: number, - limit: number, - sorting: any, - filter: any - ) => { - if (!selectedTransferType) { - return { data: [], totalCount: 0 }; - } - - sorting = sorting.length === 0 ? [{ id: 'id', desc: false }] : sorting; - filter = filter.length === 0 ? {} : { any: filter[0].value.toLowerCase() }; - - try { - const response = await GetData(`${API_URL}/transactionfees/getdatabytransactiontype/${selectedTransferType}`, {}); - - // console.log('API Response:', response?.data); - - return { - data: response?.data , - totalCount: 1 - }; - } catch (error) { - console.error("Error fetching transaction fees by transaction type:", error); - return { data: [], totalCount: 0 }; - } - }; - - const columns = useMemo[]>( - () => [ - { - accessorFn: (row) => row.name, - id: 'name', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[200px]' } - }, - { - accessorFn: (row) => row.description, - id: 'description', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[250px]' } - }, - { - accessorFn: (row) => row.minimum_amount, - id: 'minimum_amount', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[150px]' } - }, - { - accessorFn: (row) => row.maximum_amount, - id: 'maximum_amount', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[150px]' } - }, - { - accessorFn: (row) => row.fee_amount, - id: 'fee_amount', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[150px]' } - }, - { - accessorFn: (row) => row.deduct_amount, - id: 'deduct_amount', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[150px]' } - }, - { - accessorFn: (row) => row.deduct_percentage, - id: 'deduct_percentage', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[150px]' } - }, - - { - accessorFn: (row) => row.period_start?.split('T')[0], - id: 'period_start', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[200px]' } - }, - { - accessorFn: (row) => row.period_end?.split('T')[0], - id: 'period_end', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[200px]' } - }, - { - accessorFn: (row) => row.transaction_type?.name, - id: 'transactionTypeId', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[250px]' } - }, - { - accessorFn: (row: { credit_to: string }) => { - const mapping: Record = { - D: 'Destination Member', - S: 'Source Member', - I: 'Input Customer' - }; - - return mapping[row.credit_to] || 'Unknown'; - }, - id: 'credit_to', - header: ({ column }) => ( - - ), - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[250px]' } - }, - { - accessorFn: (row) => { - if (row.credit_to === 'S' || row.credit_to === 'D' || !row.credit_destination) { - return 'N/A'; - } - return row.credit_destination?.fullname; - }, - id: 'credit_destination', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[250px]' } - }, - { - accessorFn: (row) => row.credit_destination_account?.description, - id: 'credit_destination_account', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[250px]' } - }, - { - accessorFn: (row: { deduct_from: string }) => { - const mapping: Record = { - D: 'Destination Member', - S: 'Source Member', - }; - - return mapping[row.deduct_from]; - }, - id: 'deduct_from', - header: ({ column }) => ( - - ), - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[250px]' } - }, - { - accessorFn: (row) => row.deduct_from_account?.description, - id: 'deduct_from_account', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[250px]' } - }, - { - accessorFn: (row) => row.priority, - id: 'priority', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - cell: ({ row }) => { - const isActive = row.original.priority === 'Y'; - - return ( - - {isActive ? 'Yes' : 'No'} - - ); - }, - meta: { - headerClassName: 'w-[100px]', - cellClassName: 'text-center' - } - }, - { - accessorFn: (row) => row.status, - id: 'status', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - cell: ({ row }) => { - const isActive = row.original.status === 'Y'; + }, + [] + ); + const doGetTransferFeeListData = async ( + page: number, + limit: number, + sorting: any, + filter: any + ) => { + if (!selectedTransferType) { + return { data: [], totalCount: 0 }; + } - return ( - - {isActive ? 'Active' : 'Inactive'} - - ); - }, - meta: { - headerClassName: 'w-[100px]', - cellClassName: 'text-center' - } - }, - { - accessorFn: (row) => row.status_include, - id: 'status_include', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - cell: ({ row }) => { - const isActive = row.original.status_include === 'Y'; + sorting = sorting.length === 0 ? [{ id: 'id', desc: false }] : sorting; + filter = filter.length === 0 ? {} : { any: filter[0].value.toLowerCase() }; - return ( - - {isActive ? 'Yes' : 'No'} - - ); - }, - meta: { - headerClassName: 'w-[100px]', - cellClassName: 'text-center' - } - }, - { - id: 'actions', - header: ({ column }) => , - enableSorting: false, - enableHiding: false, - cell: (data) => { - const row = data.row.original; - return ( - <> - - - - ); - }, - meta: { - headerClassName: 'w-[100px]', - cellClassName: 'text-center' - } - } - ], - [handleEditFeeDialog, handleDeleteFeeDialog] - ); + try { + const response = await GetData( + `${API_URL}/transactionfees/getdatabytransactiontype/${selectedTransferType}`, + {} + ); - return ( -
-
-

Manage Transaction Fee

-
- - - } - sorting={[{ id: 'id', desc: true }]} - serverSide={true} - onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => - doGetTransferFeeListData(pageIndex, pageSize, sorting, columnFilters) - } - > - {children} + // console.log('API Response:', response?.data); - - -
- ); + return { + data: response?.data, + totalCount: 1 + }; + } catch (error) { + console.error('Error fetching transaction fees by transaction type:', error); + return { data: [], totalCount: 0 }; + } }; - export { ManageTransferFeeContext, ManageTransferFeeContextProvider }; + const columns = useMemo[]>( + () => [ + { + accessorFn: (row) => row.name, + id: 'name', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[200px]' } + }, + { + accessorFn: (row) => row.description, + id: 'description', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[250px]' } + }, + { + accessorFn: (row) => row.minimum_amount, + id: 'minimum_amount', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[150px]' } + }, + { + accessorFn: (row) => row.maximum_amount, + id: 'maximum_amount', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[150px]' } + }, + { + accessorFn: (row) => row.fee_amount, + id: 'fee_amount', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[150px]' } + }, + { + accessorFn: (row) => row.deduct_amount, + id: 'deduct_amount', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[150px]' } + }, + { + accessorFn: (row) => row.deduct_percentage, + id: 'deduct_percentage', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[150px]' } + }, + + { + accessorFn: (row) => row.period_start?.split('T')[0], + id: 'period_start', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[200px]' } + }, + { + accessorFn: (row) => row.period_end?.split('T')[0], + id: 'period_end', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[200px]' } + }, + { + accessorFn: (row) => row.transaction_type?.name, + id: 'transactionTypeId', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[250px]' } + }, + { + accessorFn: (row: { credit_to: string }) => { + const mapping: Record = { + D: 'Destination Member', + S: 'Source Member', + I: 'Input Customer' + }; + + return mapping[row.credit_to] || 'Unknown'; + }, + id: 'credit_to', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[250px]' } + }, + { + accessorFn: (row) => { + if (row.credit_to === 'S' || row.credit_to === 'D' || !row.credit_destination) { + return 'N/A'; + } + return row.credit_destination?.fullname; + }, + id: 'credit_destination', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[250px]' } + }, + { + accessorFn: (row) => row.credit_destination_account?.description, + id: 'credit_destination_account', + header: ({ column }) => ( + + ), + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[250px]' } + }, + { + accessorFn: (row: { deduct_from: string }) => { + const mapping: Record = { + D: 'Destination Member', + S: 'Source Member' + }; + + return mapping[row.deduct_from]; + }, + id: 'deduct_from', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[250px]' } + }, + { + accessorFn: (row) => row.deduct_from_account?.description, + id: 'deduct_from_account', + header: ({ column }) => ( + + ), + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[250px]' } + }, + { + accessorFn: (row) => row.priority, + id: 'priority', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + cell: ({ row }) => { + const isActive = row.original.priority === 'Y'; + + return ( + + {isActive ? 'Yes' : 'No'} + + ); + }, + meta: { + headerClassName: 'w-[100px]', + cellClassName: 'text-center' + } + }, + { + accessorFn: (row) => row.status, + id: 'status', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + cell: ({ row }) => { + const isActive = row.original.status === 'Y'; + + return ( + + {isActive ? 'Active' : 'Inactive'} + + ); + }, + meta: { + headerClassName: 'w-[100px]', + cellClassName: 'text-center' + } + }, + { + accessorFn: (row) => row.status_include, + id: 'status_include', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + cell: ({ row }) => { + const isActive = row.original.status_include === 'Y'; + + return ( + + {isActive ? 'Yes' : 'No'} + + ); + }, + meta: { + headerClassName: 'w-[100px]', + cellClassName: 'text-center' + } + }, + { + id: 'actions', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + cell: (data) => { + const row = data.row.original; + return ( + <> + + + + ); + }, + meta: { + headerClassName: 'w-[100px]', + cellClassName: 'text-center' + } + } + ], + [handleEditFeeDialog, handleDeleteFeeDialog] + ); + + return ( +
+
+

Manage Transaction Fee

+
+ + + } + sorting={[{ id: 'id', desc: true }]} + serverSide={true} + onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => + doGetTransferFeeListData(pageIndex, pageSize, sorting, columnFilters) + } + > + {children} + + +
+ ); +}; + +export { ManageTransferFeeContext, ManageTransferFeeContextProvider }; diff --git a/src/pages/transfer/transfertype/blocks/AddDialog.tsx b/src/pages/transfer/transfertype/blocks/AddDialog.tsx index 6d35adc..6e5f51f 100644 --- a/src/pages/transfer/transfertype/blocks/AddDialog.tsx +++ b/src/pages/transfer/transfertype/blocks/AddDialog.tsx @@ -76,7 +76,6 @@ const AddDialog = () => { wallet_origin: '', wallet_destination: '', - minimum_amount: 0, maximum_amount: 0, max_transaction_per_day: 0, @@ -141,13 +140,13 @@ const AddDialog = () => { toast.success('Success Create Transfer Type'); reload(); resetForm(); - const createActivity = { - module: 'Manage Transfer Type', - description: `Create Transfer Type => ${formField.name}`, - action: 'C' - }; + const createActivity = { + module: 'Manage Transfer Type', + description: `Create Transfer Type => ${formField.name}`, + action: 'C' + }; - doSaveLogActivity(createActivity); + doSaveLogActivity(createActivity); } }) .finally(() => { @@ -222,7 +221,7 @@ const AddDialog = () => { page: 1, with_deleted: false, order_field: 'wallets.name', - order_direction: 'ASC', + order_direction: 'ASC' }; const response = await GetData(`${API_URL_MASTERDATA}/wallet/list`, params); // console.log(response) @@ -265,11 +264,11 @@ const AddDialog = () => {
{alert.show && ( -
- -

{alert.message}

-
-
+
+ +

{alert.message}

+
+
)}
@@ -439,11 +438,10 @@ const AddDialog = () => {
-
@@ -458,7 +456,7 @@ const AddDialog = () => { - Disbursement + Disbursement Other Change Group Emoney Customer to Agent Change Group Emoney Agent to Customer diff --git a/src/pages/transfer/transfertype/blocks/DeleteDialog.tsx b/src/pages/transfer/transfertype/blocks/DeleteDialog.tsx index e4c51ad..ec35bf1 100644 --- a/src/pages/transfer/transfertype/blocks/DeleteDialog.tsx +++ b/src/pages/transfer/transfertype/blocks/DeleteDialog.tsx @@ -1,4 +1,10 @@ -import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Alert, useDataGrid } from '@/components'; import { useCallback, useState } from 'react'; @@ -12,7 +18,8 @@ import { doSaveLogActivity } from '@/actions/GlobalActions'; const API_URL = apiConfig.service_transaction; const DeleteDialog = () => { - const { showDeleteDialog, handleDeleteDialog, selectedTransferType } = useManageTransferTypeContext(); + const { showDeleteDialog, handleDeleteDialog, selectedTransferType } = + useManageTransferTypeContext(); const { reload } = useDataGrid(); const { DeleteData } = useCallApi(); const [alert, setAlert] = useState({ @@ -26,9 +33,12 @@ const DeleteDialog = () => { return; } - const response = await DeleteData(`${API_URL}/transactiontype/delete/${selectedTransferType}/false`, { - id: selectedTransferType - }); + const response = await DeleteData( + `${API_URL}/transactiontype/delete/${selectedTransferType}/false`, + { + id: selectedTransferType + } + ); if (response?.status) { setAlert({ show: false, message: '' }); @@ -37,7 +47,7 @@ const DeleteDialog = () => { const createActivity = { module: 'Manage Transfer Type', description: `Delete Transfer Type => ${selectedTransferType}`, - action: 'D' + action: 'D' }; doSaveLogActivity(createActivity); // setTimeout(() => toast.success('Success Delete Transaction Type'), 0); @@ -51,7 +61,9 @@ const DeleteDialog = () => { Delete Transfer Type - Are you sure you want to delete this data? + + Are you sure you want to delete this data? +

Are you sure?

You will delete this data! diff --git a/src/pages/transfer/transfertype/blocks/EditDialog.tsx b/src/pages/transfer/transfertype/blocks/EditDialog.tsx index c4b9e8f..1e3ca41 100644 --- a/src/pages/transfer/transfertype/blocks/EditDialog.tsx +++ b/src/pages/transfer/transfertype/blocks/EditDialog.tsx @@ -589,7 +589,7 @@ const EditDialog = () => { - Disbursement + Disbursement Other Change Group Emoney Customer to Agent Change Group Emoney Agent to Customer diff --git a/src/pages/transfer/transfertype/hooks/ManageTransferTypeContext.tsx b/src/pages/transfer/transfertype/hooks/ManageTransferTypeContext.tsx index efa6279..942ee8f 100644 --- a/src/pages/transfer/transfertype/hooks/ManageTransferTypeContext.tsx +++ b/src/pages/transfer/transfertype/hooks/ManageTransferTypeContext.tsx @@ -147,21 +147,21 @@ const ManageTransferTypeContextProvider = ({ children }: { children: React.React meta: { headerClassName: 'w-[250px]' } }, { - accessorFn: (row: {type: string }) => { + accessorFn: (row: { type: string }) => { const mapping: Record = { D: 'Disbursement', O: 'Other', CA: 'Change Group Emoney Customer to Agent', - AC:'Change Group Emoney Agent to Customer', + 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', + CE: 'Return Customer Emoney', + AD: 'Return Agent Deposit', AM: 'Return Agent Merchant', AE: 'Return Agent Emoney', - R: 'Reward Point', + R: 'Reward Point' }; - + return mapping[row.type] || 'Unknown'; }, id: 'type', @@ -171,7 +171,7 @@ const ManageTransferTypeContextProvider = ({ children }: { children: React.React enableSorting: true, enableHiding: false, meta: { headerClassName: 'w-[250px]' } - }, + }, { accessorFn: (row) => (row.status === 'Y' ? 'Active' : 'Inactive'), id: 'status', @@ -217,20 +217,19 @@ const ManageTransferTypeContextProvider = ({ children }: { children: React.React ], [handleEditDialog, handleDeleteDialog] ); - + const doGetTransferTypeListData = async ( page: number, limit: number, sorting: any, filter: any ) => { - const orderField = 'created_at'; const orderDirection = sorting.length > 0 ? (sorting[0].desc ? 'DESC' : 'ASC') : 'DESC'; - + filter = filter.length == 0 ? {} : { any: filter[0].value?.toLowerCase() }; - + const response = await GetData(`${API_URL}/transactiontype/list`, { limit: limit, page: page + 1, @@ -281,4 +280,4 @@ const ManageTransferTypeContextProvider = ({ children }: { children: React.React }; export { ManageTransferTypeContext, ManageTransferTypeContextProvider }; -export type { TransferType }; \ No newline at end of file +export type { TransferType }; diff --git a/src/routing/AppRoutingSetup.tsx b/src/routing/AppRoutingSetup.tsx index b7ce41f..865effb 100644 --- a/src/routing/AppRoutingSetup.tsx +++ b/src/routing/AppRoutingSetup.tsx @@ -15,6 +15,7 @@ import ManagePositionPage from '@/pages/settings/user/manage-position/ManagePosi import ManageAccount from '@/pages/account/manage-account/ManageAccount'; import ManageGroups from '@/pages/groups/ManageGroups'; import ManageMembers from '@/pages/members/manage-members/ManageMembers'; +import ManageKycDeletion from '@/pages/members/kyc-delete-member/ManageKycDeletion'; import Kyc from '@/pages/members/kyc/Kyc'; import AccessType from '@/pages/access/access-type/AccessType'; import MemberCredential from '@/pages/access/member-credentials/MemberCredentials'; @@ -79,6 +80,7 @@ const AppRoutingSetup = (): ReactElement => { } /> } /> + } /> } /> } />