From 65986ae3709560b201423cd9ec0de449606c3b55 Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Mon, 14 Apr 2025 10:47:19 +0700 Subject: [PATCH 1/8] update id column to sync with db on Manage Wallet --- src/pages/master/wallet/hooks/ManageWalletContext.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/master/wallet/hooks/ManageWalletContext.tsx b/src/pages/master/wallet/hooks/ManageWalletContext.tsx index 28a5a96..823d6eb 100644 --- a/src/pages/master/wallet/hooks/ManageWalletContext.tsx +++ b/src/pages/master/wallet/hooks/ManageWalletContext.tsx @@ -74,7 +74,7 @@ const ManageWalletContextProvider = ({ children }: { children: React.ReactNode } () => [ { accessorFn: (row) => row.name, - id: 'name', + id: 'wallets.name', header: ({ column }) => , enableSorting: true, enableHiding: false, @@ -86,7 +86,7 @@ const ManageWalletContextProvider = ({ children }: { children: React.ReactNode } accessorFn: (row) => row.description, id: 'description', header: ({ column }) => , - enableSorting: true, + enableSorting: false, enableHiding: false, meta: { headerClassName: 'w-[250px]' @@ -94,7 +94,7 @@ const ManageWalletContextProvider = ({ children }: { children: React.ReactNode } }, { accessorFn: (row) => row.status, - id: 'status', + id: 'wallets.status', header: ({ column }) => , enableSorting: true, enableHiding: false, From 20ea6677ebc146912b41a448d5a824b09694bff6 Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Mon, 14 Apr 2025 11:31:01 +0700 Subject: [PATCH 2/8] fix pagination Manage Menu - set params page to 1 --- .../menu/manage-menu/hooks/ManageMenusContext.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx b/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx index 3ff2f54..16f5b25 100644 --- a/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx +++ b/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx @@ -193,9 +193,8 @@ const ManageMenusContextProvider = ({ children }: { children: React.ReactNode }) if (parent.link === '/') { if (!parents.find((el: any) => el.id === parent.id)) - setParents((el: any) => [...el, { id: parent.id, name: parent.name }]); // GET PARENTS + setParents((el: any) => [...el, { id: parent.id, name: parent.name }]); - // Menambahkan parent ke dalam result meskipun tidak memiliki children result.push({ id: parent.id, module: parent.module, @@ -208,7 +207,6 @@ const ManageMenusContextProvider = ({ children }: { children: React.ReactNode }) }); } - // Jika parent tidak memiliki children, langsung return hasil yang sudah ada if (!parent.children || parent.children.length === 0) { return result; } @@ -230,7 +228,6 @@ const ManageMenusContextProvider = ({ children }: { children: React.ReactNode }) }; }); - // Gabungkan parent dengan children yang sudah diflatten return [...result, ...childrenFlattened]; }; @@ -241,7 +238,7 @@ const ManageMenusContextProvider = ({ children }: { children: React.ReactNode }) const response = await GetData(`${API_URL}/menus/list`, { limit, - page: page + 1, + page: 1, with_deleted: false, order_field: sorting[0].id, order_direction: sorting[0].desc ? 'DESC' : 'ASC', @@ -255,10 +252,11 @@ const ManageMenusContextProvider = ({ children }: { children: React.ReactNode }) ); const total_count = transformedData.length; - const paginatedData = transformedData.slice(page * limit, (page + 1) * limit); - console.log('data', paginatedData); - // setMenus(transformedData); + // **Pagination di frontend saja (tanpa hit API ulang)** + const paginatedData = transformedData.slice(page * limit, (page + 1) * limit); + const totalPages = Math.ceil(total_count / limit); + return { data: paginatedData, totalCount: total_count }; } catch (error) { console.error('Error fetching Menus', error); From 5a1a51bf5d6935e44e060cf00605425998a7e006 Mon Sep 17 00:00:00 2001 From: Raja Oktafrianto Date: Mon, 14 Apr 2025 11:57:29 +0700 Subject: [PATCH 3/8] fixing update customer manage user & sorting --- .../user/manage-user/blocks/EditDialog.tsx | 15 ++----- .../user/manage-user/blocks/ListToolBar.tsx | 2 +- .../manage-user/hooks/ManageUserContext.tsx | 42 ++++++++++++------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/pages/settings/user/manage-user/blocks/EditDialog.tsx b/src/pages/settings/user/manage-user/blocks/EditDialog.tsx index ecf99cd..a18ec29 100644 --- a/src/pages/settings/user/manage-user/blocks/EditDialog.tsx +++ b/src/pages/settings/user/manage-user/blocks/EditDialog.tsx @@ -153,9 +153,9 @@ const EditDialog = () => { email: response.data.email, id_role: response.data.idRole, status: response.data.status, - customerid: response.data.customerid + customerid: response.data.customer.id })); - // console.log('Customer ID dari user detail:', response.data.customerid); + // console.log('Customer ID from API:', response?.data.customerid); } else { setFormField((prev) => ({ ...prev, @@ -170,16 +170,6 @@ const EditDialog = () => { // console.log('Fetched ID Role:', response?.data.id_role); }, []); - useEffect(() => { - if (selectedUser) { - doFetchUserData(selectedUser); - } - }, [selectedUser]); - - useEffect(() => { - getCustomerList([{ id: 'id', desc: false }]); - }, []); - useEffect(() => { if (showEditDialog === false) { resetForm(); @@ -188,6 +178,7 @@ const EditDialog = () => { useEffect(() => { const fetchAllData = async () => { + await getCustomerList([{ id: 'id', desc: false }]); await doFetchUserRole([{ id: 'name', desc: false }]); if (selectedUser) { await doFetchUserData(selectedUser); diff --git a/src/pages/settings/user/manage-user/blocks/ListToolBar.tsx b/src/pages/settings/user/manage-user/blocks/ListToolBar.tsx index 70eb5be..2e76deb 100644 --- a/src/pages/settings/user/manage-user/blocks/ListToolBar.tsx +++ b/src/pages/settings/user/manage-user/blocks/ListToolBar.tsx @@ -15,7 +15,7 @@ const ListToolBar = () => { table.getColumn('username')?.setFilterValue(event.target.value) diff --git a/src/pages/settings/user/manage-user/hooks/ManageUserContext.tsx b/src/pages/settings/user/manage-user/hooks/ManageUserContext.tsx index 31c3c48..2e2b2dd 100644 --- a/src/pages/settings/user/manage-user/hooks/ManageUserContext.tsx +++ b/src/pages/settings/user/manage-user/hooks/ManageUserContext.tsx @@ -78,7 +78,7 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode }) const columns = useMemo[]>( () => [ { - accessorFn: (row) => row.username, + accessorKey: 'username', id: 'username', header: ({ column }) => , enableSorting: true, @@ -88,27 +88,27 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode }) accessorFn: (row) => row.customer?.username, id: 'customer', header: ({ column }) => , - enableSorting: false, + enableSorting: true, enableHiding: false, meta: { headerClassName: 'w-[300px]' } }, { - accessorFn: (row) => row.email, + accessorKey: 'email', id: 'email', header: ({ column }) => , - enableSorting: false, + enableSorting: true, enableHiding: false, meta: { headerClassName: 'w-[350px]' } }, { - accessorFn: (row) => row.name, + accessorKey: 'name', id: 'name', header: ({ column }) => , - enableSorting: false, + enableSorting: true, enableHiding: false, meta: { headerClassName: 'w-[250px]' @@ -118,7 +118,7 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode }) accessorFn: (row) => row.role.name, id: 'role_name', header: ({ column }) => , - enableSorting: false, + enableSorting: true, enableHiding: false, cell: (data: any) => { const { role } = data.row.original; @@ -132,7 +132,7 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode }) } }, { - accessorFn: (row) => row.status, + accessorKey: 'status', id: 'status', header: ({ column }) => , enableSorting: false, @@ -190,18 +190,31 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode }) ); const doGetListData = async (page: number, limit: number, sorting: any, filter: any) => { + const mappedFilter = filter.reduce((acc: any, curr: any) => { + let key = curr.id; + + // mapping kolom ke nama kolom lengkap (agar tidak ambigu di backend) + if (key === 'username') key = 'Users.username'; + if (key === 'customer') key = 'Customers.username'; + if (key === 'role_name') key = 'Roles.name'; + if (key === 'email') key = 'Users.email'; + if (key === 'name') key = 'Users.name'; + + acc[key] = curr.value.toLowerCase(); + return acc; + }, {}); + sorting = sorting.length == 0 ? [{ id: 'Users.username', desc: false }] : sorting; - filter = filter.length == 0 ? {} : { any: filter[0].value.toLowerCase() }; + filter = Object.keys(mappedFilter).length === 0 ? {} : mappedFilter; const response = await GetData(`${API_URL}/user/list`, { limit: limit, page: page + 1, with_deleted: false, order_field: sorting[0].id, - order_direction: sorting[0].desc == false ? 'ASC' : 'DESC', + order_direction: sorting[0].desc === false ? 'DESC' : 'ASC', filter: JSON.stringify(filter) }); // console.log('response api:', response); - return { data: response?.data.list, totalCount: response?.data.total_count }; }; @@ -228,9 +241,10 @@ const ManageUserContextProvider = ({ children }: { children: React.ReactNode }) layout={{ card: true }} sorting={[{ id: 'Users.username', desc: false }]} serverSide={true} - onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => - doGetListData(pageIndex, pageSize, sorting, columnFilters) - } + onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => { + // console.log('FILTER SENT:', columnFilters); + return doGetListData(pageIndex, pageSize, sorting, columnFilters); + }} > {children} From 3bbea4bff75830f3097ad6399357686fbc41d349 Mon Sep 17 00:00:00 2001 From: bagusajisaputroo Date: Mon, 14 Apr 2025 12:07:54 +0700 Subject: [PATCH 4/8] fixing transactionfee --- .../transferfee/blocks/EditDialog.tsx | 50 ++++---- .../hooks/ManageTransferFeeContext.tsx | 116 +++++++++++++++--- .../transfertype/blocks/EditDialog.tsx | 11 +- 3 files changed, 125 insertions(+), 52 deletions(-) diff --git a/src/pages/transfer/transferfee/blocks/EditDialog.tsx b/src/pages/transfer/transferfee/blocks/EditDialog.tsx index 895a1dd..13ccfe2 100644 --- a/src/pages/transfer/transferfee/blocks/EditDialog.tsx +++ b/src/pages/transfer/transferfee/blocks/EditDialog.tsx @@ -7,7 +7,8 @@ import { SelectTrigger, SelectValue } from '@/components/ui/select'; - +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command'; import { Dialog, DialogBody, @@ -59,6 +60,7 @@ const EditFeeDialog = () => { const [transactionTypes, setTransactionTypes] = useState([]); const parsedUser = getAuth()?.user; const [customers, setCustomers] = useState([]); + const [open, setOpen] = useState(false); const [alert, setAlert] = useState({ show: false, message: '' @@ -94,24 +96,22 @@ const EditFeeDialog = () => { const formattedTime = updated_time.toISOString().slice(0, 19).replace('T', ' '); if (showEditFeeDialog) { - setFormField({ - ...formField, + setFormField(prevState => ({ + ...prevState, updated_by: parsedUser?.username, updated_at: formattedTime - }); + })); } }, [showEditFeeDialog]); const resetForm = () => { if (selectedTransferFee) { - // Re-fetch the current data to reset the form to original values fetchTransactionFee(selectedTransferFee); } else { setFormField(initialState); } }; - /* actions */ const doUpdateTransferFee = useCallback( async (e: React.FormEvent) => { e.preventDefault(); @@ -257,15 +257,10 @@ const EditFeeDialog = () => { const fetchTransactionFee = useCallback(async (id: string) => { try { - const response = await GetData(`${API_URL}/transactionfees/getdata/${id}`, { id }); + const response = await GetData(`${API_URL}/transactionfees/getdata/${id}`, { }); if (response?.status) { - let creditTo = ''; - if (response.data.credit_destination?.id === '00000000-0000-0000-0000-000000000000') { - creditTo = 'D'; - } else { - creditTo = 'I'; - } + setFormField({ ...initialState, @@ -282,23 +277,21 @@ const EditFeeDialog = () => { priority: response.data.priority || '', status: response.data.status || '', status_include: response.data.status_include || '', - deduct_from: 'S', + deduct_from: response.data.deduct_from||'', deduct_from_account: response.data.deduct_from_account?.id || '', - credit_to: creditTo, + credit_to: response.data.credit_to || '', credit_destination: response.data.credit_destination?.id || '00000000-0000-0000-0000-000000000000', credit_destination_account: response.data.credit_destination_account?.id || '', updated_by: parsedUser?.username, updated_at: new Date().toISOString().slice(0, 19).replace('T', ' ') }); } - console.log(response) } catch (error) { console.error('Error fetching transaction fee details', error); setAlert({ show: true, message: 'Failed to fetch transaction fee details' }); } - }, [GetData, parsedUser?.username]); // only use username if that's all you need + }, [GetData, parsedUser?.username]); - // This flag ensures it only fetches once per open const hasFetchedRef = useRef(false); useEffect(() => { @@ -307,15 +300,23 @@ const EditFeeDialog = () => { hasFetchedRef.current = true; } - // Reset fetch flag if dialog is closed if (!showEditFeeDialog) { hasFetchedRef.current = false; } }, [selectedTransferFee, showEditFeeDialog, fetchTransactionFee]); + const handleCloseDialog = () => { + setFormField(initialState); + setAlert({ show: false, message: '' }); + handleEditFeeDialog(false, null); + }; return ( - handleEditFeeDialog(open, null)}> + { + if (!open) { + handleCloseDialog(); + } + }}> @@ -328,7 +329,7 @@ const EditFeeDialog = () => {
handleEditFeeDialog(false, null)} + onClick={handleCloseDialog} >
@@ -654,13 +655,6 @@ const EditFeeDialog = () => {
- diff --git a/src/pages/transfer/transferfee/hooks/ManageTransferFeeContext.tsx b/src/pages/transfer/transferfee/hooks/ManageTransferFeeContext.tsx index 6f6bee5..df14f82 100644 --- a/src/pages/transfer/transferfee/hooks/ManageTransferFeeContext.tsx +++ b/src/pages/transfer/transferfee/hooks/ManageTransferFeeContext.tsx @@ -181,7 +181,30 @@ const ManageTransferFeeContextProvider = ({ children }: { children: React.ReactN meta: { headerClassName: 'w-[250px]' } }, { - accessorFn: (row) => row.credit_destination?.fullname, + accessorFn: (row: { credit_to: string }) => { + const mapping: Record = { + D: 'Destination Member', + S: 'Source Member', + I: 'Input Customer' + }; + + return mapping[row.credit_to] || 'Unknown'; + }, + id: 'credit_to', + header: ({ column }) => ( + + ), + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[250px]' } + }, + { + accessorFn: (row) => { + if (row.credit_to === 'S' || row.credit_to === 'D' || !row.credit_destination) { + return 'N/A'; + } + return row.credit_destination?.fullname; + }, id: 'credit_destination', header: ({ column }) => , enableSorting: true, @@ -196,37 +219,102 @@ const ManageTransferFeeContextProvider = ({ children }: { children: React.ReactN enableHiding: false, meta: { headerClassName: 'w-[250px]' } }, + { + accessorFn: (row: { deduct_from: string }) => { + const mapping: Record = { + D: 'Destination Member', + S: 'Source Member', + }; + + return mapping[row.deduct_from]; + }, + id: 'deduct_from', + header: ({ column }) => ( + + ), + enableSorting: true, + enableHiding: false, + meta: { headerClassName: 'w-[250px]' } + }, { accessorFn: (row) => row.deduct_from_account?.description, id: 'deduct_from_account', - header: ({ column }) => , + header: ({ column }) => , enableSorting: true, enableHiding: false, meta: { headerClassName: 'w-[250px]' } }, + { + accessorFn: (row) => row.priority, + id: 'priority', + header: ({ column }) => , + enableSorting: true, + enableHiding: false, + cell: ({ row }) => { + const isActive = row.original.priority === 'Y'; + + return ( + + {isActive ? 'Yes' : 'No'} + + ); + }, + meta: { + headerClassName: 'w-[100px]', + cellClassName: 'text-center' + } + }, { - accessorFn: (row) => (row.priority === 'Y' ? 'Yes' : 'No'), - id: 'priority', - header: ({ column }) => , - enableSorting: true, - enableHiding: false, - meta: { headerClassName: 'w-[100px]' } - }, - { - accessorFn: (row) => (row.status === 'Y' ? 'Active' : 'Inactive'), + accessorFn: (row) => row.status, id: 'status', header: ({ column }) => , enableSorting: true, enableHiding: false, - meta: { headerClassName: 'w-[150px]' } + cell: ({ row }) => { + const isActive = row.original.status === 'Y'; + + return ( + + {isActive ? 'Active' : 'Inactive'} + + ); + }, + meta: { + headerClassName: 'w-[100px]', + cellClassName: 'text-center' + } }, { - accessorFn: (row) => (row.status_include === 'Y' ? 'Yes' : 'No'), + accessorFn: (row) => row.status_include, id: 'status_include', header: ({ column }) => , enableSorting: true, enableHiding: false, - meta: { headerClassName: 'w-[150px]' } + cell: ({ row }) => { + const isActive = row.original.status_include === 'Y'; + + return ( + + {isActive ? 'Yes' : 'No'} + + ); + }, + meta: { + headerClassName: 'w-[100px]', + cellClassName: 'text-center' + } }, { id: 'actions', diff --git a/src/pages/transfer/transfertype/blocks/EditDialog.tsx b/src/pages/transfer/transfertype/blocks/EditDialog.tsx index d646905..0039649 100644 --- a/src/pages/transfer/transfertype/blocks/EditDialog.tsx +++ b/src/pages/transfer/transfertype/blocks/EditDialog.tsx @@ -701,15 +701,6 @@ const EditDialog = () => {
- @@ -724,7 +715,7 @@ const EditDialog = () => {
- {/* */} + From 1a8849275993cc1cf8b4302dc84167fd46a577d0 Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Mon, 14 Apr 2025 14:00:33 +0700 Subject: [PATCH 5/8] update column on table Manage Menus - fix filter searching menus - synx with response filter searching --- .../manage-menu/hooks/ManageMenusContext.tsx | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx b/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx index 16f5b25..7ced52f 100644 --- a/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx +++ b/src/pages/menu/manage-menu/hooks/ManageMenusContext.tsx @@ -109,7 +109,7 @@ const ManageMenusContextProvider = ({ children }: { children: React.ReactNode }) meta: { headerClassName: 'w-[200px]' } }, { - accessorFn: (row) => row.parentName, + accessorFn: (row) => row.parentName || row.module, id: 'menu', header: ({ column }) => , enableSorting: false, @@ -234,30 +234,38 @@ const ManageMenusContextProvider = ({ children }: { children: React.ReactNode }) const getMenusLists = 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() }; + filter = filter.length === 0 ? {} : { name: { like: `%${filter[0].value?.toLowerCase()}%` } }; - const response = await GetData(`${API_URL}/menus/list`, { + const query: any = { limit, page: 1, with_deleted: false, order_field: sorting[0].id, - order_direction: sorting[0].desc ? 'DESC' : 'ASC', - filter: JSON.stringify(filter) - }); + order_direction: sorting[0].desc ? 'DESC' : 'ASC' + }; - if (!response?.data.list) return { data: [], totalCount: 0 }; + if (filter && Object.keys(filter).length > 0) { + query.filter = JSON.stringify(filter); + query.page = page + 1; + } - const transformedData = response.data.list.flatMap((row: any, parentIdx: number) => - flattenChildren(row, parentIdx) - ); + const response = await GetData(`${API_URL}/menus/list`, query); - const total_count = transformedData.length; + if (query.filter && query.filter.length > 0) { + return { data: response?.data.list, totalCount: response?.data.total_count }; + } else { + const transformedData = response?.data.list.flatMap((row: any, parentIdx: number) => + flattenChildren(row, parentIdx) + ); - // **Pagination di frontend saja (tanpa hit API ulang)** - const paginatedData = transformedData.slice(page * limit, (page + 1) * limit); - const totalPages = Math.ceil(total_count / limit); + const total_count = transformedData.length; - return { data: paginatedData, totalCount: total_count }; + // **Pagination di frontend saja (tanpa hit API ulang)** + const paginatedData = transformedData.slice(page * limit, (page + 1) * limit); + const totalPages = Math.ceil(total_count / limit); + + return { data: paginatedData, totalCount: total_count }; + } } catch (error) { console.error('Error fetching Menus', error); return { data: [], totalCount: 0 }; @@ -287,7 +295,7 @@ const ManageMenusContextProvider = ({ children }: { children: React.ReactNode }) pagination={{ size: 25 }} toolbar={} layout={{ card: true }} - sorting={[{ id: 'id', desc: false }]} + sorting={[{ id: 'created_at', desc: true }]} serverSide={true} onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => getMenusLists(pageIndex, pageSize, sorting, columnFilters) From e17e99a6de5a33cf26157045f3c6cc816416d1b2 Mon Sep 17 00:00:00 2001 From: Narul Hidayah Date: Mon, 14 Apr 2025 14:52:32 +0700 Subject: [PATCH 6/8] detail transaction disbursement --- .../blocks/DetailTransaction.tsx | 49 ++++--- .../blocks/DetailTransactionLog.tsx | 131 ++++++++++++++++++ .../hooks/TransactionContext.tsx | 24 +++- .../hooks/TransactionDialogContext.tsx | 42 ++++++ 4 files changed, 229 insertions(+), 17 deletions(-) create mode 100644 src/pages/disbursement/history-transaction/blocks/DetailTransactionLog.tsx create mode 100644 src/pages/disbursement/history-transaction/hooks/TransactionDialogContext.tsx diff --git a/src/pages/disbursement/history-transaction/blocks/DetailTransaction.tsx b/src/pages/disbursement/history-transaction/blocks/DetailTransaction.tsx index 28df134..dfa33ec 100644 --- a/src/pages/disbursement/history-transaction/blocks/DetailTransaction.tsx +++ b/src/pages/disbursement/history-transaction/blocks/DetailTransaction.tsx @@ -1,7 +1,10 @@ +// disbursement/history-transaction/blocks/DetailTransaction.tsx +import { KeenIcon } from '@/components'; import { useTransactionContext } from '../hooks/useTransactionContext'; import { useCallApi } from '@/hooks'; import { apiConfig } from '@/config/api.config'; import { useEffect, useState } from 'react'; +import moment from 'moment'; import { Dialog, DialogBody, @@ -10,10 +13,11 @@ import { DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import TransactionLogViewer from './DetailTransactionLog'; const API_URL = apiConfig.service_disbursement; -type StatusCode = 'W' | 'P' | 'F' | 'D' | 'Y'; +type StatusCode = 'W' | 'O' | 'F' | 'D'; interface StatusInfo { label: string; @@ -22,11 +26,10 @@ interface StatusInfo { } const statusMap: Record = { - W: { label: 'Waiting', bg: 'bg-yellow-100', text: 'text-yellow-600' }, - P: { label: 'Pending', bg: 'bg-blue-100', text: 'text-blue-600' }, + W: { label: 'Waiting Schedule', bg: 'bg-yellow-100', text: 'text-yellow-600' }, + O: { label: 'On Process', bg: 'bg-blue-100', text: 'text-blue-600' }, F: { label: 'Fail', bg: 'bg-red-100', text: 'text-red-600' }, D: { label: 'Done', bg: 'bg-green-100', text: 'text-green-600' }, - Y: { label: 'Active', bg: 'bg-green-100', text: 'text-green-600' }, }; export const renderStatusBadge = (statusRaw: string | null | undefined) => { @@ -49,7 +52,9 @@ const DetailTransaction = () => { const { showDetailDialog, setShowDetailDialog, - selectedTransactionId + selectedTransactionId, + setShowDetailLogDialog, + setDetailLogData } = useTransactionContext(); const [transactionDetails, setTransactionDetails] = useState(null); @@ -92,27 +97,39 @@ const DetailTransaction = () => { Username - Name - Transfer Amount - Description - Invoice Number - Process Date - Response + Fullname + Amount Status + Process Date + Invoice Number + Actions + {transactionDetails?.log && transactionDetails?.log.length > 0 ? ( - transactionDetails.log.map((log: { customer: any, amount: number, remark: string, reference: string, response_date: string, payment_response: string, status: string}, index: number) => ( + transactionDetails.log.map((log: { id: number, customer: any, amount: number, remark: string, reference: string, request_date: string, payment_response: string, status: string}, index: number) => ( {log.customer.username ?? '-'} {log.customer.fullname ?? '-'} {log.amount ?? '-'} - {log.remark ?? '-'} - {log.reference ?? '-'} - {log.response_date ?? '-'} - {log.payment_response ?? '-'} {renderStatusBadge(log.status) ?? '-'} + {log.request_date && moment(log.request_date).isValid() ? moment(log.request_date).format('YYYY-MM-DD HH:mm:ss') : '-'} + {log.reference ?? '-'} + +
+ +
+ + )) ) : ( diff --git a/src/pages/disbursement/history-transaction/blocks/DetailTransactionLog.tsx b/src/pages/disbursement/history-transaction/blocks/DetailTransactionLog.tsx new file mode 100644 index 0000000..7b26b05 --- /dev/null +++ b/src/pages/disbursement/history-transaction/blocks/DetailTransactionLog.tsx @@ -0,0 +1,131 @@ +import React, { useState } from 'react'; +import moment from 'moment'; +import { useTransactionContext } from '../hooks/useTransactionContext'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogBody } from '@/components/ui/dialog'; + +interface LogType { + id: string; + amount: number; + remark: string; + reference: string; + response_date: string; + status: string; + customer?: { + username: string; + fullname: string; + }; + payment_response?: string; +} + +type StatusCode = 'W' | 'O' | 'F' | 'D'; + +interface StatusInfo { + label: string; + bg: string; + text: string; +} + +const statusMap: Record = { + W: { label: 'Waiting Schedule', bg: 'bg-yellow-100', text: 'text-yellow-600' }, + O: { label: 'On Process', bg: 'bg-blue-100', text: 'text-blue-600' }, + F: { label: 'Fail', bg: 'bg-red-100', text: 'text-red-600' }, + D: { label: 'Done', bg: 'bg-green-100', text: 'text-green-600' }, +}; + +export const renderStatusBadge = (statusRaw: string | null | undefined) => { + const status = statusRaw as StatusCode; + const { label, bg, text } = statusMap[status] ?? { + label: 'Unknown', + bg: 'bg-gray-100', + text: 'text-gray-600', + }; + + return ( + + {label} + + ); + }; + +const TransactionLogViewer = () => { + const { + showDetailLogDialog, + setShowDetailLogDialog, + detailLogData + } = useTransactionContext(); + + console.log('detailLogData: ', detailLogData) + + + + return ( + + + + Detail Record + + + {/* Tab Content */} + {detailLogData && detailLogData != null ? ( +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID{detailLogData.customer.id}
Username{detailLogData.customer.username}
Name{detailLogData.customer.fullname}
Amount{detailLogData.amount}
Remark{detailLogData.remark}
Payment Request
{detailLogData.payment_request}
Payment Response
{detailLogData.payment_response}
Status{renderStatusBadge(detailLogData.status)}
Prosess Date{detailLogData.request_date && moment(detailLogData.request_date).isValid() ? moment(detailLogData.request_date).format('YYYY-MM-DD HH:mm:ss') : '-'}
Response Date{detailLogData.response_date && moment(detailLogData.response_date).isValid() ? moment(detailLogData.response_date).format('YYYY-MM-DD HH:mm:ss') : '-'}
Reference Number{detailLogData.reference}
+
+
+
+ ) : (
)} +
+
+
+ ); +}; + +export default TransactionLogViewer; diff --git a/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx b/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx index 19e2382..b1807a1 100644 --- a/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx +++ b/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx @@ -10,6 +10,7 @@ import { Button } from '@/components/ui/button'; import { useNavigate } from 'react-router'; import moment from 'moment'; import DetailTransaction from '../blocks/DetailTransaction'; +import TransactionLogViewer from '../blocks/DetailTransactionLog'; interface TransactionProps { id: number; @@ -31,6 +32,11 @@ interface ContextProps { setSelectedTransactionId: React.Dispatch>; showUploadBatchDialog: boolean; handleUploadBatchDialog: (show: boolean) => void; + showDetailLogDialog: boolean; + // handleDetailLogDialog: (show: boolean) => void; + setShowDetailLogDialog: React.Dispatch>; + setDetailLogData: React.Dispatch>; + detailLogData: any | null } const initialProps: ContextProps = { @@ -41,6 +47,11 @@ const initialProps: ContextProps = { setSelectedTransactionId: () => { }, showUploadBatchDialog: false, handleUploadBatchDialog: (show: boolean) => {}, + showDetailLogDialog: false, + // handleDetailLogDialog: (show: boolean) => {}, + setShowDetailLogDialog: () => { }, + setDetailLogData: () => {}, + detailLogData: null, }; type StatusCode = 'W' | 'P' | 'F' | 'D' | 'Y'; @@ -67,6 +78,8 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => { const [showDetailDialog, setShowDetailDialog] = useState(false); const [selectedTransactionId, setSelectedTransactionId] = useState(null); const [showUploadBatchDialog, setShowUploadBatchDialog] = useState(false); + const [showDetailLogDialog, setShowDetailLogDialog] = useState(false); + const [detailLogData, setDetailLogData] = useState(null); const [transaction, setTransaction] = useState([]); const { GetData } = useCallApi(); const navigate = useNavigate(); @@ -74,6 +87,9 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => { const handleUploadBatchDialog = useCallback((show: boolean) => { setShowUploadBatchDialog(show); }, []); + // const handleDetailLogDialog = useCallback((show: boolean) => { + // setShowDetailLogDialog(show); + // }, []); const columns = useMemo[]>( () => [ @@ -262,11 +278,17 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => { selectedTransactionId, setSelectedTransactionId, handleUploadBatchDialog, - showUploadBatchDialog + showUploadBatchDialog, + // handleDetailLogDialog, + showDetailLogDialog, + setShowDetailLogDialog, + setDetailLogData, + detailLogData }} > + void; + setSelectedLog: (log: LogDetail | null) => void; +} + +const TransactionDialogContext = createContext(undefined); + +export const TransactionDialogProvider = ({ children }: { children: React.ReactNode }) => { + const [showLogDialog, setShowLogDialog] = useState(false); + const [selectedLog, setSelectedLog] = useState(null); + + return ( + + {children} + + ); +}; + +export const useTransactionDialog = () => { + const context = useContext(TransactionDialogContext); + if (!context) throw new Error("useTransactionDialog must be used within TransactionDialogProvider"); + return context; +}; From 9762402be9e256df6f306396e1b7a1b4d7eeb5e1 Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Mon, 14 Apr 2025 15:04:29 +0700 Subject: [PATCH 7/8] update ListToolbar on Manage Wallet --- src/pages/master/wallet/blocks/ListToolbar.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/master/wallet/blocks/ListToolbar.tsx b/src/pages/master/wallet/blocks/ListToolbar.tsx index 11b5767..1b35ce4 100644 --- a/src/pages/master/wallet/blocks/ListToolbar.tsx +++ b/src/pages/master/wallet/blocks/ListToolbar.tsx @@ -16,8 +16,10 @@ const ListToolbar = () => { table.getColumn('name')?.setFilterValue(event.target.value)} + value={(table.getColumn('wallets.name')?.getFilterValue() as string) ?? ''} + onChange={(event) => + table.getColumn('wallets.name')?.setFilterValue(event.target.value) + } /> {/* From a15aa14eb329b105d196bebdf6ea267e8ee7dbf3 Mon Sep 17 00:00:00 2001 From: Wikzyy Date: Mon, 14 Apr 2025 15:08:06 +0700 Subject: [PATCH 8/8] fix table and list toolbar on Manage Menu - add button search for not debounce searching - set if condition parent to `id_parent` === null --- .../menu/manage-menu/blocks/ListToolbar.tsx | 22 +++++++++++++++++-- .../manage-menu/hooks/ManageMenusContext.tsx | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/pages/menu/manage-menu/blocks/ListToolbar.tsx b/src/pages/menu/manage-menu/blocks/ListToolbar.tsx index 9ed6b64..8d1915c 100644 --- a/src/pages/menu/manage-menu/blocks/ListToolbar.tsx +++ b/src/pages/menu/manage-menu/blocks/ListToolbar.tsx @@ -1,10 +1,22 @@ import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components'; import { useManageMenusContext } from '../hooks/useManageMenusContext'; import { Button } from '@/components/ui/button'; +import React, { useState } from 'react'; const ListToolbar = () => { const { table, reload } = useDataGrid(); const { handleAddDialog } = useManageMenusContext(); + const [searchValue, setSearchValue] = useState(''); + + const handleKeyDown = (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + handleSearch(); + } + }; + + const handleSearch = () => { + table.getColumn('name')?.setFilterValue(searchValue); + }; return (
@@ -16,10 +28,16 @@ const ListToolbar = () => { table.getColumn('name')?.setFilterValue(event.target.value)} + value={searchValue} + onChange={(event) => setSearchValue(event.target.value)} + onKeyDown={handleKeyDown} /> + + + {/*