diff --git a/src/pages/master/wallet/hooks/ManageWalletContext.tsx b/src/pages/master/wallet/hooks/ManageWalletContext.tsx index 28a5a96..823d6eb 100644 --- a/src/pages/master/wallet/hooks/ManageWalletContext.tsx +++ b/src/pages/master/wallet/hooks/ManageWalletContext.tsx @@ -74,7 +74,7 @@ const ManageWalletContextProvider = ({ children }: { children: React.ReactNode } () => [ { accessorFn: (row) => row.name, - id: 'name', + id: 'wallets.name', header: ({ column }) => , enableSorting: true, enableHiding: false, @@ -86,7 +86,7 @@ const ManageWalletContextProvider = ({ children }: { children: React.ReactNode } accessorFn: (row) => row.description, id: 'description', header: ({ column }) => , - enableSorting: true, + enableSorting: false, enableHiding: false, meta: { headerClassName: 'w-[250px]' @@ -94,7 +94,7 @@ const ManageWalletContextProvider = ({ children }: { children: React.ReactNode } }, { accessorFn: (row) => row.status, - id: 'status', + id: 'wallets.status', header: ({ column }) => , enableSorting: true, enableHiding: false, diff --git a/src/pages/members/manage-members/blocks/DetailMember.tsx b/src/pages/members/manage-members/blocks/DetailMember.tsx index 9853208..370f957 100644 --- a/src/pages/members/manage-members/blocks/DetailMember.tsx +++ b/src/pages/members/manage-members/blocks/DetailMember.tsx @@ -170,6 +170,9 @@ const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialDa e.preventDefault(); if (page === 'kyc' && formData.destinationGroup === "Premium" && !formData.approval_description_premium) return toast.warning(`Approval Premium Description needed!`) if (page === 'kyc' && formData.destinationGroup === "Agent" && !formData.approval_description_premium) return toast.warning(`Approval Agent Description needed!`) + if (!formData.msisdn||!formData.email||!formData.fullname||!formData.username||!formData.mother_fullname||!formData.address||!formData.nationality||!formData.date_birth||!formData.gender) { + return toast.warning(`Required fields cannot be empty: msisdn, email, fullname, username, mother Full Name, Address, Nationality, Date Of Birth, Gender.!`) + } // if (page === 'kyc' && !formData.description) return toast.warning(`Description for approval needed!`) handleSubmit(formData); } diff --git a/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx b/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx index 3ff2f54..16f5b25 100644 --- a/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx +++ b/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx @@ -193,9 +193,8 @@ const ManageMenusContextProvider = ({ children }: { children: React.ReactNode }) if (parent.link === '/') { if (!parents.find((el: any) => el.id === parent.id)) - setParents((el: any) => [...el, { id: parent.id, name: parent.name }]); // GET PARENTS + setParents((el: any) => [...el, { id: parent.id, name: parent.name }]); - // Menambahkan parent ke dalam result meskipun tidak memiliki children result.push({ id: parent.id, module: parent.module, @@ -208,7 +207,6 @@ const ManageMenusContextProvider = ({ children }: { children: React.ReactNode }) }); } - // Jika parent tidak memiliki children, langsung return hasil yang sudah ada if (!parent.children || parent.children.length === 0) { return result; } @@ -230,7 +228,6 @@ const ManageMenusContextProvider = ({ children }: { children: React.ReactNode }) }; }); - // Gabungkan parent dengan children yang sudah diflatten return [...result, ...childrenFlattened]; }; @@ -241,7 +238,7 @@ const ManageMenusContextProvider = ({ children }: { children: React.ReactNode }) const response = await GetData(`${API_URL}/menus/list`, { limit, - page: page + 1, + page: 1, with_deleted: false, order_field: sorting[0].id, order_direction: sorting[0].desc ? 'DESC' : 'ASC', @@ -255,10 +252,11 @@ const ManageMenusContextProvider = ({ children }: { children: React.ReactNode }) ); const total_count = transformedData.length; - const paginatedData = transformedData.slice(page * limit, (page + 1) * limit); - console.log('data', paginatedData); - // setMenus(transformedData); + // **Pagination di frontend saja (tanpa hit API ulang)** + const paginatedData = transformedData.slice(page * limit, (page + 1) * limit); + const totalPages = Math.ceil(total_count / limit); + return { data: paginatedData, totalCount: total_count }; } catch (error) { console.error('Error fetching Menus', error); diff --git a/src/pages/transfer/transferfee/blocks/AddDialog.tsx b/src/pages/transfer/transferfee/blocks/AddDialog.tsx index c7c8d64..271f4fe 100644 --- a/src/pages/transfer/transferfee/blocks/AddDialog.tsx +++ b/src/pages/transfer/transferfee/blocks/AddDialog.tsx @@ -50,9 +50,18 @@ interface TransactionTypeProps { id: string; name: string; } - +interface WalletProps { + id: string; + name: string; +} +interface CustomerProps { + id: string; + username: string; + msisdn: string; +} const API_URL = apiConfig.service_transaction; - +const API_URL_MASTER_DATA = apiConfig.service_master_data; +const API_URL_CUSTOMER = apiConfig.service_customer; const AddFeeDialog = () => { const parentRef = useRef(null); const { reload } = useDataGrid(); @@ -64,6 +73,9 @@ const AddFeeDialog = () => { message: '' }); const [transactionTypes, setTransactionTypes] = useState([]); + const [wallets, setWallets] = useState([]); + const [customers, setCustomers] = useState([]); + const [defaultCustomer, setDefaultCustomer] = useState(''); const initialState = { name: '', @@ -75,11 +87,17 @@ const AddFeeDialog = () => { period_end: '', deduct_amount: 0, deduct_percentage: 0, + fee_amount: 0, priority: '', status: '', status_include: '', created_by: '', - created_at: '' + created_at: '', + deduct_from: '', + deduct_from_account: '', + credit_to: '', + credit_destination: '00000000-0000-0000-0000-000000000000', + credit_destination_account: '' }; const [formField, setFormField] = useState(initialState); @@ -104,12 +122,19 @@ const AddFeeDialog = () => { maximum_amount: formField.maximum_amount, deduct_amount: formField.deduct_amount, deduct_percentage: formField.deduct_percentage, + fee_amount: formField.fee_amount, transaction_type: formField.transaction_type, status: formField.status, status_include: formField.status_include, - priority: formField.priority + priority: formField.priority, + deduct_from: formField.deduct_from, + deduct_from_account: formField.deduct_from_account, + credit_to: formField.credit_to, + credit_destination: formField.credit_destination, + credit_destination_account: formField.credit_destination_account }; }; + useEffect(() => { const created_time = new Date(); const formattedTime = created_time.toISOString().slice(0, 19).replace('T', ' '); @@ -138,38 +163,134 @@ const AddFeeDialog = () => { }); setTransactionTypes(response?.data.list || []); } catch (error) { - console.error('Error fetching customer', error); + console.error('Error fetching transaction types', error); } }; getTransactionTypeList([{ id: 'id', desc: false }]); }, [showAddFeeDialog]); + const fetchWallets = useCallback(async () => { + const params = { + limit: 100, + page: 1, + with_deleted: false, + order_field: 'Wallets.name', + order_direction: 'ASC', + }; + try { + const response = await GetData(`${API_URL_MASTER_DATA}/wallet/list`, params); + if (response?.status && response?.data) { + setWallets(response.data.list); + } else { + setWallets([]); + } + } catch (error) { + console.error('Error fetching wallets', error); + setWallets([]); + } + }, [GetData]); + + useEffect(() => { + if (!showAddFeeDialog) return; + fetchWallets(); + }, [showAddFeeDialog, fetchWallets]); + + useEffect(() => { + if (!showAddFeeDialog) return; + const getCustomerList = async (sorting: any) => { + try { + sorting = sorting.length === 0 ? [{ id: 'name', desc: false }] : sorting; + + const response = await GetData(`${API_URL_CUSTOMER}/customer/list`, { + limit: 100, + page: 1, + with_deleted: false, + order_field: sorting[0].id, + order_direction: sorting[0].desc ? 'DESC' : 'ASC' + }); + const customerList = response?.data.list || []; + setCustomers(customerList); + + if (customerList.length > 0) { + setDefaultCustomer(customerList[0].id); + } + } catch (error) { + console.error('Error fetching customers', error); + setCustomers([]); + } + }; + + getCustomerList([{ id: 'id', desc: false }]); + }, [showAddFeeDialog, GetData]); + const doCreateTransferType = useCallback( async (e: React.FormEvent) => { e.preventDefault(); - for (const key in formField) { - if ( - formField[key as keyof typeof formField] === '' ) { - setAlert({ show: true, message: 'All fields must be filled out' }); + setIsSubmitting(true); + + const requiredFields = [ + 'name', + 'description', + 'period_start', + 'period_end', + 'transaction_type', + 'status', + 'status_include', + 'priority', + 'deduct_from', + 'deduct_from_account', + '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, ' ')}` + }); + setIsSubmitting(false); return; } } + + if (formField.credit_to === 'I' && !formField.credit_destination) { + setAlert({ + show: true, + message: 'Credit destination is required when Input Customer is selected' + }); + setIsSubmitting(false); + return; + } setAlert({ show: false, message: '' }); - const response = await PostData(`${API_URL}/transactionfees/create`, { - ...formField - }); - if (response?.status) { - // toast.success('Success Create Transfer Fee'); - reload(); - resetForm(); - handleAddFeeDialog(false); - } else { - setAlert({ show: true, message: response?.message || 'Failed to create transfer fee' }); + + 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); + } else { + setAlert({ show: true, message: response?.message || 'Failed to create transfer fee' }); + } + } catch (error) { + console.error('Error creating transfer fee', error); + setAlert({ show: true, message: 'An error occurred while creating the transfer fee' }); + } finally { + setIsSubmitting(false); } }, - [formField] + [formField, PostData, reload, handleAddFeeDialog] ); return ( @@ -194,7 +315,13 @@ const AddFeeDialog = () => { - {alert.show && {alert.message}} + {alert.show && ( +
+ +

{alert.message}

+
+
+ )}
@@ -236,7 +363,7 @@ const AddFeeDialog = () => { minimum_amount: values.floatValue || 0 })); }} - placeholder="Enter Max Transaction Per Day" + placeholder="Enter Minimum Amount" />
@@ -253,7 +380,7 @@ const AddFeeDialog = () => { maximum_amount: values.floatValue || 0 })); }} - placeholder="Enter Max Transaction Per Day" + placeholder="Enter Maximum Amount" />
@@ -294,7 +421,7 @@ const AddFeeDialog = () => { deduct_amount: values.floatValue || 0 })); }} - placeholder="Enter Max Transaction Per Day" + placeholder="Enter Deduct Amount" />
@@ -311,11 +438,119 @@ const AddFeeDialog = () => { deduct_percentage: values.floatValue || 0 })); }} - placeholder="Enter Max Transaction Per Day" + placeholder="Enter Deduct Percentage" />
- + + { + setFormField((prev) => ({ + ...prev, + fee_amount: values.floatValue || 0 + })); + }} + placeholder="Enter Fee Amount" + /> +
+
+ + +
+
+ + +
+
+ + +
+ {formField.credit_to === 'I' && ( +
+ + +
+ )} +
+ + +
+
+ + 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" + /> +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + {formField.credit_to === 'I' && (
- - - 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 Minimum 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 Minimum Amount" - /> -
-
- - { - setFormField((prev) => ({ - ...prev, - deduct_percentage: values.floatValue || 0 - })); - }} - placeholder="Enter Minimum Amount" - /> -
-
- +
-
- - -
-
- - -
-
- - -
- - {/*
- + )} + +
+ +
+ +
+ + +
+ +
+ + -
*/} - - {/*
- - -
*/} -
- - -
+
+ +
+ + +
+ +
+ + +
+ +
+ +
-
- + +
); }; -export { EditFeeDialog }; +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 2510b2d..6f6bee5 100644 --- a/src/pages/transfer/transferfee/hooks/ManageTransferFeeContext.tsx +++ b/src/pages/transfer/transferfee/hooks/ManageTransferFeeContext.tsx @@ -7,6 +7,7 @@ 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; @@ -28,6 +29,21 @@ const initialProps: ContextProps = { selectedTransferFee: 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; @@ -35,6 +51,7 @@ const ManageTransferFeeContextProvider = ({ children }: { children: React.ReactN 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(); @@ -58,18 +75,28 @@ const ManageTransferFeeContextProvider = ({ children }: { children: React.ReactN sorting: any, filter: any ) => { - sorting = sorting.length == 0 ? [{ id: 'id', desc: false }] : sorting; - filter = filter.length == 0 ? {} : { any: filter[0].value.toLowerCase() }; - const response = await GetData(`${API_URL}/transactionfees/list`, { - limit: limit, - page: page+1, - with_deleted: false, - order_field: sorting[0].id, - order_direction: sorting[0].desc ? 'ASC' : 'DESC', - filter: JSON.stringify(filter) - }); - return { data: response?.data.list, totalCount: response?.data.total_count }; + 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[]>( () => [ { @@ -105,20 +132,12 @@ const ManageTransferFeeContextProvider = ({ children }: { children: React.ReactN meta: { headerClassName: 'w-[150px]' } }, { - accessorFn: (row) => row.period_start?.split('T')[0], - id: 'period_start', - header: ({ column }) => , + accessorFn: (row) => row.fee_amount, + id: 'fee_amount', + 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]' } + meta: { headerClassName: 'w-[150px]' } }, { accessorFn: (row) => row.deduct_amount, @@ -136,6 +155,23 @@ const ManageTransferFeeContextProvider = ({ children }: { children: React.ReactN 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', @@ -144,6 +180,30 @@ const ManageTransferFeeContextProvider = ({ children }: { children: React.ReactN enableHiding: false, meta: { headerClassName: 'w-[250px]' } }, + { + accessorFn: (row) => 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) => row.deduct_from_account?.description, + id: 'deduct_from_account', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[250px]' } + }, { accessorFn: (row) => (row.priority === 'Y' ? 'Yes' : 'No'), id: 'priority', @@ -230,8 +290,7 @@ const ManageTransferFeeContextProvider = ({ children }: { children: React.ReactN } > {children} - - + diff --git a/src/pages/transfer/transfertype/blocks/EditDialog.tsx b/src/pages/transfer/transfertype/blocks/EditDialog.tsx index 82a757a..d646905 100644 --- a/src/pages/transfer/transfertype/blocks/EditDialog.tsx +++ b/src/pages/transfer/transfertype/blocks/EditDialog.tsx @@ -25,6 +25,8 @@ import { useCallApi } from '@/hooks'; import { getAuth } from '@/auth'; import { ManageTransferFeeContextProvider } from '../../transferfee/hooks/ManageTransferFeeContext'; import AddFeeDialog from '../../transferfee/blocks/AddDialog'; +import { EditFeeDialog } from '../../transferfee/blocks/EditDialog'; +import DeleteFeeDialog from '../../transferfee/blocks/DeleteDialog'; import { Checkbox } from '@/components/ui/checkbox'; import { doSaveLogActivity } from '@/actions/GlobalActions'; @@ -721,6 +723,8 @@ const EditDialog = () => { + + {/* */}