From 115fb9c00132d81f577b3f3e1a4149a97c5fefeb Mon Sep 17 00:00:00 2001 From: Raja Oktafrianto Date: Sat, 12 Apr 2025 10:54:56 +0700 Subject: [PATCH 1/4] fixing manage user & add field customer --- src/pages/master/reward/blocks/AddDialog.tsx | 8 ++ .../master/reward/blocks/DeleteDialog.tsx | 8 ++ src/pages/master/reward/blocks/EditDialog.tsx | 8 ++ .../user/manage-user/blocks/AddDialog.tsx | 103 +++++++++++++++++- .../user/manage-user/blocks/EditDialog.tsx | 97 ++++++++++++++++- .../manage-user/hooks/ManageUserContext.tsx | 19 +++- 6 files changed, 231 insertions(+), 12 deletions(-) diff --git a/src/pages/master/reward/blocks/AddDialog.tsx b/src/pages/master/reward/blocks/AddDialog.tsx index 6e32603..de9756c 100644 --- a/src/pages/master/reward/blocks/AddDialog.tsx +++ b/src/pages/master/reward/blocks/AddDialog.tsx @@ -23,6 +23,7 @@ import { SelectTrigger, SelectValue } from '@/components/ui/select'; +import { doSaveLogActivity } from '@/actions/GlobalActions'; const API_URL = apiConfig.service_master_data; @@ -75,6 +76,13 @@ const AddDialog = () => { resetForm(); reload(); toast.success('Reward Create successfully!'); + const createActivity = { + module: 'Manage Reward', + description: `Create New Reward => ${formField.name}`, + action: 'C' + }; + + doSaveLogActivity(createActivity); } else { toast.error('Failed to create reward.'); setAlert({ show: true, message: 'Failed to create reward. Please try again.' }); diff --git a/src/pages/master/reward/blocks/DeleteDialog.tsx b/src/pages/master/reward/blocks/DeleteDialog.tsx index 70aaf5e..8014795 100644 --- a/src/pages/master/reward/blocks/DeleteDialog.tsx +++ b/src/pages/master/reward/blocks/DeleteDialog.tsx @@ -13,6 +13,7 @@ import { import { Button } from '@/components/ui/button'; import { DialogDescription } from '@radix-ui/react-dialog'; import { useManageRewardContext } from '../hooks/useManageRewardContext'; +import { doSaveLogActivity } from '@/actions/GlobalActions'; const API_URL = apiConfig.service_master_data; @@ -42,6 +43,13 @@ const DeleteDialog = () => { handleDeleteDialog(false, null); toast.success('Success Delete Reward'); reload(); + const deleteActivity = { + module: 'Manage Reward', + description: `Delete Reward => ${selectedReward}`, + action: 'D' + }; + + doSaveLogActivity(deleteActivity); } else { setAlert({ show: true, message: response?.message }); toast.error('Failed Delete Reward'); diff --git a/src/pages/master/reward/blocks/EditDialog.tsx b/src/pages/master/reward/blocks/EditDialog.tsx index ac71043..817dfb3 100644 --- a/src/pages/master/reward/blocks/EditDialog.tsx +++ b/src/pages/master/reward/blocks/EditDialog.tsx @@ -23,6 +23,7 @@ import { SelectValue } from '@/components/ui/select'; import { useManageRewardContext } from '../hooks/useManageRewardContext'; +import { doSaveLogActivity } from '@/actions/GlobalActions'; const API_URL = apiConfig.service_master_data; @@ -74,6 +75,13 @@ const EditDialog = () => { handleEditDialog(false, null); toast.success('Success Update Reward'); reload(); + const editActivity = { + module: 'Manage Reward', + description: `Edit Reward => ${formField.name}`, + action: 'U' + }; + + doSaveLogActivity(editActivity); } else { toast.error('Error Update Reward'); setAlert({ show: true, message: 'Failed to Update Reward. Please try again.' }); diff --git a/src/pages/settings/user/manage-user/blocks/AddDialog.tsx b/src/pages/settings/user/manage-user/blocks/AddDialog.tsx index 84298fa..333f84c 100644 --- a/src/pages/settings/user/manage-user/blocks/AddDialog.tsx +++ b/src/pages/settings/user/manage-user/blocks/AddDialog.tsx @@ -15,6 +15,16 @@ import { DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList +} from '@/components/ui/command'; +import { ChevronDown } from 'lucide-react'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { useUserContext } from '../hooks'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; @@ -25,6 +35,14 @@ import { useCallApi } from '@/hooks'; import { doSaveLogActivity } from '@/actions/GlobalActions'; import clsx from 'clsx'; +export interface CustomerProps { + id: string; + msisdn: string; + email: string; + fullname: string; + username: string; +} + interface RoleListProps { id: string; name: string; @@ -42,7 +60,9 @@ interface CreateUserParams { status: string; } +const API_URL_CUSTOMER = apiConfig.service_customer; const API_URL = apiConfig.service_dashboard; + type PasswordType = 'password' | 'retype_password'; const AddDialog = () => { @@ -62,7 +82,8 @@ const AddDialog = () => { retype_password: '', name: '', id_role: '', - status: '' + status: '', + customerid: '' }; const [formField, setFormField] = useState(initialState); const [showPassword, setShowPassword] = useState({ @@ -71,6 +92,8 @@ const AddDialog = () => { }); const [messagePassword, setMessagePassword] = useState(true); + const [open, setOpen] = useState(false); + const [customers, setCustomers] = useState([]); const [isSubmitting, setIsSubmitting] = useState(false); const [passwordErrors, setPasswordErrors] = useState([]); @@ -155,6 +178,23 @@ const AddDialog = () => { // console.log('ini data user_role:', response?.data); }, []); + 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); + } + }; + useEffect(() => { fetchRoles(); }, [fetchRoles]); @@ -162,7 +202,7 @@ const AddDialog = () => { const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); - console.log('Form data before submit:', formField); + // console.log('Form data before submit:', formField); if ( formField.email.trim() === '' || @@ -171,7 +211,8 @@ const AddDialog = () => { formField.retype_password.trim() === '' || formField.name.trim() === '' || formField.id_role.trim() === '' || - formField.status.trim() === '' + formField.status.trim() === '' || + formField.customerid.trim() === '' ) { setAlert({ show: true, message: 'Please fill name field.' }); return; @@ -196,6 +237,10 @@ const AddDialog = () => { } }, [showAddDialog]); + useEffect(() => { + getCustomerList([{ id: 'id', desc: false }]); + }, []); + const togglePassword = useCallback((event: MouseEvent, key: string) => { event.preventDefault(); setShowPassword((prev) => ({ ...prev, [key]: !prev[key as PasswordType] })); @@ -262,6 +307,58 @@ const AddDialog = () => { +
+
+ + + + + + + + + { + e.currentTarget.scrollTop += e.deltaY; + }} + > + No Customer found. + + {customers.map((customer) => ( + { + setFormField({ + ...formField, + customerid: customer.id + }); + setOpen(false); + }} + > + {customer.username} + + ))} + + + + + +
+
+
diff --git a/src/pages/settings/user/manage-user/blocks/EditDialog.tsx b/src/pages/settings/user/manage-user/blocks/EditDialog.tsx index 0d32ca6..5fc88a1 100644 --- a/src/pages/settings/user/manage-user/blocks/EditDialog.tsx +++ b/src/pages/settings/user/manage-user/blocks/EditDialog.tsx @@ -6,7 +6,15 @@ import { SelectTrigger, SelectValue } from '@/components/ui/select'; - +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList +} from '@/components/ui/command'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { Dialog, DialogBody, @@ -15,6 +23,7 @@ import { DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { CustomerProps } from './AddDialog'; import { useUserContext } from '../hooks'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; @@ -30,6 +39,7 @@ interface RoleListProps { status: string; } +const API_URL_CUSTOMER = apiConfig.service_customer; const API_URL = apiConfig.service_dashboard; const initialState = { @@ -37,7 +47,8 @@ const initialState = { username: '', email: '', id_role: '', - status: '' + status: '', + customerid: '' }; const EditDialog = () => { @@ -45,7 +56,9 @@ const EditDialog = () => { const { showEditDialog, selectedUser, handleEditDialog } = useUserContext(); const { reload } = useDataGrid(); const { GetData, PutData } = useCallApi(); + const [open, setOpen] = useState(false); const [roles, setRoles] = useState([]); + const [customers, setCustomers] = useState([]); const [alert, setAlert] = useState({ show: false, message: '' @@ -109,9 +122,27 @@ const EditDialog = () => { } }, []); + 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' + }); + + // console.log('CUSTOMER: ', response?.data); + setCustomers(response?.data.list); + } catch (error) { + console.error('Error fetching customer', error); + } + }; + const doFetchUserData = useCallback(async (id: string) => { const response = await GetData(`${API_URL}/user/detail/${id}`, { id }); - // console.log('User detail response:', response); + console.log('User detail response:', response?.data); if (response?.status) { setFormField((prev) => ({ @@ -120,8 +151,10 @@ const EditDialog = () => { username: response.data.username, email: response.data.email, id_role: response.data.idRole, - status: response.data.status + status: response.data.status, + customerid: response.data.customerid })); + console.log('Customer ID dari user detail:', response.data.customerid); } else { setFormField((prev) => ({ ...prev, @@ -129,7 +162,8 @@ const EditDialog = () => { username: '', email: '', id_role: '0', - status: '' + status: '', + customerid: '' })); } // console.log('Fetched ID Role:', response?.data.id_role); @@ -141,6 +175,10 @@ const EditDialog = () => { } }, [selectedUser]); + useEffect(() => { + getCustomerList([{ id: 'id', desc: false }]); + }, []); + useEffect(() => { if (showEditDialog === false) { resetForm(); @@ -192,6 +230,55 @@ const EditDialog = () => { )}
+
+
+ + + + + + + + + { + e.currentTarget.scrollTop += e.deltaY; + }} + > + No Customer found. + + {customers.map((customer) => ( + { + setFormField({ + ...formField, + customerid: customer.id + }); + setOpen(false); + }} + > + {customer.username} + + ))} + + + + + +
+
+
diff --git a/src/pages/settings/user/manage-user/hooks/ManageUserContext.tsx b/src/pages/settings/user/manage-user/hooks/ManageUserContext.tsx index 24cbc41..31c3c48 100644 --- a/src/pages/settings/user/manage-user/hooks/ManageUserContext.tsx +++ b/src/pages/settings/user/manage-user/hooks/ManageUserContext.tsx @@ -27,6 +27,7 @@ interface SelectedUser { role: string; new_password: string; check_new_password: string; + customer: string; } const initialProps: ContextProps = { @@ -83,6 +84,16 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode }) enableSorting: true, enableHiding: false }, + { + accessorFn: (row) => row.customer?.username, + id: 'customer', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + meta: { + headerClassName: 'w-[300px]' + } + }, { accessorFn: (row) => row.email, id: 'email', @@ -100,7 +111,7 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode }) enableSorting: false, enableHiding: false, meta: { - headerClassName: 'w-[350px]' + headerClassName: 'w-[250px]' } }, { @@ -179,7 +190,7 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode }) ); const doGetListData = async (page: number, limit: number, sorting: any, filter: any) => { - sorting = sorting.length == 0 ? [{ id: 'username', desc: false }] : sorting; + sorting = sorting.length == 0 ? [{ id: 'Users.username', desc: false }] : sorting; filter = filter.length == 0 ? {} : { any: filter[0].value.toLowerCase() }; const response = await GetData(`${API_URL}/user/list`, { limit: limit, @@ -189,7 +200,7 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode }) order_direction: sorting[0].desc == false ? 'ASC' : 'DESC', filter: JSON.stringify(filter) }); - console.log('response api:', response); + // console.log('response api:', response); return { data: response?.data.list, totalCount: response?.data.total_count }; }; @@ -215,7 +226,7 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode }) pagination={{ size: 10 }} toolbar={} layout={{ card: true }} - sorting={[{ id: 'username', desc: false }]} + sorting={[{ id: 'Users.username', desc: false }]} serverSide={true} onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => doGetListData(pageIndex, pageSize, sorting, columnFilters) From 2ebf7c9a0aafc286f68b1cf546a3403bb2b644af Mon Sep 17 00:00:00 2001 From: bagusajisaputroo Date: Sat, 12 Apr 2025 13:23:01 +0700 Subject: [PATCH 2/4] Adding permission on update transfertype --- .../transfer/transfertype/TransferType.tsx | 2 +- .../transfertype/blocks/EditDialog.tsx | 36 +++++++++++++------ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/pages/transfer/transfertype/TransferType.tsx b/src/pages/transfer/transfertype/TransferType.tsx index ab79eca..903669e 100644 --- a/src/pages/transfer/transfertype/TransferType.tsx +++ b/src/pages/transfer/transfertype/TransferType.tsx @@ -37,7 +37,7 @@ const TransferType = () => {
- {/* */} + diff --git a/src/pages/transfer/transfertype/blocks/EditDialog.tsx b/src/pages/transfer/transfertype/blocks/EditDialog.tsx index db7e391..82a757a 100644 --- a/src/pages/transfer/transfertype/blocks/EditDialog.tsx +++ b/src/pages/transfer/transfertype/blocks/EditDialog.tsx @@ -43,7 +43,7 @@ interface CustomerProps { msisdn: string; } -interface GroupProps { +interface PermissionObject { id: string; name: string; description: string; @@ -56,7 +56,7 @@ const EditDialog = () => { useManageTransferTypeContext(); const { reload } = useDataGrid(); const [wallets, setWallets] = useState([]); - const [groups, setGroups] = useState([]); + const [groups, setGroups] = useState([]); const { GetData, PutData } = useCallApi(); const [isSubmitting, setIsSubmitting] = useState(false); const parsedUser = getAuth()?.user; @@ -215,9 +215,13 @@ const EditDialog = () => { const doUpdateTransferType = useCallback(async () => { try { - const response = await PutData(`${API_URL}/transactiontype/update/${selectedTransferType}`, { - ...formField - }); + // Create a clean copy of the form data with properly formatted permissions + const formDataToSend = { + ...formField, + permission: formField.permission.filter(id => typeof id === 'string') + }; + + const response = await PutData(`${API_URL}/transactiontype/update/${selectedTransferType}`, formDataToSend); if (response?.status) { setAlert({ show: false, message: '' }); @@ -299,7 +303,7 @@ const EditDialog = () => { limit: 100, page: 1, with_deleted: false, - order_field: 'wallets.name', + order_field: 'Wallets.name', order_direction: 'ASC', }); @@ -321,8 +325,21 @@ const EditDialog = () => { const fetchTransactionType = async () => { try { const response = await GetData(`${API_URL}/transactiontype/getdata/${selectedTransferType}`, {}); - + console.log(response); if (response?.status) { + // Process permissions to ensure they're always string IDs + let permissionIds: string[] = []; + + if (Array.isArray(response.data.permission)) { + permissionIds = response.data.permission.map((perm: any) => { + // Check if permission is an object or string + if (typeof perm === 'object' && perm !== null) { + return perm.id; + } + return perm; + }); + } + setFormField((prev) => ({ ...prev, name: response.data.name, @@ -335,7 +352,7 @@ const EditDialog = () => { status_approval: response.data.status_approval, type: response.data.type || '', status: response.data.status, - permission: response.data.permission || [] + permission: permissionIds })); } } catch (error) { @@ -346,14 +363,13 @@ const EditDialog = () => { }); } }; - + const timer = setTimeout(() => { fetchTransactionType(); }, 150); return () => clearTimeout(timer); }, [showEditDialog, selectedTransferType, GetData]); - useEffect(() => { if (showEditDialog === false) { resetForm(); From a44bbcc77a247eff798bdb252e006f101da98de7 Mon Sep 17 00:00:00 2001 From: Raja Oktafrianto Date: Sat, 12 Apr 2025 13:27:16 +0700 Subject: [PATCH 3/4] update user & reward --- .../master/reward/blocks/DeleteDialog.tsx | 13 ++-- src/pages/master/reward/blocks/EditDialog.tsx | 8 ++- .../reward/hooks/ManageRewardContext.tsx | 39 ++++------- .../user/manage-user/blocks/DeleteDialog.tsx | 1 + .../user/manage-user/blocks/EditDialog.tsx | 70 ++++++++++--------- 5 files changed, 63 insertions(+), 68 deletions(-) diff --git a/src/pages/master/reward/blocks/DeleteDialog.tsx b/src/pages/master/reward/blocks/DeleteDialog.tsx index 8014795..4ece16d 100644 --- a/src/pages/master/reward/blocks/DeleteDialog.tsx +++ b/src/pages/master/reward/blocks/DeleteDialog.tsx @@ -34,24 +34,25 @@ const DeleteDialog = () => { return; } // console.log('Ini datanya:', selectedReward); - const response = await DeleteData(`${API_URL}/reward/delete/${selectedReward?.id}/true`, { - id: selectedReward.id + const response = await DeleteData(`${API_URL}/reward/delete/${selectedReward}/true`, { + id: selectedReward }); + // console.log('Response Delete:', response); if (response?.status) { - setAlert({ show: false, message: '' }); + setAlert((prev) => ({ ...prev, show: false, message: '' })); handleDeleteDialog(false, null); toast.success('Success Delete Reward'); reload(); - const deleteActivity = { + const createActivity = { module: 'Manage Reward', description: `Delete Reward => ${selectedReward}`, action: 'D' }; - doSaveLogActivity(deleteActivity); + doSaveLogActivity(createActivity); } else { - setAlert({ show: true, message: response?.message }); + setAlert((prev) => ({ ...prev, show: true, message: response?.message })); toast.error('Failed Delete Reward'); } }, [selectedReward, DeleteData, handleDeleteDialog, reload]); diff --git a/src/pages/master/reward/blocks/EditDialog.tsx b/src/pages/master/reward/blocks/EditDialog.tsx index 817dfb3..d31989d 100644 --- a/src/pages/master/reward/blocks/EditDialog.tsx +++ b/src/pages/master/reward/blocks/EditDialog.tsx @@ -58,7 +58,9 @@ const EditDialog = () => { const RewardType = { 'Daily Check in': 'D', - Referal: 'R' + Referal: 'R', + 'Level Pro': 'P', + 'Level Prioritas': 'L' } as const; type RewardTypeValue = (typeof RewardType)[keyof typeof RewardType]; @@ -68,7 +70,7 @@ const EditDialog = () => { e.preventDefault(); // console.log('Ini datanya:', selectedReward); - const response = await PutData(`${API_URL}/reward/update/${selectedReward?.id}`, formField); + const response = await PutData(`${API_URL}/reward/update/${selectedReward}`, formField); if (response?.status) { resetForm(); @@ -126,7 +128,7 @@ const EditDialog = () => { useEffect(() => { if (selectedReward) { - doFetchData(selectedReward.id.toString()); + doFetchData(selectedReward); } }, [selectedReward]); diff --git a/src/pages/master/reward/hooks/ManageRewardContext.tsx b/src/pages/master/reward/hooks/ManageRewardContext.tsx index 97c0130..32b8116 100644 --- a/src/pages/master/reward/hooks/ManageRewardContext.tsx +++ b/src/pages/master/reward/hooks/ManageRewardContext.tsx @@ -6,7 +6,7 @@ import React, { createContext, useCallback, useMemo, useState } from 'react'; import { Toaster } from 'sonner'; import ListToolbar from '../blocks/ListToolbar'; -interface Reward { +interface SelectedReward { id: number; name: string; type: string; @@ -14,23 +14,14 @@ interface Reward { status: string; } -const initialState = { - id: '', - name: '', - type: '', - amount: '', - status: '' -}; - interface ContextProps { showAddDialog: boolean; handleAddDialog: (show: boolean) => void; showEditDialog: boolean; - handleEditDialog: (show: boolean, selected_reward: Reward | null) => void; + handleEditDialog: (show: boolean, selected_reward: string | null) => void; showDeleteDialog: boolean; - handleDeleteDialog: (show: boolean, selected_rewad: Reward | null) => void; - selectedReward: Reward | null; - reward: string | null; + handleDeleteDialog: (show: boolean, selected_reward: string | null) => void; + selectedReward: string | null; } const initialProps: ContextProps = { @@ -40,31 +31,29 @@ const initialProps: ContextProps = { handleEditDialog: () => {}, showDeleteDialog: false, handleDeleteDialog: () => {}, - selectedReward: null, - reward: null + selectedReward: null }; const ManageRewardContext = createContext(initialProps); const API_URL_MASTER_DATA = apiConfig.service_master_data; const ManageRewardContextProvider = ({ children }: { children: React.ReactNode }) => { - const [reward, setRewards] = useState(null); const [showAddDialog, setShowAddDialog] = useState(false); const [showEditDialog, setShowEditDialog] = useState(false); const [showDeleteDialog, setShowDeleteDialog] = useState(false); - const [selectedReward, setSelectedReward] = useState(null); + const [selectedReward, setSelectedReward] = useState(null); const { GetData } = useCallApi(); const handleAddDialog = useCallback((show: boolean) => { setShowAddDialog(show); }, []); - const handleEditDialog = useCallback((show: boolean, selected_reward: Reward | null) => { + const handleEditDialog = useCallback((show: boolean, selected_reward: string | null) => { setShowEditDialog(show); setSelectedReward(show ? selected_reward : null); }, []); - const handleDeleteDialog = useCallback((show: boolean, selected_reward: Reward | null) => { + const handleDeleteDialog = useCallback((show: boolean, selected_reward: string | null) => { setShowDeleteDialog(show); setSelectedReward(show ? selected_reward : null); }, []); @@ -135,13 +124,13 @@ const ManageRewardContextProvider = ({ children }: { children: React.ReactNode } <> @@ -154,7 +143,7 @@ const ManageRewardContextProvider = ({ children }: { children: React.ReactNode } } } ], - [] + [handleEditDialog, handleDeleteDialog] ); const getRewardList = async (page: number, limit: number, sorting: any, filter: any) => { @@ -162,7 +151,7 @@ const ManageRewardContextProvider = ({ children }: { children: React.ReactNode } sorting = sorting.length == 0 ? [{ id: 'name', desc: false }] : sorting; filter = filter.length == 0 ? {} : { any: filter[0].value?.toLowerCase() }; const response = await GetData(`${API_URL_MASTER_DATA}/reward/list`, { - limit, + limit: limit, page: page + 1, with_deleted: false, order_field: sorting[0].id, @@ -171,7 +160,6 @@ const ManageRewardContextProvider = ({ children }: { children: React.ReactNode } }); // console.log('API Response:', response?.data.list); // console.log('reward list :', response); - setRewards(response?.data.list); return { data: response?.data.list, totalCount: response?.data.total_count }; } catch (error) { console.error('Error fethcing reward', error); @@ -181,7 +169,6 @@ const ManageRewardContextProvider = ({ children }: { children: React.ReactNode } return ( { const response = await DeleteData(`${API_URL}/user/delete/${selectedUser}/${enforce}`, { id: selectedUser }); + // console.log('Delete Response User:', response); if (response?.status) { setAlert((prev) => ({ ...prev, show: false, message: '' })); handleDeleteDialog(false, null); diff --git a/src/pages/settings/user/manage-user/blocks/EditDialog.tsx b/src/pages/settings/user/manage-user/blocks/EditDialog.tsx index 5fc88a1..ecf99cd 100644 --- a/src/pages/settings/user/manage-user/blocks/EditDialog.tsx +++ b/src/pages/settings/user/manage-user/blocks/EditDialog.tsx @@ -25,6 +25,7 @@ import { } from '@/components/ui/dialog'; import { CustomerProps } from './AddDialog'; import { useUserContext } from '../hooks'; +import { ChevronDown } from 'lucide-react'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { apiConfig } from '@/config/api.config'; @@ -142,7 +143,7 @@ const EditDialog = () => { const doFetchUserData = useCallback(async (id: string) => { const response = await GetData(`${API_URL}/user/detail/${id}`, { id }); - console.log('User detail response:', response?.data); + // console.log('User detail response:', response?.data); if (response?.status) { setFormField((prev) => ({ @@ -154,7 +155,7 @@ const EditDialog = () => { status: response.data.status, customerid: response.data.customerid })); - console.log('Customer ID dari user detail:', response.data.customerid); + // console.log('Customer ID dari user detail:', response.data.customerid); } else { setFormField((prev) => ({ ...prev, @@ -230,6 +231,34 @@ const EditDialog = () => { )}
+
+
+ + + setFormField((prev) => ({ ...prev, name: target.value })) + } + /> +
+
+ +
+
+ + + setFormField((prev) => ({ ...prev, username: target.value })) + } + /> +
+
+
@@ -237,11 +266,14 @@ const EditDialog = () => { @@ -279,34 +311,6 @@ const EditDialog = () => {
-
-
- - - setFormField((prev) => ({ ...prev, name: target.value })) - } - /> -
-
- -
-
- - - setFormField((prev) => ({ ...prev, username: target.value })) - } - /> -
-
-
From a773b7c558f3484721ce7fbc0d8d845093e021d4 Mon Sep 17 00:00:00 2001 From: bagusajisaputroo Date: Sun, 13 Apr 2025 11:46:49 +0700 Subject: [PATCH 4/4] fixing Add+ delete+ managetransferfee --- .../transfer/transferfee/blocks/AddDialog.tsx | 293 ++++++- .../transferfee/blocks/DeleteDialog.tsx | 6 +- .../transferfee/blocks/EditDialog.tsx | 807 +++++++++++------- .../hooks/ManageTransferFeeContext.tsx | 109 ++- .../transfertype/blocks/EditDialog.tsx | 4 + 5 files changed, 838 insertions(+), 381 deletions(-) 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 = () => {
+ + {/* */}