From 3812e73b31c245191aacb9f5815f2f7eb02deab5 Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Wed, 19 Mar 2025 14:51:31 +0700 Subject: [PATCH 01/14] add dropdown for form --- .../master/products/blocks/AddDialog.tsx | 96 ++++++++++++++++--- 1 file changed, 85 insertions(+), 11 deletions(-) diff --git a/src/pages/master/products/blocks/AddDialog.tsx b/src/pages/master/products/blocks/AddDialog.tsx index 3cdcf3c..1b8fc20 100644 --- a/src/pages/master/products/blocks/AddDialog.tsx +++ b/src/pages/master/products/blocks/AddDialog.tsx @@ -15,13 +15,25 @@ import { } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from '@/components/ui/select'; + +interface ProviderProps { + id: number; + name: string; +} const API_URL = apiConfig.service_master_data; const AddDialog = () => { const parentRef = useRef(null); const parsedUser = getAuth()?.user; const { showAddDialog, handleAddDialog } = useManageProductsContext(); - const { PostData } = useCallApi(); + const { PostData, GetData } = useCallApi(); const { reload } = useDataGrid(); const [alert, setAlert] = useState({ show: false, @@ -36,10 +48,12 @@ const AddDialog = () => { cashback_cash: 0, status: '', providerId: '', + process_on_third_party: '', created_by: '', created_at: '' }; const [formField, setFormField] = useState(initialState); + const [providers, setProviders] = useState([]); const created_time = new Date(); const formattedTime = created_time.toISOString().slice(0, 19).replace('T', ' '); @@ -67,6 +81,23 @@ const AddDialog = () => { [formField] ); + const doFetchProvider = useCallback(async (sorting: any) => { + try { + const response = await GetData(`${API_URL}/provider/list`, { + limit: 100, + page: 1, + with_deleted: false, + order_field: sorting[0].id, + order_direction: sorting[0].desc == false ? 'ASC' : 'DESC' + }); + + console.log('PROVIDER: ', response?.data); + setProviders(response?.data.list); + } catch (error) { + console.error('Error fetching provider', error); + } + }, []); + const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); @@ -90,6 +121,10 @@ const AddDialog = () => { setAlert({ show: false, message: '' }); }; + useEffect(() => { + doFetchProvider([{ id: 'id', desc: false }]); + }, []); + useEffect(() => { if (showAddDialog) { setFormField({ @@ -232,12 +267,18 @@ const AddDialog = () => { - setFormField({ ...formField, status: e.target.value })} - /> + onValueChange={(value) => setFormField({ ...formField, status: value })} + > + + + + + Yes + No + + @@ -246,12 +287,45 @@ const AddDialog = () => { - setFormField({ ...formField, providerId: e.target.value })} - /> + onValueChange={(value) => + setFormField({ ...formField, providerId: value.toString() }) + } + > + + + + + {providers.map((provider) => ( + + {provider.name} + + ))} + + + + + +
+
+ +
From ac3ccbdaba3ebea7353e1aa755613a7e53f495bd Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Thu, 20 Mar 2025 10:15:01 +0700 Subject: [PATCH 02/14] fix fill edit formField --- .../master/profession/blocks/EditDialog.tsx | 27 ++++- .../master/provider/blocks/EditDialog.tsx | 107 +++++++++--------- 2 files changed, 81 insertions(+), 53 deletions(-) diff --git a/src/pages/master/profession/blocks/EditDialog.tsx b/src/pages/master/profession/blocks/EditDialog.tsx index 7dfc06c..ffdc02a 100644 --- a/src/pages/master/profession/blocks/EditDialog.tsx +++ b/src/pages/master/profession/blocks/EditDialog.tsx @@ -20,7 +20,7 @@ const API_URL = apiConfig.service_master_data; const EditDialog = () => { const { showEditDialog, handleEditDialog, selectedProfession } = useManageProfessionContext(); const { reload } = useDataGrid(); - const { PutData } = useCallApi(); + const { PutData, GetData } = useCallApi(); const parsedUser = getAuth()?.user; const created_time = new Date(); const formattedTime = created_time.toISOString().slice(0, 19).replace('T', ' '); @@ -62,6 +62,19 @@ const EditDialog = () => { [selectedProfession, formField] ); + const doFetchData = useCallback(async (id: string) => { + const response = await GetData(`${API_URL}/profession/getdata/${id}`, { id }); + + if (response?.status) { + setFormField((prev) => ({ + ...prev, + name: response.data.name + })); + } else { + setFormField(initialState); + } + }, []); + const handleUpdate = (e: React.FormEvent) => { e.preventDefault(); @@ -75,6 +88,12 @@ const EditDialog = () => { setAlert({ show: false, message: '' }); }; + useEffect(() => { + if (selectedProfession) { + doFetchData(selectedProfession); + } + }, [selectedProfession]); + useEffect(() => { if (showEditDialog) { setFormField({ @@ -85,6 +104,12 @@ const EditDialog = () => { } }, [formattedTime]); + useEffect(() => { + if (showEditDialog === false) { + resetForm(); + } + }, [showEditDialog]); + return ( handleEditDialog(open, null)}> diff --git a/src/pages/master/provider/blocks/EditDialog.tsx b/src/pages/master/provider/blocks/EditDialog.tsx index 5fa37c2..e7c55d0 100644 --- a/src/pages/master/provider/blocks/EditDialog.tsx +++ b/src/pages/master/provider/blocks/EditDialog.tsx @@ -91,22 +91,54 @@ const EditDialog = () => { [selectedProvider, formField] ); - const doFetchData = useCallback(async () => { - if (selectedProvider) { - const data = provider.find((item) => item.id === selectedProvider); - if (data) { - setFormField((prev) => ({ - ...prev, - name: data.name, - description: data.description, - type: data.type, - status: data.status, - transactionTypeId: data.transactionTypeId, - agentId: data.agentId - })); - } - } else { - resetForm(); + 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 getTransactionTypeList = async (sorting: any) => { + try { + const response = await GetData(`${API_URL_TRANSACTION}/transactiontype/list`, { + limit: 100, + page: 1, + with_deleted: false, + order_field: sorting[0].id, + order_direction: sorting[0].desc == false ? 'ASC' : 'DESC' + }); + + // console.log('TRANSACTION TYPE: ', response?.data); + setTransactions(response?.data.list); + } catch (error) { + console.error('Error fetching transaction type', error); + } + }; + + const doFetchData = useCallback(async (id: string) => { + const response = await GetData(`${API_URL_MASTERDATA}/provider/getdata/${id}`, { id }); + + if (response?.status) { + setFormField((prev) => ({ + ...prev, + name: response?.data.name, + description: response?.data.description, + type: response?.data.type, + status: response?.data.status, + transactionTypeId: response?.data.transactionTypeId, + agentId: response?.data.agentId + })); } }, []); @@ -130,9 +162,15 @@ const EditDialog = () => { setAlert({ show: false, message: '' }); }; + useEffect(() => { + if (showEditDialog === false) { + resetForm(); + } + }, [showEditDialog]); + useEffect(() => { if (selectedProvider) { - doFetchData(); + doFetchData(selectedProvider); } }, [selectedProvider]); @@ -147,41 +185,6 @@ const EditDialog = () => { }, [formattedTime]); useEffect(() => { - 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 getTransactionTypeList = async (sorting: any) => { - try { - const response = await GetData(`${API_URL_TRANSACTION}/transactiontype/list`, { - limit: 100, - page: 1, - with_deleted: false, - order_field: sorting[0].id, - order_direction: sorting[0].desc == false ? 'ASC' : 'DESC' - }); - - // console.log('TRANSACTION TYPE: ', response?.data); - setTransactions(response?.data.list); - } catch (error) { - console.error('Error fetching transaction type', error); - } - }; - getCustomerList([{ id: 'msisdn', desc: false }]); getTransactionTypeList([{ id: 'name', desc: false }]); }, []); From 69d9fc66cbd2a876b31e510ae3d58ab5eaec8ea8 Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Thu, 20 Mar 2025 14:40:18 +0700 Subject: [PATCH 03/14] add form validation to prevent empty input --- .../settings/user/manage-position/blocks/AddDialog.tsx | 6 ++++++ .../settings/user/manage-position/blocks/EditDialog.tsx | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/pages/settings/user/manage-position/blocks/AddDialog.tsx b/src/pages/settings/user/manage-position/blocks/AddDialog.tsx index 1c0dd94..6c32480 100644 --- a/src/pages/settings/user/manage-position/blocks/AddDialog.tsx +++ b/src/pages/settings/user/manage-position/blocks/AddDialog.tsx @@ -83,6 +83,12 @@ const AddDialog = () => { const doCreatePosition = useCallback( async (e: React.FormEvent) => { e.preventDefault(); + + if (formField.name.trim() === '') { + setAlert({ show: true, message: 'Please fill in all required fields.' }); + return; + } + const response = await PostData(`${API_URL}/user_role/create`, { name: formField.name, roles: selectMenus, diff --git a/src/pages/settings/user/manage-position/blocks/EditDialog.tsx b/src/pages/settings/user/manage-position/blocks/EditDialog.tsx index 4209f2e..d000fac 100644 --- a/src/pages/settings/user/manage-position/blocks/EditDialog.tsx +++ b/src/pages/settings/user/manage-position/blocks/EditDialog.tsx @@ -91,6 +91,12 @@ const EditDialog = () => { const doEditPosition = useCallback( async (e: React.FormEvent) => { e.preventDefault(); + + if (formField.name.trim() === '') { + setAlert((prev) => ({ ...prev, show: true, message: 'Please fill name field.' })); + return; + } + if (!selectedPosition) { toast.success('Please Select Position'); return; From d6807468d4860923bfae1facbf1f62bd3b010c80 Mon Sep 17 00:00:00 2001 From: Raja Oktafrianto Date: Thu, 20 Mar 2025 15:08:50 +0700 Subject: [PATCH 04/14] validate logout --- src/components/ui/DataTable.tsx | 20 ++-- .../account/manage-account/ManageAccount.tsx | 4 +- src/pages/groups/ManageGroups.tsx | 2 +- src/pages/members/kyc/Kyc.tsx | 2 +- src/pages/members/manage-members/Columns.tsx | 96 +++++++++---------- .../members/manage-members/ManageMembers.tsx | 2 +- src/pages/menu/manage-menu/ManageMenu.tsx | 4 +- src/partials/dropdowns/user/DropdownUser.tsx | 34 ++++++- 8 files changed, 98 insertions(+), 66 deletions(-) diff --git a/src/components/ui/DataTable.tsx b/src/components/ui/DataTable.tsx index 8908c92..d4f1e94 100644 --- a/src/components/ui/DataTable.tsx +++ b/src/components/ui/DataTable.tsx @@ -30,7 +30,7 @@ import { DialogDescription, DialogHeader, DialogTitle, - DialogTrigger, + DialogTrigger } from './dialog'; interface DataTableProps { @@ -67,7 +67,7 @@ export function DataTable({ return (
-
+
({
- { - onDelete ? ( - - ) : ('') - } + ) : ( + '' + )}
diff --git a/src/pages/account/manage-account/ManageAccount.tsx b/src/pages/account/manage-account/ManageAccount.tsx index f6398dc..f116edd 100644 --- a/src/pages/account/manage-account/ManageAccount.tsx +++ b/src/pages/account/manage-account/ManageAccount.tsx @@ -1,11 +1,11 @@ import { apiConfig } from '@/config/api.config'; -import { Account, columns } from './Columns'; import axios from 'axios'; import { useEffect, useState } from 'react'; import { getData } from '@/utils'; import { useCallApi } from '@/hooks'; import { DataTable } from '@/components/ui/DataTable'; import { Breadcrumbs, Link } from '@mui/material'; +import { Account, columns } from './Columns'; const API_URL = apiConfig.service_dashboard; @@ -75,7 +75,7 @@ const ManageAccount = () => { return (
-
+

Manage Account

diff --git a/src/pages/groups/ManageGroups.tsx b/src/pages/groups/ManageGroups.tsx index 3b5a832..3be46e2 100644 --- a/src/pages/groups/ManageGroups.tsx +++ b/src/pages/groups/ManageGroups.tsx @@ -182,7 +182,7 @@ const ManageGroups = () => { onDelete={handleDelete} /> - +
Create New Group diff --git a/src/pages/members/kyc/Kyc.tsx b/src/pages/members/kyc/Kyc.tsx index 6ee1148..6291aa7 100644 --- a/src/pages/members/kyc/Kyc.tsx +++ b/src/pages/members/kyc/Kyc.tsx @@ -142,7 +142,7 @@ const Kyc = () => { return (
-
+
setDialogOpen(false)} diff --git a/src/pages/members/manage-members/Columns.tsx b/src/pages/members/manage-members/Columns.tsx index 8a08b94..e657553 100644 --- a/src/pages/members/manage-members/Columns.tsx +++ b/src/pages/members/manage-members/Columns.tsx @@ -132,51 +132,51 @@ export const columns: ColumnDef[] = [ ]; export const initialMember = { - id: "", - fullname: "", - email: "", - username: "", - msisdn: "", - password: "", - pin: "", - try_pin: "", - mother_fullname: "", - agent_name: "", - bank_name: "", - bank_account: "", - ibank_number: "", - address: "", - longitude: "", - latitude: "", - nationality: "", - photouser: "", - photomerchant: "", - file_selfie: "", - file_document_id: "", - file_document_id_selfie: "", - file_commercial_license: "", - identity_type: "", - identity_number: "", - license_number: "", - date_birth: "", - gender: "", - status: "N", - isneedapproval: "", - isapproved: "", - approveddate: "", - approvedby: "", - created_by: "", - created_at: "", - updated_by: "", - updated_at: "", - deleted_by: "", - deleted_at: "", - group: "", - point_tier: "", - language: "", - municipio: "", - posto_adms: "", - suco: "", - aldeia: "", - profession: "", -} + id: '', + fullname: '', + email: '', + username: '', + msisdn: '', + password: '', + pin: '', + try_pin: '', + mother_fullname: '', + agent_name: '', + bank_name: '', + bank_account: '', + ibank_number: '', + address: '', + longitude: '', + latitude: '', + nationality: '', + photouser: '', + photomerchant: '', + file_selfie: '', + file_document_id: '', + file_document_id_selfie: '', + file_commercial_license: '', + identity_type: '', + identity_number: '', + license_number: '', + date_birth: '', + gender: '', + status: 'N', + isneedapproval: '', + isapproved: '', + approveddate: '', + approvedby: '', + created_by: '', + created_at: '', + updated_by: '', + updated_at: '', + deleted_by: '', + deleted_at: '', + group: '', + point_tier: '', + language: '', + municipio: '', + posto_adms: '', + suco: '', + aldeia: '', + profession: '' +}; diff --git a/src/pages/members/manage-members/ManageMembers.tsx b/src/pages/members/manage-members/ManageMembers.tsx index e3ede28..fb8e77b 100644 --- a/src/pages/members/manage-members/ManageMembers.tsx +++ b/src/pages/members/manage-members/ManageMembers.tsx @@ -115,7 +115,7 @@ const ManageMembers = () => { return (
-
+
setDialogOpen(false)} diff --git a/src/pages/menu/manage-menu/ManageMenu.tsx b/src/pages/menu/manage-menu/ManageMenu.tsx index 7dac58a..c0849a7 100644 --- a/src/pages/menu/manage-menu/ManageMenu.tsx +++ b/src/pages/menu/manage-menu/ManageMenu.tsx @@ -9,7 +9,7 @@ const ManageMenu = () => { return ( -

Manage Menus

+

Manage Menu

Dashboard @@ -20,7 +20,7 @@ const ManageMenu = () => { - Manage Menus + Manage Menu
diff --git a/src/partials/dropdowns/user/DropdownUser.tsx b/src/partials/dropdowns/user/DropdownUser.tsx index 266372d..0ccf0eb 100644 --- a/src/partials/dropdowns/user/DropdownUser.tsx +++ b/src/partials/dropdowns/user/DropdownUser.tsx @@ -1,4 +1,4 @@ -import { Fragment } from 'react'; +import { Fragment, useState } from 'react'; import { Link } from 'react-router-dom'; import { FormattedMessage } from 'react-intl'; import { getAuth, useAuthContext } from '@/auth'; @@ -15,6 +15,8 @@ import { MenuArrow, MenuIcon } from '@/components/menu'; +import { Alert, Button, Dialog, DialogContent } from '@mui/material'; +import { DialogFooter, DialogHeader } from '@/components/ui/dialog'; interface IDropdownUserProps { menuItemRef: any; @@ -26,6 +28,12 @@ interface IDropdownUserProps { const DropdownUser = ({ menuItemRef }: IDropdownUserProps) => { const { logout, auth } = useAuthContext(); + const [showLogoutDialog, setShowLogoutDialog] = useState(false); + + const handleLogout = () => { + setShowLogoutDialog(false); // Tutup dialog + logout(); // Panggil fungsi logout + }; const buildHeader = () => { return ( @@ -73,7 +81,10 @@ const DropdownUser = ({ menuItemRef }: IDropdownUserProps) => { return (
@@ -89,6 +100,25 @@ const DropdownUser = ({ menuItemRef }: IDropdownUserProps) => { {buildHeader()} {buildMenu()} {buildFooter()} + + setShowLogoutDialog(false)}> + + + +

Are you sure?

+ You are about to log out! +
+
+ + + + +
+
); }; From 50810f539ce487a69b69c076814e65ad70de7794 Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Fri, 21 Mar 2025 09:43:50 +0700 Subject: [PATCH 05/14] fix form add type and code field --- .../master/products/blocks/AddDialog.tsx | 42 +++++- .../master/products/blocks/EditDialog.tsx | 138 ++++++++++++++++-- 2 files changed, 160 insertions(+), 20 deletions(-) diff --git a/src/pages/master/products/blocks/AddDialog.tsx b/src/pages/master/products/blocks/AddDialog.tsx index 1b8fc20..a854203 100644 --- a/src/pages/master/products/blocks/AddDialog.tsx +++ b/src/pages/master/products/blocks/AddDialog.tsx @@ -41,13 +41,15 @@ const AddDialog = () => { }); const initialState = { name: '', + code: '', + type: '', description: '', price_point: 0, price_cash: 0, cashback_point: 0, cashback_cash: 0, status: '', - providerId: '', + provider: '', process_on_third_party: '', created_by: '', created_at: '' @@ -91,7 +93,7 @@ const AddDialog = () => { order_direction: sorting[0].desc == false ? 'ASC' : 'DESC' }); - console.log('PROVIDER: ', response?.data); + // console.log('PROVIDER: ', response?.data); setProviders(response?.data.list); } catch (error) { console.error('Error fetching provider', error); @@ -103,6 +105,8 @@ const AddDialog = () => { if ( formField.name.trim() === '' || + formField.type.trim() === '' || + formField.code.trim() === '' || formField.description.trim() === '' || formField.price_point === 0 || formField.price_cash === 0 || @@ -148,7 +152,7 @@ const AddDialog = () => { Product - Create - +
{alert.show && ( @@ -172,6 +176,34 @@ const AddDialog = () => {
+
+
+ + setFormField({ ...formField, type: e.target.value })} + /> +
+
+ +
+
+ + setFormField({ ...formField, code: e.target.value })} + /> +
+
+
setFormField({ ...formField, type: e.target.value })} + /> +
+
+ +
+
+ + setFormField({ ...formField, code: e.target.value })} + /> +
+
+
@@ -270,12 +347,43 @@ const EditDialog = () => { - setFormField({ ...formField, providerId: e.target.value })} - /> + +
+
+ +
+
+ +
From 65f966081d4b0c6ad98a3a881985f448cd7a5f85 Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Fri, 21 Mar 2025 09:49:09 +0700 Subject: [PATCH 06/14] fix padding --- src/pages/groups/ManageGroups.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/groups/ManageGroups.tsx b/src/pages/groups/ManageGroups.tsx index 3be46e2..819ad82 100644 --- a/src/pages/groups/ManageGroups.tsx +++ b/src/pages/groups/ManageGroups.tsx @@ -148,7 +148,7 @@ const ManageGroups = () => { return (
-
+
setDialogOpen(false)} From 164d6dc5aec67dd378c431cae0262612608826ef Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Fri, 21 Mar 2025 09:49:39 +0700 Subject: [PATCH 07/14] fix padding --- src/pages/groups/ManageGroups.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/groups/ManageGroups.tsx b/src/pages/groups/ManageGroups.tsx index 3be46e2..819ad82 100644 --- a/src/pages/groups/ManageGroups.tsx +++ b/src/pages/groups/ManageGroups.tsx @@ -148,7 +148,7 @@ const ManageGroups = () => { return (
-
+
setDialogOpen(false)} From 90dc351393fdbf92d432333442bd6a7ac6dd5071 Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Fri, 21 Mar 2025 13:29:56 +0700 Subject: [PATCH 08/14] remove filter icon --- .../master/aldeias/blocks/ListToolbar.tsx | 10 ++-- .../master/conversion/blocks/ListToolbar.tsx | 55 +++++++++++++++++++ .../master/municipios/blocks/ListToolbar.tsx | 6 +- .../master/postoadms/blocks/ListToolbar.tsx | 6 +- .../master/products/blocks/ListToolbar.tsx | 6 +- .../master/profession/blocks/ListToolbar.tsx | 6 +- .../master/provider/blocks/ListToolbar.tsx | 6 +- src/pages/master/sucos/blocks/ListToolbar.tsx | 6 +- 8 files changed, 78 insertions(+), 23 deletions(-) create mode 100644 src/pages/master/conversion/blocks/ListToolbar.tsx diff --git a/src/pages/master/aldeias/blocks/ListToolbar.tsx b/src/pages/master/aldeias/blocks/ListToolbar.tsx index 70ababf..fbf8537 100644 --- a/src/pages/master/aldeias/blocks/ListToolbar.tsx +++ b/src/pages/master/aldeias/blocks/ListToolbar.tsx @@ -20,17 +20,17 @@ const ListToolbar = () => { onChange={(event) => table.getColumn('name')?.setFilterValue(event.target.value)} /> - + {/* - + */} {/* + */} +
+
+ + + + +
+
+
+
+ ); +}; + +export default ListToolbar; diff --git a/src/pages/master/municipios/blocks/ListToolbar.tsx b/src/pages/master/municipios/blocks/ListToolbar.tsx index 68dd8fe..4c5a633 100644 --- a/src/pages/master/municipios/blocks/ListToolbar.tsx +++ b/src/pages/master/municipios/blocks/ListToolbar.tsx @@ -33,17 +33,17 @@ const ListToolbar = () => { onChange={(event) => table.getColumn('name')?.setFilterValue(event.target.value)} /> - + {/* - + */} {/* - + */} {/* - + */}
- + */}
- + */}
- + */} {/*
@@ -184,8 +186,10 @@ const AddDialog = () => { setFormField({ ...formField, type: e.target.value })} + value={formField.products_type} + onChange={(e) => + setFormField({ ...formField, products_type: e.target.value }) + } />
@@ -198,8 +202,10 @@ const AddDialog = () => { setFormField({ ...formField, code: e.target.value })} + value={formField.products_code} + onChange={(e) => + setFormField({ ...formField, products_code: e.target.value }) + } />
@@ -212,8 +218,10 @@ const AddDialog = () => { setFormField({ ...formField, description: e.target.value })} + value={formField.products_description} + onChange={(e) => + setFormField({ ...formField, products_description: e.target.value }) + } />
@@ -228,10 +236,13 @@ const AddDialog = () => { type="number" min={0} step={0.01} - value={formField.price_point} + value={formField.products_price_point} onChange={(e) => { const value = parseFloat(e.target.value); - setFormField({ ...formField, price_point: isNaN(value) ? 0 : value }); + setFormField({ + ...formField, + products_price_point: isNaN(value) ? 0 : value + }); }} />
@@ -247,10 +258,13 @@ const AddDialog = () => { type="number" min={0} step={0.01} - value={formField.price_cash} + value={formField.products_price_cash} onChange={(e) => { const value = parseFloat(e.target.value); - setFormField({ ...formField, price_cash: isNaN(value) ? 0 : value }); + setFormField({ + ...formField, + products_price_cash: isNaN(value) ? 0 : value + }); }} />
@@ -266,10 +280,13 @@ const AddDialog = () => { type="number" min={0} step={0.01} - value={formField.cashback_point} + value={formField.products_cashback_point} onChange={(e) => { const value = parseFloat(e.target.value); - setFormField({ ...formField, cashback_point: isNaN(value) ? 0 : value }); + setFormField({ + ...formField, + products_cashback_point: isNaN(value) ? 0 : value + }); }} />
@@ -285,10 +302,13 @@ const AddDialog = () => { type="number" min={0} step={0.01} - value={formField.cashback_cash} + value={formField.products_cashback_cash} onChange={(e) => { const value = parseFloat(e.target.value); - setFormField({ ...formField, cashback_cash: isNaN(value) ? 0 : value }); + setFormField({ + ...formField, + products_cashback_cash: isNaN(value) ? 0 : value + }); }} />
@@ -300,8 +320,10 @@ const AddDialog = () => { Status* - setFormField({ ...formField, provider: value.toString() }) + setFormField({ ...formField, products_provider: value.toString() }) } > @@ -330,8 +352,11 @@ const AddDialog = () => { {providers.map((provider) => ( - - {provider.name} + + {provider.provider_name} ))} @@ -345,9 +370,9 @@ const AddDialog = () => { Process on Third Party* table.getColumn('name')?.setFilterValue(event.target.value)} + /> + + {/* + + */} +
+
+ + + + +
+
+
+ + ); +}; + +export default ListToolbar; diff --git a/src/pages/master/walletRule/hooks/ManageWalletRuleContext.tsx b/src/pages/master/walletRule/hooks/ManageWalletRuleContext.tsx new file mode 100644 index 0000000..e196eaa --- /dev/null +++ b/src/pages/master/walletRule/hooks/ManageWalletRuleContext.tsx @@ -0,0 +1,240 @@ +import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components'; +import { Toaster } from '@/components/ui/sonner'; +import { apiConfig } from '@/config/api.config'; +import { useCallApi } from '@/hooks'; +import { ColumnDef } from '@tanstack/react-table'; +import React, { createContext, useCallback, useMemo, useState } from 'react'; +import ListToolbar from '../blocks/ListToolbar'; + +interface WalletRuleProps { + name: string; + id_currency: string; + status: string; +} + +interface ContextProps { + walletRules: WalletRuleProps[]; + showAddDialog: boolean; + handleAddDialog: (show: boolean) => void; + showEditDialog: boolean; + handleEditDialog: (show: boolean, selected_walletRule: string | null) => void; + showDeleteDialog: boolean; + handleDeleteDialog: (show: boolean, selected_walletRule: string | null) => void; + selectedWalletRule: string | null; + getWalletRuleLists: ( + limit: number, + page: number, + with_deleted: boolean, + order_field: any, + order_direction: any + ) => Promise<{ data: WalletRuleProps[]; totalCount: number } | undefined>; +} + +const initialProps: ContextProps = { + walletRules: [], + showAddDialog: false, + handleAddDialog: (show: boolean) => {}, + showEditDialog: false, + handleEditDialog: (show: boolean, selected_walletRule: string | null) => {}, + showDeleteDialog: false, + handleDeleteDialog: (show: boolean, selected_walletRule: string | null) => {}, + selectedWalletRule: null, + getWalletRuleLists: async () => undefined +}; + +const ManageWalletRuleContext = createContext(initialProps); +const API_URL_WALLET = apiConfig.service_wallet; + +const ManageWalletRuleContextProvider = ({ children }: { children: React.ReactNode }) => { + const [walletRules, setWalletRules] = useState([]); + const [showAddDialog, setShowAddDialog] = useState(false); + const [showEditDialog, setShowEditDialog] = useState(false); + const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const [selectedWalletRule, setSelectedWalletRule] = useState(null); + const { GetData } = useCallApi(); + + const handleAddDialog = useCallback((show: boolean) => { + setShowAddDialog(show); + }, []); + + const handleEditDialog = useCallback((show: boolean, selected_walletRule: string | null) => { + setShowEditDialog(show); + setSelectedWalletRule(show ? selected_walletRule : null); + }, []); + + const handleDeleteDialog = useCallback((show: boolean, selected_walletRule: string | null) => { + setShowDeleteDialog(show); + setSelectedWalletRule(show ? selected_walletRule : null); + }, []); + + const columns = useMemo[]>( + () => [ + { + accessorFn: (row) => row.ID, + id: 'id', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[100px]' + } + }, + { + accessorFn: (row) => row.IDWallet, + id: 'name', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + accessorFn: (row) => row.balance_minimum, + id: 'balance_minimum', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + accessorFn: (row) => row.balance_maximum, + id: 'balance_maximum', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + accessorFn: (row) => row.credit_limit, + id: 'credit_limit', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + accessorFn: (row) => row.max_transaction_per_day, + id: 'max_transaction_per_day', + header: ({ column }) => ( + + ), + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + accessorFn: (row) => row.monthly_limit, + id: 'monthly_limit', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + accessorFn: (row) => row.status, + id: 'status', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + id: 'actions', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + cell: (data) => { + const row = data.row.original; + return ( + <> + + + + ); + }, + meta: { + headerClassName: 'w-[100px]', + cellClassName: 'text-center' + } + } + ], + [] + ); + + const getWalletRuleLists = async (page: number, limit: number, sorting: any, filter: any) => { + try { + sorting = sorting.length == 0 ? [{ id: 'name', desc: false }] : sorting; + filter = filter.length == 0 ? {} : { any: filter[0].value?.toLowerCase() }; + const response = await GetData(`${API_URL_WALLET}/dashboard/wallet_rule`, { + limit, + page: page + 1, + with_deleted: false, + order_field: sorting[0].id, + order_direction: sorting[0].desc == false ? 'ASC' : 'DESC', + filter: JSON.stringify(filter) + }); + console.log(response?.data); + setWalletRules(response?.data.list); + return { data: response?.data.list, totalCount: response?.data.total_count }; + } catch (error) { + console.error('Error fetching Wallet Rule', error); + } + }; + + return ( + + + } + layout={{ card: true }} + sorting={[{ id: 'id', desc: false }]} + serverSide={true} + onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => + getWalletRuleLists(pageIndex, pageSize, sorting, columnFilters) + } + > + {children} + + + ); +}; + +export { ManageWalletRuleContext, ManageWalletRuleContextProvider }; +export type { WalletRuleProps }; diff --git a/src/pages/master/walletRule/hooks/useManageWalletRuleContext.tsx b/src/pages/master/walletRule/hooks/useManageWalletRuleContext.tsx new file mode 100644 index 0000000..5d1c3a7 --- /dev/null +++ b/src/pages/master/walletRule/hooks/useManageWalletRuleContext.tsx @@ -0,0 +1,16 @@ +import { useContext } from 'react'; +import { ManageWalletRuleContext } from './ManageWalletRuleContext'; + +const useManageWalletRuleContext = () => { + const context = useContext(ManageWalletRuleContext); + + if (!context) { + throw new Error( + 'useManageWalletRuleContext must be used within a ManageWalletRuleContextProvider' + ); + } + + return context; +}; + +export { useManageWalletRuleContext }; diff --git a/src/routing/AppRoutingSetup.tsx b/src/routing/AppRoutingSetup.tsx index faa26c7..ace6ab8 100644 --- a/src/routing/AppRoutingSetup.tsx +++ b/src/routing/AppRoutingSetup.tsx @@ -35,6 +35,7 @@ import ProductsMaster from '@/pages/master/products/ProductsMaster'; import ProviderMaster from '@/pages/master/provider/ProviderMaster'; import Transaction from '@/pages/transaction/transaction'; import ConversionMaster from '@/pages/master/conversion/ConversionMaster'; +import WalletRuleMaster from '@/pages/master/walletRule/WalletRuleMaster'; const AppRoutingSetup = (): ReactElement => { return ( @@ -56,6 +57,7 @@ const AppRoutingSetup = (): ReactElement => { element={} /> } /> + } /> } /> From b1e93f0909f6060c3a2c5585271fb0c4cb490687 Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Fri, 21 Mar 2025 15:21:14 +0700 Subject: [PATCH 12/14] fix define row to match with response --- .../provider/hooks/ManageProviderContext.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pages/master/provider/hooks/ManageProviderContext.tsx b/src/pages/master/provider/hooks/ManageProviderContext.tsx index 2beb2a2..ba92dda 100644 --- a/src/pages/master/provider/hooks/ManageProviderContext.tsx +++ b/src/pages/master/provider/hooks/ManageProviderContext.tsx @@ -74,7 +74,7 @@ const ManageProviderContextProvider = ({ children }: { children: React.ReactNode const columns = useMemo[]>( () => [ { - accessorFn: (row) => row.id, + accessorFn: (row) => row.provider_id, id: 'id', header: ({ column }) => , enableSorting: true, @@ -84,7 +84,7 @@ const ManageProviderContextProvider = ({ children }: { children: React.ReactNode } }, { - accessorFn: (row) => row.name, + accessorFn: (row) => row.provider_name, id: 'name', header: ({ column }) => , enableSorting: true, @@ -94,7 +94,7 @@ const ManageProviderContextProvider = ({ children }: { children: React.ReactNode } }, { - accessorFn: (row) => row.description, + accessorFn: (row) => row.provider_description, id: 'description', header: ({ column }) => , enableSorting: false, @@ -104,7 +104,7 @@ const ManageProviderContextProvider = ({ children }: { children: React.ReactNode } }, { - accessorFn: (row) => row.type, + accessorFn: (row) => row.provider_type, id: 'type', header: ({ column }) => , enableSorting: false, @@ -114,7 +114,7 @@ const ManageProviderContextProvider = ({ children }: { children: React.ReactNode } }, { - accessorFn: (row) => row.status, + accessorFn: (row) => row.provider_status, id: 'status', header: ({ column }) => , enableSorting: false, @@ -134,13 +134,13 @@ const ManageProviderContextProvider = ({ children }: { children: React.ReactNode <> @@ -168,7 +168,7 @@ const ManageProviderContextProvider = ({ children }: { children: React.ReactNode order_direction: sorting[0].desc == false ? 'ASC' : 'DESC', filter: JSON.stringify(filter) }); - // console.log(response?.data); + console.log(response?.data); setProvider(response?.data.list); return { data: response?.data.list, totalCount: response?.data.total_count }; } catch (error) { From be7b3fa458e8e4fec4b9809f795999231f02f14e Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Fri, 21 Mar 2025 15:22:44 +0700 Subject: [PATCH 13/14] fix define row to match with response --- src/pages/master/provider/blocks/EditDialog.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/master/provider/blocks/EditDialog.tsx b/src/pages/master/provider/blocks/EditDialog.tsx index e7c55d0..93b487e 100644 --- a/src/pages/master/provider/blocks/EditDialog.tsx +++ b/src/pages/master/provider/blocks/EditDialog.tsx @@ -56,7 +56,7 @@ const EditDialog = () => { type: '', status: '', transactionTypeId: '', - agentId: '', + agent: '', updated_by: '', updated_at: '' }; @@ -128,7 +128,7 @@ const EditDialog = () => { const doFetchData = useCallback(async (id: string) => { const response = await GetData(`${API_URL_MASTERDATA}/provider/getdata/${id}`, { id }); - + console.log(response); if (response?.status) { setFormField((prev) => ({ ...prev, @@ -137,7 +137,7 @@ const EditDialog = () => { type: response?.data.type, status: response?.data.status, transactionTypeId: response?.data.transactionTypeId, - agentId: response?.data.agentId + agent: response?.data.agent })); } }, []); @@ -151,7 +151,7 @@ const EditDialog = () => { formField.type.trim() === '' || formField.status.trim() === '' || formField.transactionTypeId.trim() === '' || - formField.agentId.trim() === '' + formField.agent.trim() === '' ) { setAlert({ show: true, message: 'Please fill in all required fields.' }); return; @@ -309,7 +309,7 @@ const EditDialog = () => { @@ -326,7 +326,7 @@ const EditDialog = () => { onSelect={() => { setFormField({ ...formField, - agentId: customer.msisdn + agent: customer.msisdn }); setOpen(false); }} From ab943cd99e694f01b1c6fe6ac8306167638eecf5 Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Fri, 21 Mar 2025 16:54:42 +0700 Subject: [PATCH 14/14] add get list conversion --- .../master/conversion/ConversionMaster.tsx | 32 ++++ .../master/conversion/blocks/AddDialog.tsx | 0 .../hooks/ManageConversionContext.tsx | 177 ++++++++++++++++++ .../hooks/useManageConversionContext.tsx | 12 ++ 4 files changed, 221 insertions(+) create mode 100644 src/pages/master/conversion/ConversionMaster.tsx create mode 100644 src/pages/master/conversion/blocks/AddDialog.tsx create mode 100644 src/pages/master/conversion/hooks/ManageConversionContext.tsx create mode 100644 src/pages/master/conversion/hooks/useManageConversionContext.tsx diff --git a/src/pages/master/conversion/ConversionMaster.tsx b/src/pages/master/conversion/ConversionMaster.tsx new file mode 100644 index 0000000..7e8725c --- /dev/null +++ b/src/pages/master/conversion/ConversionMaster.tsx @@ -0,0 +1,32 @@ +import { Container, DataGridInner } from '@/components'; +import { ManageConversionContextProvider } from './hooks/ManageConversionContext'; +import { Breadcrumbs, Link } from '@mui/material'; + +const ConversionMaster = () => { + return ( + + +

Conversion

+ + + Dashboard + + + + Master Data + + + + Manage Conversion + + + +
+ +
+
+
+ ); +}; + +export default ConversionMaster; diff --git a/src/pages/master/conversion/blocks/AddDialog.tsx b/src/pages/master/conversion/blocks/AddDialog.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/pages/master/conversion/hooks/ManageConversionContext.tsx b/src/pages/master/conversion/hooks/ManageConversionContext.tsx new file mode 100644 index 0000000..fe96cb0 --- /dev/null +++ b/src/pages/master/conversion/hooks/ManageConversionContext.tsx @@ -0,0 +1,177 @@ +import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components'; +import { Toaster } from '@/components/ui/sonner'; +import { apiConfig } from '@/config/api.config'; +import { useCallApi } from '@/hooks'; +import { ColumnDef } from '@tanstack/react-table'; +import { createContext, useCallback, useMemo, useState } from 'react'; +import ListToolbar from '../blocks/ListToolbar'; + +interface ConversionProps { + id: string; + name: string; +} + +interface ContextProps { + conversion: ConversionProps[]; + showAddDialog: boolean; + handleAddDialog: (show: boolean) => void; + showEditDialog: boolean; + handleEditDialog: (show: boolean, selected_conversion: string | null) => void; + showDeleteDialog: boolean; + handleDeleteDialog: (show: boolean, selected_conversion: string | null) => void; + selectedConversion: string | null; + getConversionLists: ( + limit: number, + page: number, + with_deleted: boolean, + order_field: any, + order_direction: any + ) => Promise<{ data: ConversionProps[]; totalCount: number } | undefined>; +} + +const initialProps: ContextProps = { + conversion: [], + showAddDialog: false, + handleAddDialog: () => {}, + showEditDialog: false, + handleEditDialog: () => {}, + showDeleteDialog: false, + handleDeleteDialog: () => {}, + selectedConversion: null, + getConversionLists: async () => undefined +}; + +const ManageConversionContext = createContext(initialProps); +const API_URL_WALLET = apiConfig.service_wallet; + +const ManageConversionContextProvider = ({ children }: { children: React.ReactNode }) => { + const [conversions, setConversions] = useState([]); + const [showAddDialog, setShowAddDialog] = useState(false); + const [showEditDialog, setShowEditDialog] = useState(false); + const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const [selectedConversion, setSelectedConversion] = useState(null); + const { GetData } = useCallApi(); + + const handleAddDialog = useCallback((show: boolean) => { + setShowAddDialog(show); + }, []); + + const handleEditDialog = useCallback((show: boolean, selected_conversion: string | null) => { + setShowEditDialog(show); + setSelectedConversion(show ? selected_conversion : null); + }, []); + + const handleDeleteDialog = useCallback((show: boolean, selected_conversion: string | null) => { + setShowEditDialog(show); + setSelectedConversion(show ? selected_conversion : null); + }, []); + + const columns = useMemo[]>( + () => [ + { + accessorFn: (row) => row.id, + id: 'id', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[100px]' + } + }, + { + accessorFn: (row) => row.name, + id: 'name', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' + } + }, + { + id: 'actions', + header: ({ column }) => , + enableSorting: false, + enableHiding: false, + cell: (data) => { + const row = data.row.original; + return ( + <> + + + + ); + }, + meta: { + headerClassName: 'w-[100px]', + cellClassName: 'text-center' + } + } + ], + [] + ); + + const getConversionLists = async (page: number, limit: number, sorting: any, filter: any) => { + try { + sorting = sorting.length == 0 ? [{ id: 'name', desc: false }] : sorting; + filter = filter.length == 0 ? {} : { any: filter[0].value?.toLowerCase() }; + const response = await GetData(`${API_URL_WALLET}/dashboard/conversion`, { + limit, + page: page + 1, + with_deleted: false, + order_field: sorting[0].id, + order_direction: sorting[0].desc == false ? 'ASC' : 'DESC', + filter: JSON.stringify(filter) + }); + console.log(response?.data); + setConversions(response?.data.list); + return { data: response?.data.list, totalCount: response?.data.total_count }; + } catch (error) { + console.error('Error fetching Conversion', error); + } + }; + + return ( + + + } + layout={{ card: true }} + sorting={[{ id: 'id', desc: false }]} + serverSide={true} + onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => + getConversionLists(pageIndex, pageSize, sorting, columnFilters) + } + > + {children} + + + ); +}; + +export { ManageConversionContext, ManageConversionContextProvider }; +export type { ConversionProps }; diff --git a/src/pages/master/conversion/hooks/useManageConversionContext.tsx b/src/pages/master/conversion/hooks/useManageConversionContext.tsx new file mode 100644 index 0000000..cf6c60b --- /dev/null +++ b/src/pages/master/conversion/hooks/useManageConversionContext.tsx @@ -0,0 +1,12 @@ +import { useContext } from 'react'; +import { ManageConversionContext } from './ManageConversionContext'; + +const useManageConversionContext = () => { + const context = useContext(ManageConversionContext); + + if (!context) throw new Error('useManageConversionContext must be used within AuthProvider'); + + return context; +}; + +export { useManageConversionContext };