diff --git a/src/pages/members/kyc-delete-member/blocks/DetailDialog.tsx b/src/pages/members/kyc-delete-member/blocks/DetailDialog.tsx new file mode 100644 index 0000000..7875949 --- /dev/null +++ b/src/pages/members/kyc-delete-member/blocks/DetailDialog.tsx @@ -0,0 +1,98 @@ +import { + Dialog, + DialogBody, + DialogContent, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { useManageKycDeletionContext } from '../hooks'; +import { apiConfig } from '@/config/api.config'; +import axios from 'axios'; + +const API_URL = apiConfig.service_customer; +const DetailDialog = () => { + const { showDetailDialog, setShowDetailDialog, detailKyc } = useManageKycDeletionContext(); + + return ( + + + + Customer Deletion Details + + + {/* Tab Content */} + {detailKyc && detailKyc != null ? ( +
+ {generateInput(detailKyc, null, 'Group', 'group_name', 'text', false, true)} + {generateInput(detailKyc, null, 'Username', 'username', 'text', false, true)} + {generateInput(detailKyc, null, 'Full Name', 'fullname', 'text', false, true)} + {generateInput(detailKyc, null, 'Gender', 'customers_gender', 'text', false, true)} + {generateInput(detailKyc, null, 'Date of Birth', 'birthdate', 'date', false, true)} + {generateInput(detailKyc, null, 'Mother Name', 'registered_mother_fullname', 'text', false, true)} + {generateInput(detailKyc, null, 'MSISDN', 'registered_msisdn', 'text', false, true)} + {generateInput(detailKyc, null, 'Reason Deletion', 'reason_deletion', 'text', false, true)} + {generateInput(detailKyc, null, 'Reason Note', 'reason_note', 'text', false, true)} + {generateInput(detailKyc, null, 'Status Approval', 'status_approve', 'text', false, true)} + +
+ + + +
+
+ ) : (
)} +
+
+
+ ); +}; + +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/hooks/ManageKycDeletionContext.tsx b/src/pages/members/kyc-delete-member/hooks/ManageKycDeletionContext.tsx index 5fec6e7..bf5e46f 100644 --- a/src/pages/members/kyc-delete-member/hooks/ManageKycDeletionContext.tsx +++ b/src/pages/members/kyc-delete-member/hooks/ManageKycDeletionContext.tsx @@ -6,6 +6,7 @@ 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; @@ -28,10 +29,13 @@ interface ContextProps { filter: any ) => Promise<{ data: ManageKycDeletionProps[]; totalCount: number } | undefined>; showDetailDialog: boolean; - handleDetailDialog: (show: boolean, selected_user: ManageKycDeletionProps | null) => void; + setShowDetailDialog: React.Dispatch>; + handleDetailDialog: (show: boolean, selected_user: string | null) => void; showAddDialog: boolean; handleAddDialog: (show: boolean) => void; - selectedUser: ManageKycDeletionProps | null; + selectedIdCustomer: string | null; + detailKyc: any | null; + setDetailKyc: React.Dispatch>; } const initialProps: ContextProps = { @@ -40,7 +44,10 @@ const initialProps: ContextProps = { handleDetailDialog: () => {}, showAddDialog: false, handleAddDialog: () => {}, - selectedUser: null + selectedIdCustomer: null, + setShowDetailDialog: () => { }, + detailKyc: async () => {}, + setDetailKyc: () => { }, }; const ManageKycDeletionContext = createContext(initialProps); @@ -79,7 +86,8 @@ export const renderStatusBadge = (statusRaw: string | null | undefined) => { const ManageKycDeletionContextProvider = ({ children }: { children: React.ReactNode }) => { const [showDetailDialog, setShowDetailDialog] = useState(false); const [showAddDialog, setShowAddDialog] = useState(false); - const [selectedUser, setSelectedUser] = useState(null); + const [selectedIdCustomer, setSelectedIdCustomer] = useState(null); + const [detailKyc, setDetailKyc] = useState(); const [manageKyc, setManageKyc] = useState([]); const { GetData } = useCallApi(); @@ -121,8 +129,13 @@ const ManageKycDeletionContextProvider = ({ children }: { children: React.ReactN } }; - const handleDetailDialog = useCallback((show: boolean, selected_user: ManageKycDeletionProps | null) => { - setSelectedUser(show ? selected_user : null); + 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) setShowDetailDialog(show); }, []); @@ -226,10 +239,14 @@ const ManageKycDeletionContextProvider = ({ children }: { children: React.ReactN handleDetailDialog, showAddDialog, handleAddDialog, - selectedUser + selectedIdCustomer, + setShowDetailDialog, + setDetailKyc, + detailKyc }} > + { 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] @@ -321,12 +343,49 @@ 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, + 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' && ( +
+ +
+
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 + }); + 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 }; +export {EditFeeDialog}; \ No newline at end of file