From f40778035553aac81b65d4abc672e110dc9ef421 Mon Sep 17 00:00:00 2001 From: bagusajisaputroo Date: Tue, 15 Apr 2025 00:31:55 +0700 Subject: [PATCH 01/20] fixing transaction_type field on update transactionfee --- .../transferfee/blocks/EditDialog.tsx | 808 ++++++++++-------- 1 file changed, 451 insertions(+), 357 deletions(-) diff --git a/src/pages/transfer/transferfee/blocks/EditDialog.tsx b/src/pages/transfer/transferfee/blocks/EditDialog.tsx index 9fd8e8f..c42823f 100644 --- a/src/pages/transfer/transferfee/blocks/EditDialog.tsx +++ b/src/pages/transfer/transferfee/blocks/EditDialog.tsx @@ -7,15 +7,6 @@ import { SelectTrigger, SelectValue } from '@/components/ui/select'; -import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList -} from '@/components/ui/command'; import { Dialog, DialogBody, @@ -33,6 +24,8 @@ import { toast } from 'sonner'; import { useCallApi } from '@/hooks'; import { doSaveLogActivity } from '@/actions/GlobalActions'; import { getAuth } from '@/auth'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command'; const API_URL = apiConfig.service_transaction; const API_URL_MASTER_DATA = apiConfig.service_master_data; @@ -41,14 +34,12 @@ const API_URL_CUSTOMER = apiConfig.service_customer; interface WalletProps { id: string; name: string; - description?: string; } interface CustomerProps { id: string; username: string; msisdn: string; - fullname?: string; } interface TransactionTypeProps { @@ -61,18 +52,32 @@ const EditFeeDialog = () => { const { showEditFeeDialog, handleEditFeeDialog, selectedTransferFee } = useManageTransferFeeContext(); const { reload } = useDataGrid(); - const [wallets, setWallets] = useState([]); const { GetData, PutData } = useCallApi(); - const [isSubmitting, setIsSubmitting] = useState(false); - const [transactionTypes, setTransactionTypes] = useState([]); const parsedUser = getAuth()?.user; + + const [wallets, setWallets] = useState([]); const [customers, setCustomers] = useState([]); + const [transactionTypes, setTransactionTypes] = useState([]); + const [transactionTypeName, setTransactionTypeName] = useState(''); + const customersWithNames = customers.map((customer) => ({ + id: customer.id, + name: customer.username + })); + const [customerSearchTerm, setCustomerSearchTerm] = useState(''); + const [showCustomerSearch, setShowCustomerSearch] = useState(false); const [open, setOpen] = useState(false); + const [isSubmitting, setIsSubmitting] = useState(false); const [alert, setAlert] = useState({ show: false, message: '' }); + // Loading states + const [isLoadingTransferFee, setIsLoadingTransferFee] = useState(false); + const [isLoadingTransactionType, setIsLoadingTransactionType] = useState(false); + const [isLoadingWallets, setIsLoadingWallets] = useState(false); + const [isLoadingCustomers, setIsLoadingCustomers] = useState(false); + const initialState = { name: '', description: '', @@ -109,7 +114,7 @@ const EditFeeDialog = () => { updated_at: formattedTime })); } - }, [showEditFeeDialog]); + }, [showEditFeeDialog, parsedUser?.username]); const resetForm = () => { if (selectedTransferFee) { @@ -178,10 +183,11 @@ const EditFeeDialog = () => { reload(); handleEditFeeDialog(false, null); const createActivity = { - module: 'Manage Transfer Type', - description: `Edit Transfer Type => ${selectedTransferFee}`, + module: 'Manage Transfer Fee', + description: `Edit Transfer Fee => ${formField.name}`, action: 'U' }; + doSaveLogActivity(createActivity); } else { setAlert({ show: true, message: response?.message || 'Failed to update transfer fee' }); } @@ -196,6 +202,7 @@ const EditFeeDialog = () => { ); const fetchWallets = useCallback(async () => { + setIsLoadingWallets(true); const params = { limit: 100, page: 1, @@ -213,6 +220,8 @@ const EditFeeDialog = () => { } catch (error) { console.error('Error fetching wallets', error); setWallets([]); + } finally { + setIsLoadingWallets(false); } }, [GetData]); @@ -225,6 +234,7 @@ const EditFeeDialog = () => { if (!showEditFeeDialog) return; const getCustomerList = async (sorting: any) => { + setIsLoadingCustomers(true); try { sorting = sorting.length === 0 ? [{ id: 'name', desc: false }] : sorting; const response = await GetData(`${API_URL_CUSTOMER}/customer/list`, { @@ -237,16 +247,19 @@ const EditFeeDialog = () => { setCustomers(response?.data.list || []); } catch (error) { console.error('Error fetching customers', error); + } finally { + setIsLoadingCustomers(false); } }; - getCustomerList([{ id: 'msisdn', desc: false }]); + getCustomerList([{ id: 'id', desc: false }]); }, [showEditFeeDialog, GetData]); useEffect(() => { if (!showEditFeeDialog) return; const getTransactionTypeList = async (sorting: any) => { + setIsLoadingTransactionType(true); try { sorting = sorting.length === 0 ? [{ id: 'name', desc: false }] : sorting; const response = await GetData(`${API_URL}/transactiontype/list`, { @@ -259,6 +272,8 @@ const EditFeeDialog = () => { setTransactionTypes(response?.data.list || []); } catch (error) { console.error('Error fetching transaction types', error); + } finally { + setIsLoadingTransactionType(false); } }; @@ -272,6 +287,7 @@ const EditFeeDialog = () => { const fetchTransactionFee = useCallback( async (id: string) => { + setIsLoadingTransferFee(true); try { const response = await GetData(`${API_URL}/transactionfees/getdata/${id}`, {}); @@ -300,10 +316,16 @@ const EditFeeDialog = () => { updated_by: parsedUser?.username, updated_at: new Date().toISOString().slice(0, 19).replace('T', ' ') }); + + if (response.data.transaction_type?.name) { + setTransactionTypeName(response.data.transaction_type.name); + } } } catch (error) { console.error('Error fetching transaction fee details', error); setAlert({ show: true, message: 'Failed to fetch transaction fee details' }); + } finally { + setIsLoadingTransferFee(false); } }, [GetData, parsedUser?.username] @@ -328,6 +350,37 @@ const EditFeeDialog = () => { handleEditFeeDialog(false, null); }; + const renderSelectWithLoading = ( + value: string, + onChangeHandler: (value: string) => void, + options: { id: string; name: string }[] | null, + placeholder: string, + isLoading: boolean + ) => { + return ( + + ); + }; + return ( { )} - -
-
-
-
- - - 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' && ( +
+ + + + + + + + + + No Customer found. + + {customers.map((customer) => ( + { + setFormField({ + ...formField, + credit_destination: customer.id + }); + setOpen(false); + }} + > + {customer.username} + + ))} + + + + + +
+)} -
- - -
-
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- + +
+ + {renderSelectWithLoading( + formField.credit_destination_account, + (value) => setFormField({ ...formField, credit_destination_account: value }), + wallets, + 'Select Wallet', + isLoadingWallets + )} +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
-
- + + )}
); }; -export { EditFeeDialog }; +export {EditFeeDialog}; \ No newline at end of file From 83593db7ac76bce18c52e01f99842238b143bddf Mon Sep 17 00:00:00 2001 From: bagusajisaputroo Date: Tue, 15 Apr 2025 01:04:53 +0700 Subject: [PATCH 02/20] adding search in credit_destination on update transaction fee --- .../transferfee/blocks/EditDialog.tsx | 95 ++++++++++++------- 1 file changed, 61 insertions(+), 34 deletions(-) diff --git a/src/pages/transfer/transferfee/blocks/EditDialog.tsx b/src/pages/transfer/transferfee/blocks/EditDialog.tsx index c42823f..b8077b5 100644 --- a/src/pages/transfer/transferfee/blocks/EditDialog.tsx +++ b/src/pages/transfer/transferfee/blocks/EditDialog.tsx @@ -343,13 +343,19 @@ const EditFeeDialog = () => { hasFetchedRef.current = false; } }, [selectedTransferFee, showEditFeeDialog, fetchTransactionFee]); - const handleCloseDialog = () => { setFormField(initialState); setAlert({ show: false, message: '' }); + setCustomerSearchTerm(''); + setOpen(false); handleEditFeeDialog(false, null); }; - + useEffect(() => { + if (!showEditFeeDialog) { + setCustomerSearchTerm(''); + setOpen(false); + } + }, [showEditFeeDialog]); const renderSelectWithLoading = ( value: string, onChangeHandler: (value: string) => void, @@ -668,36 +674,51 @@ const EditFeeDialog = () => { -{formField.credit_to === 'I' && ( + {formField.credit_to === 'I' && (
- - - - - - - - - No Customer found. - - {customers.map((customer) => ( - { +
+
setOpen(!open)} + > + + {customers.find(customer => customer.id === formField.credit_destination)?.username || 'Search customer...'} + + + + +
+ + {open && ( +
+
+ setCustomerSearchTerm(e.target.value)} + autoComplete="off" + // Prevent clicks from closing the dropdown + onClick={(e) => e.stopPropagation()} + // Auto focus the input when opened + autoFocus + /> +
+
+ {customers + .filter(customer => + customer.username.toLowerCase().includes(customerSearchTerm.toLowerCase()) || + customer.msisdn.includes(customerSearchTerm) + ) + .map(customer => ( +
{ setFormField({ ...formField, credit_destination: customer.id @@ -706,17 +727,23 @@ const EditFeeDialog = () => { }} > {customer.username} - +
))} - - - - - + {customers.filter(customer => + customer.username.toLowerCase().includes(customerSearchTerm.toLowerCase()) || + customer.msisdn.includes(customerSearchTerm) + ).length === 0 && ( +
No customer found
+ )} +
+
+ )} +
)} +
) : (
)} diff --git a/src/pages/members/kyc-delete-member/hooks/ManageKycDeletionContext.tsx b/src/pages/members/kyc-delete-member/hooks/ManageKycDeletionContext.tsx index bf5e46f..c0742fd 100644 --- a/src/pages/members/kyc-delete-member/hooks/ManageKycDeletionContext.tsx +++ b/src/pages/members/kyc-delete-member/hooks/ManageKycDeletionContext.tsx @@ -1,5 +1,6 @@ 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'; @@ -36,6 +37,7 @@ interface ContextProps { selectedIdCustomer: string | null; detailKyc: any | null; setDetailKyc: React.Dispatch>; + handleApproveReject: (customerDeletionId: string, status_approve: string) => {}; } const initialProps: ContextProps = { @@ -48,6 +50,7 @@ const initialProps: ContextProps = { setShowDetailDialog: () => { }, detailKyc: async () => {}, setDetailKyc: () => { }, + handleApproveReject: () => ({customerDeletionId: '0', status_approve: 'Y'}), }; const ManageKycDeletionContext = createContext(initialProps); @@ -83,13 +86,15 @@ export const renderStatusBadge = (statusRaw: string | null | undefined) => { ); }; +// 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 } = useCallApi(); + const { GetData, PostData } = useCallApi(); const getKycDeletionList = async (page: number, limit: number, sorting: any, filter: any) => { try { @@ -130,12 +135,11 @@ const ManageKycDeletionContextProvider = ({ children }: { children: React.ReactN }; const handleDetailDialog = useCallback(async (show: boolean, selected_id_customer: string | null) => { - setSelectedIdCustomer(show ? selected_id_customer : null); - let detailCustomer = await GetData(`${API_URL}/customer_deletion/detail/${selected_id_customer}`, {}) - setDetailKyc(detailCustomer?.data) - console.log('detailCustomer: ',detailCustomer) - console.log('detailCustomer2: ',detailCustomer?.data) - console.log('detailKyc: ', detailKyc) + 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); }, []); @@ -143,6 +147,26 @@ const ManageKycDeletionContextProvider = ({ children }: { children: React.ReactN 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[]>( () => [ { @@ -242,7 +266,8 @@ const ManageKycDeletionContextProvider = ({ children }: { children: React.ReactN selectedIdCustomer, setShowDetailDialog, setDetailKyc, - detailKyc + detailKyc, + handleApproveReject, }} > From 172e8a0815f04b8b4d2abd37ba2df61aca8ed23e Mon Sep 17 00:00:00 2001 From: bagusajisaputroo Date: Tue, 15 Apr 2025 10:44:43 +0700 Subject: [PATCH 05/20] added search on credit_destination field in transaction fee add +edit --- .../transfer/transferfee/blocks/AddDialog.tsx | 83 +++++++++++++++++-- .../transferfee/blocks/EditDialog.tsx | 5 -- 2 files changed, 76 insertions(+), 12 deletions(-) diff --git a/src/pages/transfer/transferfee/blocks/AddDialog.tsx b/src/pages/transfer/transferfee/blocks/AddDialog.tsx index 0860010..6b73eed 100644 --- a/src/pages/transfer/transferfee/blocks/AddDialog.tsx +++ b/src/pages/transfer/transferfee/blocks/AddDialog.tsx @@ -70,6 +70,8 @@ const AddFeeDialog = () => { selectedTransferFee, transactionTypeId } = useManageTransferFeeContext(); + const [customerSearchTerm, setCustomerSearchTerm] = useState(''); + const [open, setOpen] = useState(false); const [alert, setAlert] = useState({ show: false, @@ -111,6 +113,7 @@ const AddFeeDialog = () => { credit_destination_account: '' }; + const [formField, setFormField] = useState(initialState); const resetForm = () => { @@ -151,6 +154,13 @@ const AddFeeDialog = () => { } }, [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', ' '); @@ -619,13 +629,72 @@ const AddFeeDialog = () => { - {renderSelectWithLoading( - formField.credit_destination, - (value) => setFormField({ ...formField, credit_destination: value }), - customersWithNames, - 'Select Customer', - isLoadingCustomers - )} +
+
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 +
+ )} +
+
+ )} +
)}
diff --git a/src/pages/transfer/transferfee/blocks/EditDialog.tsx b/src/pages/transfer/transferfee/blocks/EditDialog.tsx index b8077b5..3d6b0ce 100644 --- a/src/pages/transfer/transferfee/blocks/EditDialog.tsx +++ b/src/pages/transfer/transferfee/blocks/EditDialog.tsx @@ -72,7 +72,6 @@ const EditFeeDialog = () => { message: '' }); - // Loading states const [isLoadingTransferFee, setIsLoadingTransferFee] = useState(false); const [isLoadingTransactionType, setIsLoadingTransactionType] = useState(false); const [isLoadingWallets, setIsLoadingWallets] = useState(false); @@ -687,9 +686,7 @@ const EditFeeDialog = () => { {customers.find(customer => customer.id === formField.credit_destination)?.username || 'Search customer...'} - -
{open && ( @@ -702,9 +699,7 @@ const EditFeeDialog = () => { value={customerSearchTerm} onChange={(e) => setCustomerSearchTerm(e.target.value)} autoComplete="off" - // Prevent clicks from closing the dropdown onClick={(e) => e.stopPropagation()} - // Auto focus the input when opened autoFocus /> From d84c62cee405f5a5a2fd5412cf88bdfb3bbc059a Mon Sep 17 00:00:00 2001 From: wayanrivan Date: Tue, 15 Apr 2025 10:52:53 +0700 Subject: [PATCH 06/20] update bug type.name --- .../blocks/DetailApprovalTransaction.tsx | 8 +++++--- .../history-transaction/blocks/DetailTransaction.tsx | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) 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/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

From 249e09774d2a834cb8c882e4755906484f3f0669 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 15 Apr 2025 14:38:29 +0700 Subject: [PATCH 07/20] fix group --- src/pages/members/kyc/Kyc.tsx | 1 - src/pages/members/manage-members/Columns.tsx | 3 +- .../members/manage-members/ManageMembers.tsx | 1 - .../manage-members/blocks/AdmAccess.tsx | 39 +++--- .../manage-members/blocks/DetailMember.tsx | 123 ++++++++++-------- 5 files changed, 89 insertions(+), 78 deletions(-) diff --git a/src/pages/members/kyc/Kyc.tsx b/src/pages/members/kyc/Kyc.tsx index 49f1fdd..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; 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 85035ad..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) } } @@ -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 }}))}> From cab30d63cc817d552e95f81112e24ae5cf6ed9d2 Mon Sep 17 00:00:00 2001 From: wayanrivan Date: Tue, 15 Apr 2025 15:05:47 +0700 Subject: [PATCH 08/20] add reload button in approval-transaction and history-transaction --- .../blocks/ListToolbar.tsx | 17 +++++++++++++++++ .../history-transaction/blocks/ListToolbar.tsx | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) 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/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" /> + + + +
From 99097a29507f8c61c38581ffc3eb60ef26d8e234 Mon Sep 17 00:00:00 2001 From: wayanrivan Date: Tue, 15 Apr 2025 15:15:18 +0700 Subject: [PATCH 09/20] update dialog add kyc-delete-member --- .../kyc-delete-member/blocks/DetailDialog.tsx | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/pages/members/kyc-delete-member/blocks/DetailDialog.tsx b/src/pages/members/kyc-delete-member/blocks/DetailDialog.tsx index 4ba5dfc..016674c 100644 --- a/src/pages/members/kyc-delete-member/blocks/DetailDialog.tsx +++ b/src/pages/members/kyc-delete-member/blocks/DetailDialog.tsx @@ -1,9 +1,9 @@ import { - Dialog, - DialogBody, - DialogContent, - DialogHeader, - DialogTitle + Dialog, + DialogBody, + DialogContent, + DialogHeader, + DialogTitle } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; @@ -13,11 +13,11 @@ import axios from 'axios'; const API_URL = apiConfig.service_customer; const DetailDialog = () => { - const { showDetailDialog, setShowDetailDialog, detailKyc, handleApproveReject } = useManageKycDeletionContext(); - - return ( + const { showDetailDialog, setShowDetailDialog, detailKyc, handleApproveReject } = useManageKycDeletionContext(); + + return ( - + Customer Deletion Details @@ -38,9 +38,9 @@ const DetailDialog = () => {
- - -
+ + + ) : (
)} @@ -51,28 +51,28 @@ const DetailDialog = () => { export default DetailDialog; -function generateInput(formData:any, handleChange:any, label:string, name:string, type: string, required: boolean, disabled: boolean) { +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" + 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'} + 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]} + const fixStatus = statusMap[status] ?? { label: formData[name] } formData[name] = fixStatus.label } return ( @@ -80,15 +80,15 @@ function generateInput(formData:any, handleChange:any, label:string, name:string
- {required ? "*" : ""} + +
From a6e341c54a3092d9fab6f62243d8418ef2f0d142 Mon Sep 17 00:00:00 2001 From: Raja Oktafrianto Date: Tue, 15 Apr 2025 15:15:56 +0700 Subject: [PATCH 10/20] manage notification --- src/config/api.config.ts | 2 + src/pages/notification/blocks/AddDialog.tsx | 378 +++++++++++++----- src/pages/notification/blocks/ListToolbar.tsx | 14 +- .../hooks/ManageNotificationContext.tsx | 165 +++++--- 4 files changed, 399 insertions(+), 160 deletions(-) diff --git a/src/config/api.config.ts b/src/config/api.config.ts index 81e8652..4025a2e 100644 --- a/src/config/api.config.ts +++ b/src/config/api.config.ts @@ -7,6 +7,7 @@ interface apiConfigProps { transaction: string; nationality: string; service_disbursement: string; + service_notification: string; } const API_URL = import.meta.env.VITE_APP_API_URL; @@ -20,6 +21,7 @@ const apiConfig: apiConfigProps = { service_wallet: `${API_URL}/w`, transaction: `${API_URL}/x`, service_disbursement: `${API_URL}/s`, + service_notification: `${API_URL}/n`, nationality: `https://tpay.shiblysolution.id/cms/api/mobile/list-country/ ` }; diff --git a/src/pages/notification/blocks/AddDialog.tsx b/src/pages/notification/blocks/AddDialog.tsx index 9daebf8..a857439 100644 --- a/src/pages/notification/blocks/AddDialog.tsx +++ b/src/pages/notification/blocks/AddDialog.tsx @@ -1,6 +1,6 @@ import { apiConfig } from '@/config/api.config'; -import { useRef, useState } from 'react'; -import { Alert, KeenIcon, useDataGrid } from '@/components'; +import { useRef, useState, useCallback, useEffect } from 'react'; +import { Alert, useDataGrid } from '@/components'; import { useCallApi } from '@/hooks'; import { Dialog, @@ -10,134 +10,322 @@ import { DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList +} from '@/components/ui/command'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { toast } from 'sonner'; import { Input } from '@/components/ui/input'; +import { Textarea } from '@/components/ui/textarea'; import { Button } from '@/components/ui/button'; import { useManageNotificationContext } from '../hooks/useManageNotificationContext'; +import { doSaveLogActivity } from '@/actions/GlobalActions'; +import { CustomerProps } from '@/pages/master/provider/blocks/AddDialog'; +import { ChevronDown } from 'lucide-react'; -const API_URL = apiConfig.service_dashboard; +const API_URL_CUSTOMER = apiConfig.service_customer; +const API_URL_NOTIFICATION = apiConfig.service_notification; const AddDialog = () => { const parentRef = useRef(null); - const { - handleAddDialog, - handleEditDialog, - showAddDialog, - showEditDialog, - selectedNotification, - notifications - } = useManageNotificationContext(); - + const { handleAddDialog, showAddDialog } = useManageNotificationContext(); const { reload } = useDataGrid(); - const { PostData, PutData } = useCallApi(); - const [alert, setAlert] = useState({ - show: false, - message: '' - }); + const { GetData, PostData } = useCallApi(); + const [alert, setAlert] = useState({ show: false, message: '' }); const initialState = { - name: '', - destination_module: '' + customers: [], + type: '', + via: '', + subject: '', + content: '' }; const [formField, setFormField] = useState(initialState); + const [open, setOpen] = useState(false); + const [customers, setCustomers] = useState([]); const resetForm = () => { setFormField(initialState); + setAlert({ show: false, message: '' }); }; - const [isSubmitting, setIsSubmitting] = useState(false); + + const handleChange = (e: React.ChangeEvent) => { + setFormField({ ...formField, [e.target.name]: e.target.value }); + }; + + const doCreateNotification = async (e: React.FormEvent) => { + e.preventDefault(); + const response = await PostData(`${API_URL_NOTIFICATION}/send`, formField); + console.log('API Response :', response); + + if (response?.status) { + handleAddDialog(false); + resetForm(); + reload(); + toast.success('Notification Send Successfully!'); + const createActivity = { + module: 'Manage Notification', + description: `Send New Notification => ${formField.via}`, + action: 'C' + }; + doSaveLogActivity(createActivity); + } else { + toast.error('Failed to create notification'); + setAlert({ show: true, message: 'Failed to create notification. Please Try Again.' }); + } + }; + + // 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 == false ? 'ASC' : 'DESC' + // }); + + // setCustomers(response?.data.list); + // } catch (error) { + // console.error('Error fetching customer', error); + // } + // }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); - if (formField.name === '' || formField.destination_module === '') { - setAlert({ show: true, message: 'Please fill in all required fields.' }); + if ( + formField.type.trim() === '' || + formField.via.trim() === '' || + formField.subject.trim() === '' || + formField.content.trim() === '' + ) { + setAlert({ show: true, message: 'Please fill all required fields.' }); return; } - console.log(formField); + + doCreateNotification(e); + // console.log(formField); setAlert({ show: false, message: '' }); }; - const handleReset = () => { - setFormField(initialState); - }; + // useEffect(() => { + // getCustomerList([{ id: 'id', desc: false }]); + // }, []); + + useEffect(() => { + if (showAddDialog === false) { + resetForm(); + } + }, [showAddDialog]); return ( - handleAddDialog(open)}> - - - - -
-
-

- Create Notification -

-
-
-
{ - handleAddDialog(false); - resetForm(); - }} - > - -
-
+ + + + Notification - Create + - -
+ +
{alert.show && ( - - {alert.message} + +

{alert.message}

)} - -
-
-
- - - setFormField((prev) => ({ ...prev, name: target.value })) - } - /> -
-
-
-
- - - setFormField((prev) => ({ ...prev, destination_module: target.value })) - } + {/*
+
+ +
+
-
- -
- - + All Users + +
- -
+
*/} + + {/*
+
+ + + + + + + + + { + e.currentTarget.scrollTop += e.deltaY; + }} + > + No Customer found. + + {customers.map((customer) => ( + { + setFormField({ + ...formField, + customers: customer.id + }); + setOpen(false); + }} + > + {customer.username} + + ))} + + + + + +
+
*/} + +
+
+ +
+ {['info', 'promo'].map((type) => ( + + ))} +
+
+
+ +
+
+ +
+ + + +
+
+
+ +
+
+ + +
+
+ +
+
+ +