fixing transaction type + transaction fee

This commit is contained in:
bagusajisaputroo
2025-04-14 16:47:59 +07:00
parent ebc1cf8108
commit 1f962e4451
7 changed files with 642 additions and 556 deletions

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
import { useManageTransferFeeContext } from '../hooks/useManageTransferFeeContext'; import { useManageTransferFeeContext } from '../hooks/useManageTransferFeeContext';
import { NumericFormat } from 'react-number-format'; import { NumericFormat } from 'react-number-format';
import { import {
@ -40,11 +40,7 @@ import {
TableHeader, TableHeader,
TableRow TableRow
} from '@/components/ui/table'; } from '@/components/ui/table';
import { ManageTransferFeeContext } from '../hooks/ManageTransferFeeContext';
import { ColumnDef } from '@tanstack/react-table';
import { ManageTransferFeeContextProvider } from '../../transferfee/hooks/ManageTransferFeeContext';
import { apiConfig } from '@/config/api.config'; import { apiConfig } from '@/config/api.config';
import { get } from 'http';
interface TransactionTypeProps { interface TransactionTypeProps {
id: string; id: string;
@ -62,12 +58,19 @@ interface CustomerProps {
const API_URL = apiConfig.service_transaction; const API_URL = apiConfig.service_transaction;
const API_URL_MASTER_DATA = apiConfig.service_master_data; const API_URL_MASTER_DATA = apiConfig.service_master_data;
const API_URL_CUSTOMER = apiConfig.service_customer; const API_URL_CUSTOMER = apiConfig.service_customer;
const AddFeeDialog = () => { const AddFeeDialog = () => {
const parentRef = useRef<any | null>(null); const parentRef = useRef<any | null>(null);
const { reload } = useDataGrid(); const { reload } = useDataGrid();
const { PostData, PutData, GetData } = useCallApi(); const { PostData, PutData, GetData } = useCallApi();
const { showAddFeeDialog, handleAddFeeDialog, handleEditFeeDialog, selectedTransferFee } = const {
useManageTransferFeeContext(); showAddFeeDialog,
handleAddFeeDialog,
handleEditFeeDialog,
selectedTransferFee,
transactionTypeId
} = useManageTransferFeeContext();
const [alert, setAlert] = useState({ const [alert, setAlert] = useState({
show: false, show: false,
message: '' message: ''
@ -75,8 +78,16 @@ const AddFeeDialog = () => {
const [transactionTypes, setTransactionTypes] = useState<TransactionTypeProps[]>([]); const [transactionTypes, setTransactionTypes] = useState<TransactionTypeProps[]>([]);
const [wallets, setWallets] = useState<WalletProps[]>([]); const [wallets, setWallets] = useState<WalletProps[]>([]);
const [customers, setCustomers] = useState<CustomerProps[]>([]); const [customers, setCustomers] = useState<CustomerProps[]>([]);
const [defaultCustomer, setDefaultCustomer] = useState(''); const [defaultCustomer, setDefaultCustomer] = useState('');
const [transactionTypeName, setTransactionTypeName] = useState('');
const [isLoadingTransactionType, setIsLoadingTransactionType] = useState(false);
const [isLoadingWallets, setIsLoadingWallets] = useState(false);
const [isLoadingCustomers, setIsLoadingCustomers] = useState(false);
const customersWithNames = customers.map((customer) => ({
id: customer.id,
name: customer.username,
}));
const initialState = { const initialState = {
name: '', name: '',
description: '', description: '',
@ -104,54 +115,57 @@ const AddFeeDialog = () => {
const resetForm = () => { const resetForm = () => {
setFormField(initialState); setFormField(initialState);
setTransactionTypeName('');
}; };
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [showTransactionFeeDialog, setShowTransactionFeeDialog] = useState(false); const [showTransactionFeeDialog, setShowTransactionFeeDialog] = useState(false);
const parsedUser = getAuth()?.user; const parsedUser = getAuth()?.user;
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { useEffect(() => {
e.preventDefault(); if (showAddFeeDialog && transactionTypeId) {
// setIsSubmitting(true); setIsLoadingTransactionType(true);
const payload = {
name: formField.name, setFormField(prev => ({
description: formField.description, ...prev,
period_start: formField.period_start, transaction_type: transactionTypeId
period_end: formField.period_end, }));
minimum_amount: formField.minimum_amount,
maximum_amount: formField.maximum_amount, const getTransactionTypeDetails = async () => {
deduct_amount: formField.deduct_amount, try {
deduct_percentage: formField.deduct_percentage, const response = await GetData(`${API_URL}/transactiontype/getdata/${transactionTypeId}`, {});
fee_amount: formField.fee_amount, if (response?.status && response?.data) {
transaction_type: formField.transaction_type, setTransactionTypeName(response.data.name);
status: formField.status, }
status_include: formField.status_include, } catch (error) {
priority: formField.priority, console.error('Error fetching transaction type details', error);
deduct_from: formField.deduct_from, } finally {
deduct_from_account: formField.deduct_from_account, setIsLoadingTransactionType(false);
credit_to: formField.credit_to, }
credit_destination: formField.credit_destination, };
credit_destination_account: formField.credit_destination_account
}; getTransactionTypeDetails();
}; }
}, [showAddFeeDialog, transactionTypeId, GetData]);
useEffect(() => { useEffect(() => {
const created_time = new Date(); const created_time = new Date();
const formattedTime = created_time.toISOString().slice(0, 19).replace('T', ' '); const formattedTime = created_time.toISOString().slice(0, 19).replace('T', ' ');
if (showAddFeeDialog) { if (showAddFeeDialog) {
setFormField({ setFormField(prev => ({
...formField, ...prev,
created_by: parsedUser?.username, created_by: parsedUser?.username,
created_at: formattedTime created_at: formattedTime
}); }));
} }
}, [showAddFeeDialog]); }, [showAddFeeDialog, parsedUser?.username]);
useEffect(() => { useEffect(() => {
if (!showAddFeeDialog) return; if (!showAddFeeDialog) return;
const getTransactionTypeList = async (sorting: any) => { const getTransactionTypeList = async (sorting: any) => {
setIsLoadingTransactionType(true);
try { try {
sorting = sorting.length === 0 ? [{ id: 'name', desc: false }] : sorting; sorting = sorting.length === 0 ? [{ id: 'name', desc: false }] : sorting;
const response = await GetData(`${API_URL}/transactiontype/list`, { const response = await GetData(`${API_URL}/transactiontype/list`, {
@ -164,13 +178,16 @@ const AddFeeDialog = () => {
setTransactionTypes(response?.data.list || []); setTransactionTypes(response?.data.list || []);
} catch (error) { } catch (error) {
console.error('Error fetching transaction types', error); console.error('Error fetching transaction types', error);
} finally {
setIsLoadingTransactionType(false);
} }
}; };
getTransactionTypeList([{ id: 'id', desc: false }]); getTransactionTypeList([{ id: 'id', desc: false }]);
}, [showAddFeeDialog]); }, [showAddFeeDialog, GetData]);
const fetchWallets = useCallback(async () => { const fetchWallets = useCallback(async () => {
setIsLoadingWallets(true);
const params = { const params = {
limit: 100, limit: 100,
page: 1, page: 1,
@ -188,6 +205,8 @@ const AddFeeDialog = () => {
} catch (error) { } catch (error) {
console.error('Error fetching wallets', error); console.error('Error fetching wallets', error);
setWallets([]); setWallets([]);
} finally {
setIsLoadingWallets(false);
} }
}, [GetData]); }, [GetData]);
@ -199,6 +218,7 @@ const AddFeeDialog = () => {
useEffect(() => { useEffect(() => {
if (!showAddFeeDialog) return; if (!showAddFeeDialog) return;
const getCustomerList = async (sorting: any) => { const getCustomerList = async (sorting: any) => {
setIsLoadingCustomers(true);
try { try {
sorting = sorting.length === 0 ? [{ id: 'name', desc: false }] : sorting; sorting = sorting.length === 0 ? [{ id: 'name', desc: false }] : sorting;
@ -218,6 +238,8 @@ const AddFeeDialog = () => {
} catch (error) { } catch (error) {
console.error('Error fetching customers', error); console.error('Error fetching customers', error);
setCustomers([]); setCustomers([]);
} finally {
setIsLoadingCustomers(false);
} }
}; };
@ -254,7 +276,7 @@ const AddFeeDialog = () => {
return; return;
} }
} }
if (formField.credit_to === 'I' && !formField.credit_destination) { if (formField.credit_to === 'I' && !formField.credit_destination) {
setAlert({ setAlert({
show: true, show: true,
@ -280,6 +302,13 @@ const AddFeeDialog = () => {
reload(); reload();
resetForm(); resetForm();
handleAddFeeDialog(false); handleAddFeeDialog(false);
const createActivity = {
module: 'Manage Transfer Fee',
description: `Create Transfer Fee => ${formField.name}`,
action: 'C'
};
doSaveLogActivity(createActivity);
} else { } else {
setAlert({ show: true, message: response?.message || 'Failed to create transfer fee' }); setAlert({ show: true, message: response?.message || 'Failed to create transfer fee' });
} }
@ -293,6 +322,40 @@ const AddFeeDialog = () => {
[formField, PostData, reload, handleAddFeeDialog] [formField, PostData, reload, handleAddFeeDialog]
); );
const renderSelectWithLoading = (
value: string,
onChangeHandler: (value: string) => void,
options: {id: string, name: string}[] | null,
placeholder: string,
isLoading: boolean
) => {
return (
<Select
value={value}
onValueChange={onChangeHandler}
disabled={isLoading}
>
<SelectTrigger>
{isLoading ? (
<div className="flex items-center">
<div className="animate-pulse bg-gray-200 h-4 w-24 rounded"></div>
<span className="ml-2">Loading...</span>
</div>
) : (
<SelectValue placeholder={placeholder} />
)}
</SelectTrigger>
<SelectContent>
{options && options.map((option) => (
<SelectItem value={option.id} key={option.id}>
{option.name}
</SelectItem>
))}
</SelectContent>
</Select>
);
};
return ( return (
<Dialog open={showAddFeeDialog} onOpenChange={(open) => handleAddFeeDialog(open)}> <Dialog open={showAddFeeDialog} onOpenChange={(open) => handleAddFeeDialog(open)}>
<DialogContent className="container-fixed max-w-[1080px] flex flex-col p-5 overflow-hidden [&>button]:hidden"> <DialogContent className="container-fixed max-w-[1080px] flex flex-col p-5 overflow-hidden [&>button]:hidden">
@ -325,6 +388,35 @@ const AddFeeDialog = () => {
<form action="" onSubmit={doCreateTransferType}> <form action="" onSubmit={doCreateTransferType}>
<div className="card flex flex-col gap-5"> <div className="card flex flex-col gap-5">
<div className="card-body grid gap-5 p-0"> <div className="card-body grid gap-5 p-0">
<div className="w-full">
<label className="form-label">Transaction Type ID <span className="text-red-500">*</span></label>
{transactionTypeId ? (
<div className="relative">
<Input
className="input bg-gray-100"
type="text"
value={isLoadingTransactionType ? '' : transactionTypeName}
readOnly
/>
{isLoadingTransactionType && (
<div className="absolute inset-0 flex items-center justify-start bg-gray-100 px-3">
<div className="flex items-center">
<div className="animate-pulse bg-gray-200 h-4 w-24 rounded"></div>
<span className="ml-2 text-gray-500">Loading transaction type...</span>
</div>
</div>
)}
</div>
) : (
renderSelectWithLoading(
formField.transaction_type,
(transaction_type) => setFormField((prev) => ({ ...prev, transaction_type })),
transactionTypes,
"Select Transaction Type",
isLoadingTransactionType
)
)}
</div>
<div className="w-full"> <div className="w-full">
<label className="form-label">Transfer Free Name <span className="text-red-500">*</span></label> <label className="form-label">Transfer Free Name <span className="text-red-500">*</span></label>
<Input <Input
@ -475,21 +567,13 @@ const AddFeeDialog = () => {
</div> </div>
<div className="w-full"> <div className="w-full">
<label className="form-label">Deduct From Destination <span className="text-red-500">*</span></label> <label className="form-label">Deduct From Destination <span className="text-red-500">*</span></label>
<Select {renderSelectWithLoading(
value={formField.deduct_from_account} formField.deduct_from_account,
onValueChange={(value) => setFormField({ ...formField, deduct_from_account: value })} (value) => setFormField({ ...formField, deduct_from_account: value }),
> wallets,
<SelectTrigger> "Select Wallet",
<SelectValue placeholder="Select Wallet" /> isLoadingWallets
</SelectTrigger> )}
<SelectContent>
{wallets.map((wallet) => (
<SelectItem value={wallet.id} key={wallet.id}>
{wallet.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div> </div>
<div className="w-full"> <div className="w-full">
<label className="form-label">Credit To <span className="text-red-500">*</span></label> <label className="form-label">Credit To <span className="text-red-500">*</span></label>
@ -514,60 +598,24 @@ const AddFeeDialog = () => {
{formField.credit_to === 'I' && ( {formField.credit_to === 'I' && (
<div className="w-full"> <div className="w-full">
<label className="form-label">Credit Destination <span className="text-red-500">*</span></label> <label className="form-label">Credit Destination <span className="text-red-500">*</span></label>
<Select {renderSelectWithLoading(
value={formField.credit_destination} formField.credit_destination,
onValueChange={(value) => setFormField({ ...formField, credit_destination: value })} (value) => setFormField({ ...formField, credit_destination: value }),
> customersWithNames,
<SelectTrigger> "Select Customer",
<SelectValue placeholder="Select Customer" /> isLoadingCustomers
</SelectTrigger> )}
<SelectContent>
{customers.map((customer) => (
<SelectItem value={customer.id} key={customer.id}>
{customer.username} - {customer.msisdn}
</SelectItem>
))}
</SelectContent>
</Select>
</div> </div>
)} )}
<div className="w-full"> <div className="w-full">
<label className="form-label">Credit Destination Account <span className="text-red-500">*</span></label> <label className="form-label">Credit Destination Account <span className="text-red-500">*</span></label>
<Select {renderSelectWithLoading(
value={formField.credit_destination_account} formField.credit_destination_account,
onValueChange={(value) => setFormField({ ...formField, credit_destination_account: value })} (value) => setFormField({ ...formField, credit_destination_account: value }),
> wallets,
<SelectTrigger> "Select Wallet",
<SelectValue placeholder="Select Wallet" /> isLoadingWallets
</SelectTrigger> )}
<SelectContent>
{wallets.map((wallet) => (
<SelectItem value={wallet.id} key={wallet.id}>
{wallet.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="w-full">
<label className="form-label">Transaction Type ID <span className="text-red-500">*</span></label>
<Select
value={formField.transaction_type}
onValueChange={(transaction_type) =>
setFormField((prev) => ({ ...prev, transaction_type }))
}
>
<SelectTrigger>
<SelectValue placeholder="Select Transaction Type" />
</SelectTrigger>
<SelectContent>
{transactionTypes.map((transactiontype) => (
<SelectItem value={transactiontype.id} key={transactiontype.id}>
{transactiontype.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div> </div>
<div className="w-full"> <div className="w-full">
<label className="form-label">Status <span className="text-red-500">*</span></label> <label className="form-label">Status <span className="text-red-500">*</span></label>
@ -624,7 +672,11 @@ const AddFeeDialog = () => {
> >
Reset Reset
</Button> </Button>
<Button variant={'default'} type="submit" disabled={isSubmitting}> <Button
variant={'default'}
type="submit"
disabled={isSubmitting || isLoadingTransactionType || isLoadingWallets || isLoadingCustomers}
>
{isSubmitting ? 'Saving...' : 'Save Changes'} {isSubmitting ? 'Saving...' : 'Save Changes'}
</Button> </Button>
</div> </div>

View File

@ -167,6 +167,11 @@ const EditFeeDialog = () => {
toast.success('Successfully updated transfer fee'); toast.success('Successfully updated transfer fee');
reload(); reload();
handleEditFeeDialog(false, null); handleEditFeeDialog(false, null);
const createActivity = {
module: 'Manage Transfer Type',
description: `Edit Transfer Type => ${selectedTransferFee}`,
action: 'U'
};
} else { } else {
setAlert({ show: true, message: response?.message || 'Failed to update transfer fee' }); setAlert({ show: true, message: response?.message || 'Failed to update transfer fee' });
} }

View File

@ -1,388 +1,391 @@
import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components'; import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components';
import { Toaster } from '@/components/ui/sonner'; import { Toaster } from '@/components/ui/sonner';
import { apiConfig } from '@/config/api.config'; import { apiConfig } from '@/config/api.config';
import { useCallApi } from '@/hooks'; import { useCallApi } from '@/hooks';
import { ColumnDef } from '@tanstack/react-table'; import { ColumnDef } from '@tanstack/react-table';
import { createContext, useCallback, useEffect, useMemo, useState } from 'react'; import { createContext, useCallback, useEffect, useMemo, useState } from 'react';
import ListToolbar from '../blocks/ListToolBar'; import ListToolbar from '../blocks/ListToolBar';
import DeleteDialog from '../blocks/DeleteDialog'; import DeleteDialog from '../blocks/DeleteDialog';
import { EditFeeDialog } from '../blocks/EditDialog'; import { EditFeeDialog } from '../blocks/EditDialog';
import { useManageTransferTypeContext } from '../../transfertype/hooks/useManageTransferTypeContext'; import { useManageTransferTypeContext } from '../../transfertype/hooks/useManageTransferTypeContext';
interface ContextProps { interface ContextProps {
showEditFeeDialog: boolean; showEditFeeDialog: boolean;
handleEditFeeDialog: (show: boolean, selected_TransferFee: string | null) => void; handleEditFeeDialog: (show: boolean, selected_TransferFee: string | null) => void;
showAddFeeDialog: boolean; showAddFeeDialog: boolean;
handleAddFeeDialog: (show: boolean) => void; handleAddFeeDialog: (show: boolean) => void;
handleDeleteFeeDialog: (show: boolean, selected_TransferFee: string | null) => void; handleDeleteFeeDialog: (show: boolean, selected_TransferFee: string | null) => void;
showDeleteFeeDialog: boolean; showDeleteFeeDialog: boolean;
selectedTransferFee: string | null; selectedTransferFee: string | null;
} transactionTypeId: string | null;
const initialProps: ContextProps = { }
showEditFeeDialog: false,
handleEditFeeDialog: () => {},
showAddFeeDialog: false,
handleAddFeeDialog: () => {},
showDeleteFeeDialog: false,
handleDeleteFeeDialog: () => {},
selectedTransferFee: null
};
interface TransferFeeProps { const initialProps: ContextProps = {
name: string; showEditFeeDialog: false,
description: string; handleEditFeeDialog: () => {},
minimum_amount: number; showAddFeeDialog: false,
maximum_amount:number; handleAddFeeDialog: () => {},
period_start:string; showDeleteFeeDialog: false,
period_end:string; handleDeleteFeeDialog: () => {},
deduct_amount: number; selectedTransferFee: null,
deduct_percentage: number; transactionTypeId: null,
transaction_type: string;
status: string;
status_include: string;
fee: number;
}
const ManageTransferFeeContext = createContext<ContextProps>(initialProps);
const API_URL = apiConfig.service_transaction;
const ManageTransferFeeContextProvider = ({ children }: { children: React.ReactNode }) => {
const [showEditFeeDialog, setShowEditFeeDialog] = useState(false);
const [showAddFeeDialog, setShowAddFeeDialog] = useState(false);
const [showDeleteFeeDialog, setShowDeleteFeeDialog] = useState(false);
const { showAddDialog, handleAddDialog, selectedTransferType } = useManageTransferTypeContext();
const [selectedTransferFee, setSelectedTransferFee] = useState<string | null>(null);
const { GetData } = useCallApi();
const handleEditFeeDialog = useCallback((show: boolean, selected_TransferFee: string | null) => {
setSelectedTransferFee(show ? selected_TransferFee : null);
setShowEditFeeDialog(show);
}, []);
const handleAddFeeDialog = useCallback((show: boolean) => {
setShowAddFeeDialog(show);
}, []);
const handleDeleteFeeDialog = useCallback((show: boolean, selected_TransferFee: string | null) => {
setSelectedTransferFee(show ? selected_TransferFee : null);
setShowDeleteFeeDialog(show);
}, []);
const doGetTransferFeeListData = async (
page: number,
limit: number,
sorting: any,
filter: any
) => {
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<ColumnDef<any>[]>(
() => [
{
accessorFn: (row) => row.name,
id: 'name',
header: ({ column }) => <DataGridColumnHeader title="Name" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[200px]' }
},
{
accessorFn: (row) => row.description,
id: 'description',
header: ({ column }) => <DataGridColumnHeader title="Description" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[250px]' }
},
{
accessorFn: (row) => row.minimum_amount,
id: 'minimum_amount',
header: ({ column }) => <DataGridColumnHeader title="Min Amount" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[150px]' }
},
{
accessorFn: (row) => row.maximum_amount,
id: 'maximum_amount',
header: ({ column }) => <DataGridColumnHeader title="Max Amount" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[150px]' }
},
{
accessorFn: (row) => row.fee_amount,
id: 'fee_amount',
header: ({ column }) => <DataGridColumnHeader title="Fee Amount" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[150px]' }
},
{
accessorFn: (row) => row.deduct_amount,
id: 'deduct_amount',
header: ({ column }) => <DataGridColumnHeader title="Deduct Amount" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[150px]' }
},
{
accessorFn: (row) => row.deduct_percentage,
id: 'deduct_percentage',
header: ({ column }) => <DataGridColumnHeader title="Deduct %" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[150px]' }
},
{
accessorFn: (row) => row.period_start?.split('T')[0],
id: 'period_start',
header: ({ column }) => <DataGridColumnHeader title="Period Start" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[200px]' }
},
{
accessorFn: (row) => row.period_end?.split('T')[0],
id: 'period_end',
header: ({ column }) => <DataGridColumnHeader title="Period End" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[200px]' }
},
{
accessorFn: (row) => row.transaction_type?.name,
id: 'transactionTypeId',
header: ({ column }) => <DataGridColumnHeader title="Transaction Type" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[250px]' }
},
{
accessorFn: (row: { credit_to: string }) => {
const mapping: Record<string, string> = {
D: 'Destination Member',
S: 'Source Member',
I: 'Input Customer'
};
return mapping[row.credit_to] || 'Unknown';
},
id: 'credit_to',
header: ({ column }) => (
<DataGridColumnHeader title="Credit To" column={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 }) => <DataGridColumnHeader title="Credit Destination" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[250px]' }
},
{
accessorFn: (row) => row.credit_destination_account?.description,
id: 'credit_destination_account',
header: ({ column }) => <DataGridColumnHeader title="Credit Destination Account" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[250px]' }
},
{
accessorFn: (row: { deduct_from: string }) => {
const mapping: Record<string, string> = {
D: 'Destination Member',
S: 'Source Member',
};
return mapping[row.deduct_from];
},
id: 'deduct_from',
header: ({ column }) => (
<DataGridColumnHeader title="Deduct From" column={column} />
),
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[250px]' }
},
{
accessorFn: (row) => row.deduct_from_account?.description,
id: 'deduct_from_account',
header: ({ column }) => <DataGridColumnHeader title="Deduct From Account" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[250px]' }
},
{
accessorFn: (row) => row.priority,
id: 'priority',
header: ({ column }) => <DataGridColumnHeader title="Priority" column={column} />,
enableSorting: true,
enableHiding: false,
cell: ({ row }) => {
const isActive = row.original.priority === 'Y';
return (
<span
className={`px-2 py-1 text-xs font-semibold rounded-full ${
isActive ? 'bg-green-100 text-green-600' : 'bg-red-100 text-red-600'
}`}
>
{isActive ? 'Yes' : 'No'}
</span>
);
},
meta: {
headerClassName: 'w-[100px]',
cellClassName: 'text-center'
}
},
{
accessorFn: (row) => row.status,
id: 'status',
header: ({ column }) => <DataGridColumnHeader title="Status" column={column} />,
enableSorting: true,
enableHiding: false,
cell: ({ row }) => {
const isActive = row.original.status === 'Y';
return ( interface TransferFeeProps {
<span name: string;
className={`px-2 py-1 text-xs font-semibold rounded-full ${ description: string;
isActive ? 'bg-green-100 text-green-600' : 'bg-red-100 text-red-600' minimum_amount: number;
}`} maximum_amount:number;
> period_start:string;
{isActive ? 'Active' : 'Inactive'} period_end:string;
</span> deduct_amount: number;
); deduct_percentage: number;
}, transaction_type: string;
meta: { status: string;
headerClassName: 'w-[100px]', status_include: string;
cellClassName: 'text-center' fee: number;
} }
},
{
accessorFn: (row) => row.status_include,
id: 'status_include',
header: ({ column }) => <DataGridColumnHeader title="Status Include" column={column} />,
enableSorting: true,
enableHiding: false,
cell: ({ row }) => {
const isActive = row.original.status_include === 'Y';
return ( const ManageTransferFeeContext = createContext<ContextProps>(initialProps);
<span const API_URL = apiConfig.service_transaction;
className={`px-2 py-1 text-xs font-semibold rounded-full ${
isActive ? 'bg-green-100 text-green-600' : 'bg-red-100 text-red-600' const ManageTransferFeeContextProvider = ({ children, transactionTypeId = null }: { children: React.ReactNode; transactionTypeId?: string | null }) => {
}`} const [showEditFeeDialog, setShowEditFeeDialog] = useState(false);
> const [showAddFeeDialog, setShowAddFeeDialog] = useState(false);
{isActive ? 'Yes' : 'No'} const [showDeleteFeeDialog, setShowDeleteFeeDialog] = useState(false);
</span> const { showAddDialog, handleAddDialog, selectedTransferType } = useManageTransferTypeContext();
); const [selectedTransferFee, setSelectedTransferFee] = useState<string | null>(null);
}, const { GetData } = useCallApi();
meta: { const handleEditFeeDialog = useCallback((show: boolean, selected_TransferFee: string | null) => {
headerClassName: 'w-[100px]', setSelectedTransferFee(show ? selected_TransferFee : null);
cellClassName: 'text-center' setShowEditFeeDialog(show);
} }, []);
},
{ const handleAddFeeDialog = useCallback((show: boolean) => {
id: 'actions', setShowAddFeeDialog(show);
header: ({ column }) => <DataGridColumnHeader title="Actions" column={column} />, }, []);
enableSorting: false,
enableHiding: false, const handleDeleteFeeDialog = useCallback((show: boolean, selected_TransferFee: string | null) => {
cell: (data) => { setSelectedTransferFee(show ? selected_TransferFee : null);
const row = data.row.original; setShowDeleteFeeDialog(show);
return ( }, []);
<> const doGetTransferFeeListData = async (
<button page: number,
className="btn btn-sm btn-icon btn-clear btn-light" limit: number,
onClick={() => handleEditFeeDialog(true, row.id)} sorting: any,
> filter: any
<KeenIcon icon="notepad-edit" /> ) => {
</button> if (!selectedTransferType) {
<button return { data: [], totalCount: 0 };
className="btn btn-sm btn-icon btn-clear btn-light"
onClick={() => handleDeleteFeeDialog(true, row.id)}
>
<KeenIcon icon="trash" />
</button>
</>
);
},
meta: {
headerClassName: 'w-[100px]',
cellClassName: 'text-center'
}
} }
],
[handleEditFeeDialog, handleDeleteFeeDialog] sorting = sorting.length === 0 ? [{ id: 'id', desc: false }] : sorting;
); filter = filter.length === 0 ? {} : { any: filter[0].value.toLowerCase() };
return ( try {
<div className="container mx-auto py-5"> const response = await GetData(`${API_URL}/transactionfees/getdatabytransactiontype/${selectedTransferType}`, {});
<div className="flex justify-between items-center mt-6">
<h1 className="text-xl font-semibold text-gray-900">Manage Transaction Fee</h1> // console.log('API Response:', response?.data);
</div>
<ManageTransferFeeContext.Provider return {
value={{ data: response?.data ,
showEditFeeDialog, totalCount: 1
handleEditFeeDialog, };
showAddFeeDialog, } catch (error) {
handleAddFeeDialog, console.error("Error fetching transaction fees by transaction type:", error);
selectedTransferFee, return { data: [], totalCount: 0 };
showDeleteFeeDialog, }
handleDeleteFeeDialog };
}}
> const columns = useMemo<ColumnDef<any>[]>(
<Toaster expand visibleToasts={9} duration={3000} /> () => [
<DataGridProvider {
columns={columns} accessorFn: (row) => row.name,
pagination={{ size: 10 }} id: 'name',
layout={{ card: true }} header: ({ column }) => <DataGridColumnHeader title="Name" column={column} />,
toolbar={<ListToolbar />} enableSorting: true,
sorting={[{ id: 'id', desc: true }]} enableHiding: false,
serverSide={true} meta: { headerClassName: 'w-[200px]' }
onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => },
doGetTransferFeeListData(pageIndex, pageSize, sorting, columnFilters) {
accessorFn: (row) => row.description,
id: 'description',
header: ({ column }) => <DataGridColumnHeader title="Description" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[250px]' }
},
{
accessorFn: (row) => row.minimum_amount,
id: 'minimum_amount',
header: ({ column }) => <DataGridColumnHeader title="Min Amount" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[150px]' }
},
{
accessorFn: (row) => row.maximum_amount,
id: 'maximum_amount',
header: ({ column }) => <DataGridColumnHeader title="Max Amount" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[150px]' }
},
{
accessorFn: (row) => row.fee_amount,
id: 'fee_amount',
header: ({ column }) => <DataGridColumnHeader title="Fee Amount" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[150px]' }
},
{
accessorFn: (row) => row.deduct_amount,
id: 'deduct_amount',
header: ({ column }) => <DataGridColumnHeader title="Deduct Amount" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[150px]' }
},
{
accessorFn: (row) => row.deduct_percentage,
id: 'deduct_percentage',
header: ({ column }) => <DataGridColumnHeader title="Deduct %" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[150px]' }
},
{
accessorFn: (row) => row.period_start?.split('T')[0],
id: 'period_start',
header: ({ column }) => <DataGridColumnHeader title="Period Start" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[200px]' }
},
{
accessorFn: (row) => row.period_end?.split('T')[0],
id: 'period_end',
header: ({ column }) => <DataGridColumnHeader title="Period End" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[200px]' }
},
{
accessorFn: (row) => row.transaction_type?.name,
id: 'transactionTypeId',
header: ({ column }) => <DataGridColumnHeader title="Transaction Type" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[250px]' }
},
{
accessorFn: (row: { credit_to: string }) => {
const mapping: Record<string, string> = {
D: 'Destination Member',
S: 'Source Member',
I: 'Input Customer'
};
return mapping[row.credit_to] || 'Unknown';
},
id: 'credit_to',
header: ({ column }) => (
<DataGridColumnHeader title="Credit To" column={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 }) => <DataGridColumnHeader title="Credit Destination" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[250px]' }
},
{
accessorFn: (row) => row.credit_destination_account?.description,
id: 'credit_destination_account',
header: ({ column }) => <DataGridColumnHeader title="Credit Destination Account" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[250px]' }
},
{
accessorFn: (row: { deduct_from: string }) => {
const mapping: Record<string, string> = {
D: 'Destination Member',
S: 'Source Member',
};
return mapping[row.deduct_from];
},
id: 'deduct_from',
header: ({ column }) => (
<DataGridColumnHeader title="Deduct From" column={column} />
),
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[250px]' }
},
{
accessorFn: (row) => row.deduct_from_account?.description,
id: 'deduct_from_account',
header: ({ column }) => <DataGridColumnHeader title="Deduct From Account" column={column} />,
enableSorting: true,
enableHiding: false,
meta: { headerClassName: 'w-[250px]' }
},
{
accessorFn: (row) => row.priority,
id: 'priority',
header: ({ column }) => <DataGridColumnHeader title="Priority" column={column} />,
enableSorting: true,
enableHiding: false,
cell: ({ row }) => {
const isActive = row.original.priority === 'Y';
return (
<span
className={`px-2 py-1 text-xs font-semibold rounded-full ${
isActive ? 'bg-green-100 text-green-600' : 'bg-red-100 text-red-600'
}`}
>
{isActive ? 'Yes' : 'No'}
</span>
);
},
meta: {
headerClassName: 'w-[100px]',
cellClassName: 'text-center'
} }
},
{
accessorFn: (row) => row.status,
id: 'status',
header: ({ column }) => <DataGridColumnHeader title="Status" column={column} />,
enableSorting: true,
enableHiding: false,
cell: ({ row }) => {
const isActive = row.original.status === 'Y';
return (
<span
className={`px-2 py-1 text-xs font-semibold rounded-full ${
isActive ? 'bg-green-100 text-green-600' : 'bg-red-100 text-red-600'
}`}
>
{isActive ? 'Active' : 'Inactive'}
</span>
);
},
meta: {
headerClassName: 'w-[100px]',
cellClassName: 'text-center'
}
},
{
accessorFn: (row) => row.status_include,
id: 'status_include',
header: ({ column }) => <DataGridColumnHeader title="Status Include" column={column} />,
enableSorting: true,
enableHiding: false,
cell: ({ row }) => {
const isActive = row.original.status_include === 'Y';
return (
<span
className={`px-2 py-1 text-xs font-semibold rounded-full ${
isActive ? 'bg-green-100 text-green-600' : 'bg-red-100 text-red-600'
}`}
>
{isActive ? 'Yes' : 'No'}
</span>
);
},
meta: {
headerClassName: 'w-[100px]',
cellClassName: 'text-center'
}
},
{
id: 'actions',
header: ({ column }) => <DataGridColumnHeader title="Actions" column={column} />,
enableSorting: false,
enableHiding: false,
cell: (data) => {
const row = data.row.original;
return (
<>
<button
className="btn btn-sm btn-icon btn-clear btn-light"
onClick={() => handleEditFeeDialog(true, row.id)}
>
<KeenIcon icon="notepad-edit" />
</button>
<button
className="btn btn-sm btn-icon btn-clear btn-light"
onClick={() => handleDeleteFeeDialog(true, row.id)}
>
<KeenIcon icon="trash" />
</button>
</>
);
},
meta: {
headerClassName: 'w-[100px]',
cellClassName: 'text-center'
}
}
],
[handleEditFeeDialog, handleDeleteFeeDialog]
);
return (
<div className="container mx-auto py-5">
<div className="flex justify-between items-center mt-6">
<h1 className="text-xl font-semibold text-gray-900">Manage Transaction Fee</h1>
</div>
<ManageTransferFeeContext.Provider
value={{
showEditFeeDialog,
handleEditFeeDialog,
showAddFeeDialog,
handleAddFeeDialog,
selectedTransferFee,
showDeleteFeeDialog,
handleDeleteFeeDialog,
transactionTypeId
}}
> >
{children} <Toaster expand visibleToasts={9} duration={3000} />
<DataGridProvider
columns={columns}
pagination={{ size: 10 }}
layout={{ card: true }}
toolbar={<ListToolbar />}
sorting={[{ id: 'id', desc: true }]}
serverSide={true}
onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) =>
doGetTransferFeeListData(pageIndex, pageSize, sorting, columnFilters)
}
>
{children}
</DataGridProvider> </DataGridProvider>
</ManageTransferFeeContext.Provider> </ManageTransferFeeContext.Provider>
</div> </div>
); );
}; };
export { ManageTransferFeeContext, ManageTransferFeeContextProvider }; export { ManageTransferFeeContext, ManageTransferFeeContextProvider };

View File

@ -39,6 +39,7 @@ import {
CommandItem, CommandItem,
CommandList CommandList
} from '@/components/ui/command'; } from '@/components/ui/command';
import { doSaveLogActivity } from '@/actions/GlobalActions';
interface WalletProps { interface WalletProps {
id: string; id: string;
@ -95,7 +96,6 @@ const AddDialog = () => {
const parsedUser = getAuth()?.user; const parsedUser = getAuth()?.user;
// Updated validation function to make only certain fields required
const validateForm = () => { const validateForm = () => {
const requiredFields = [ const requiredFields = [
'name', 'name',
@ -141,6 +141,13 @@ const AddDialog = () => {
toast.success('Success Create Transfer Type'); toast.success('Success Create Transfer Type');
reload(); reload();
resetForm(); resetForm();
const createActivity = {
module: 'Manage Transfer Type',
description: `Create Transfer Type => ${formField.name}`,
action: 'C'
};
doSaveLogActivity(createActivity);
} }
}) })
.finally(() => { .finally(() => {
@ -218,7 +225,7 @@ const AddDialog = () => {
order_direction: 'ASC', order_direction: 'ASC',
}; };
const response = await GetData(`${API_URL_MASTERDATA}/wallet/list`, params); const response = await GetData(`${API_URL_MASTERDATA}/wallet/list`, params);
console.log(response) // console.log(response)
if (response?.status && response?.data) { if (response?.status && response?.data) {
setWallets(response.data.list); setWallets(response.data.list);
} else { } else {
@ -230,7 +237,7 @@ const AddDialog = () => {
if (!showAddDialog) return; if (!showAddDialog) return;
fetchWallets(); fetchWallets();
}, [showAddDialog]); }, [showAddDialog]);
console.log(formField) // console.log(formField)
return ( return (
<Dialog open={showAddDialog} onOpenChange={(open) => handleAddDialog(open)}> <Dialog open={showAddDialog} onOpenChange={(open) => handleAddDialog(open)}>
<DialogContent className="container-fixed max-w-[1080px] flex flex-col p-5 overflow-hidden [&>button]:hidden"> <DialogContent className="container-fixed max-w-[1080px] flex flex-col p-5 overflow-hidden [&>button]:hidden">
@ -436,7 +443,7 @@ const AddDialog = () => {
<div className="w-full"> <div className="w-full">
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> <div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label className="form-label flex items-center gap-1 max-w-56"> <label className="form-label flex items-center gap-1 max-w-56">
TransactionType Status Status Transaction Type
<span className="text-red-500"> *</span> <span className="text-red-500"> *</span>
</label> </label>
@ -451,14 +458,17 @@ const AddDialog = () => {
<SelectValue placeholder="Select" /> <SelectValue placeholder="Select" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="D">Disbursement </SelectItem> <SelectItem value="D">Disbursement </SelectItem>
<SelectItem value="O">Other </SelectItem> <SelectItem value="O">Other </SelectItem>
<SelectItem value="CA">Change Customer to Agent </SelectItem> <SelectItem value="CA">Change Group Emoney Customer to Agent </SelectItem>
<SelectItem value="AC">Change Agent to Customer </SelectItem> <SelectItem value="AC">Change Group Emoney Agent to Customer </SelectItem>
<SelectItem value="PA">Change Group Point Agent to Customer </SelectItem>
<SelectItem value="PC">Change Group Point Customer to Agent</SelectItem>
<SelectItem value="CE">Return Customer Emoney </SelectItem> <SelectItem value="CE">Return Customer Emoney </SelectItem>
<SelectItem value="AD">Return Agent Deposit </SelectItem> <SelectItem value="AD">Return Agent Deposit </SelectItem>
<SelectItem value="AM">Return Agent Merchant </SelectItem> <SelectItem value="AM">Return Agent Merchant </SelectItem>
<SelectItem value="AE">Return Agent Emoney </SelectItem> <SelectItem value="AE">Return Agent Emoney </SelectItem>
<SelectItem value="R">Reward Point </SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@ -7,6 +7,7 @@ import { toast } from 'sonner';
import { useCallApi } from '@/hooks'; import { useCallApi } from '@/hooks';
import { useManageTransferTypeContext } from '../hooks/useManageTransferTypeContext'; import { useManageTransferTypeContext } from '../hooks/useManageTransferTypeContext';
import { DialogDescription } from '@radix-ui/react-dialog'; import { DialogDescription } from '@radix-ui/react-dialog';
import { doSaveLogActivity } from '@/actions/GlobalActions';
const API_URL = apiConfig.service_transaction; const API_URL = apiConfig.service_transaction;
@ -33,6 +34,12 @@ const DeleteDialog = () => {
setAlert({ show: false, message: '' }); setAlert({ show: false, message: '' });
handleDeleteDialog(false, null); handleDeleteDialog(false, null);
reload(); reload();
const createActivity = {
module: 'Manage Transfer Type',
description: `Delete Transfer Type => ${selectedTransferType}`,
action: 'D'
};
doSaveLogActivity(createActivity);
// setTimeout(() => toast.success('Success Delete Transaction Type'), 0); // setTimeout(() => toast.success('Success Delete Transaction Type'), 0);
} else { } else {
setAlert({ show: true, message: response?.message }); setAlert({ show: true, message: response?.message });

View File

@ -54,8 +54,7 @@ interface PermissionObject {
const EditDialog = () => { const EditDialog = () => {
const parentRef = useRef<any | null>(null); const parentRef = useRef<any | null>(null);
const { showEditDialog, handleEditDialog, selectedTransferType } = const { showEditDialog, handleEditDialog, selectedTransferType } = useManageTransferTypeContext();
useManageTransferTypeContext();
const { reload } = useDataGrid(); const { reload } = useDataGrid();
const [wallets, setWallets] = useState<WalletProps[]>([]); const [wallets, setWallets] = useState<WalletProps[]>([]);
const [groups, setGroups] = useState<PermissionObject[]>([]); const [groups, setGroups] = useState<PermissionObject[]>([]);
@ -68,7 +67,7 @@ const EditDialog = () => {
show: false, show: false,
message: '' message: ''
}); });
const initialState: { const initialState: {
name: string; name: string;
description: string; description: string;
@ -98,9 +97,9 @@ const EditDialog = () => {
updated_by: '', updated_by: '',
updated_at: '' updated_at: ''
}; };
const [formField, setFormField] = useState(initialState); const [formField, setFormField] = useState(initialState);
const resetForm = () => { const resetForm = () => {
setFormField(initialState); setFormField(initialState);
setSelectedGroups([]); setSelectedGroups([]);
@ -112,13 +111,11 @@ const EditDialog = () => {
const isSelected = prevState.permission.includes(groupId); const isSelected = prevState.permission.includes(groupId);
if (isSelected) { if (isSelected) {
// Remove the permission if already selected
return { return {
...prevState, ...prevState,
permission: prevState.permission.filter((id) => id !== groupId) permission: prevState.permission.filter((id) => id !== groupId)
}; };
} else { } else {
// Add the permission if not selected
return { return {
...prevState, ...prevState,
permission: [...prevState.permission, groupId] permission: [...prevState.permission, groupId]
@ -138,13 +135,13 @@ const EditDialog = () => {
'type' 'type'
]; ];
const missingFields = requiredFields.filter( const missingFields = requiredFields.filter((field) => {
(field) => { return (
return formField[field as keyof typeof formField] === '' || formField[field as keyof typeof formField] === '' ||
formField[field as keyof typeof formField] === null || formField[field as keyof typeof formField] === null ||
formField[field as keyof typeof formField] === undefined; formField[field as keyof typeof formField] === undefined
} );
); });
if (missingFields.length > 0) { if (missingFields.length > 0) {
setAlert({ setAlert({
@ -154,7 +151,6 @@ const EditDialog = () => {
return false; return false;
} }
// Validate that at least one permission is selected
if (formField.permission.length === 0) { if (formField.permission.length === 0) {
setAlert({ setAlert({
show: true, show: true,
@ -179,12 +175,12 @@ const EditDialog = () => {
})); }));
} }
}, [showEditDialog, parsedUser]); }, [showEditDialog, parsedUser]);
const selectedPermissionNames = groups const selectedPermissionNames = groups
.filter((g) => formField.permission.includes(g.id)) .filter((g) => formField.permission.includes(g.id))
.map((g) => g.name) .map((g) => g.name)
.join(', '); .join(', ');
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); e.preventDefault();
setIsSubmitting(true); setIsSubmitting(true);
@ -200,7 +196,7 @@ const EditDialog = () => {
handleEditDialog(false, null); handleEditDialog(false, null);
toast.success('Success Update Transfer Type'); toast.success('Success Update Transfer Type');
reload(); reload();
const createActivity = { const createActivity = {
module: 'Manage Transfer Type', module: 'Manage Transfer Type',
description: `Edit Transfer Type => ${selectedTransferType}`, description: `Edit Transfer Type => ${selectedTransferType}`,
@ -214,17 +210,19 @@ const EditDialog = () => {
setIsSubmitting(false); setIsSubmitting(false);
}); });
}; };
const doUpdateTransferType = useCallback(async () => { const doUpdateTransferType = useCallback(async () => {
try { try {
// Create a clean copy of the form data with properly formatted permissions
const formDataToSend = { const formDataToSend = {
...formField, ...formField,
permission: formField.permission.filter(id => typeof id === 'string') permission: formField.permission.filter((id) => typeof id === 'string')
}; };
const response = await PutData(`${API_URL}/transactiontype/update/${selectedTransferType}`, formDataToSend); const response = await PutData(
`${API_URL}/transactiontype/update/${selectedTransferType}`,
formDataToSend
);
if (response?.status) { if (response?.status) {
setAlert({ show: false, message: '' }); setAlert({ show: false, message: '' });
return true; return true;
@ -236,18 +234,15 @@ const EditDialog = () => {
setAlert({ setAlert({
show: true, show: true,
message: message:
error instanceof Error error instanceof Error ? error.message : 'An error occurred while updating transfer type'
? error.message
: 'An error occurred while updating transfer type'
}); });
return false; return false;
} }
}, [formField, selectedTransferType, PutData]); }, [formField, selectedTransferType, PutData]);
// Fetch customers
useEffect(() => { useEffect(() => {
if (!showEditDialog) return; if (!showEditDialog) return;
const getCustomerList = async () => { const getCustomerList = async () => {
try { try {
const sorting = [{ id: 'id', desc: false }]; const sorting = [{ id: 'id', desc: false }];
@ -258,7 +253,7 @@ const EditDialog = () => {
order_field: sorting[0].id, order_field: sorting[0].id,
order_direction: sorting[0].desc ? 'DESC' : 'ASC' order_direction: sorting[0].desc ? 'DESC' : 'ASC'
}); });
if (response?.status && response?.data) { if (response?.status && response?.data) {
setCustomers(response.data.list); setCustomers(response.data.list);
} }
@ -270,10 +265,9 @@ const EditDialog = () => {
getCustomerList(); getCustomerList();
}, [showEditDialog, GetData]); }, [showEditDialog, GetData]);
// Fetch groups
useEffect(() => { useEffect(() => {
if (!showEditDialog) return; if (!showEditDialog) return;
const getGroupList = async () => { const getGroupList = async () => {
try { try {
const response = await GetData(`${API_URL_MASTERDATA}/groups/list`, { const response = await GetData(`${API_URL_MASTERDATA}/groups/list`, {
@ -283,7 +277,7 @@ const EditDialog = () => {
order_field: 'name', order_field: 'name',
order_direction: 'ASC' order_direction: 'ASC'
}); });
if (response?.status && response?.data) { if (response?.status && response?.data) {
setGroups(response.data.list); setGroups(response.data.list);
} }
@ -295,10 +289,9 @@ const EditDialog = () => {
getGroupList(); getGroupList();
}, [showEditDialog, GetData]); }, [showEditDialog, GetData]);
// Fetch wallets
useEffect(() => { useEffect(() => {
if (!showEditDialog) return; if (!showEditDialog) return;
const getWalletList = async () => { const getWalletList = async () => {
try { try {
const response = await GetData(`${API_URL_MASTERDATA}/wallet/list`, { const response = await GetData(`${API_URL_MASTERDATA}/wallet/list`, {
@ -306,9 +299,9 @@ const EditDialog = () => {
page: 1, page: 1,
with_deleted: false, with_deleted: false,
order_field: 'Wallets.name', order_field: 'Wallets.name',
order_direction: 'ASC', order_direction: 'ASC'
}); });
if (response?.status && response?.data) { if (response?.status && response?.data) {
setWallets(response.data.list); setWallets(response.data.list);
} }
@ -320,28 +313,28 @@ const EditDialog = () => {
getWalletList(); getWalletList();
}, [showEditDialog, GetData]); }, [showEditDialog, GetData]);
// Fetch transaction type data
useEffect(() => { useEffect(() => {
if (!showEditDialog || !selectedTransferType) return; if (!showEditDialog || !selectedTransferType) return;
const fetchTransactionType = async () => { const fetchTransactionType = async () => {
try { try {
const response = await GetData(`${API_URL}/transactiontype/getdata/${selectedTransferType}`, {}); const response = await GetData(
console.log(response); `${API_URL}/transactiontype/getdata/${selectedTransferType}`,
{}
);
// console.log(response);
if (response?.status) { if (response?.status) {
// Process permissions to ensure they're always string IDs
let permissionIds: string[] = []; let permissionIds: string[] = [];
if (Array.isArray(response.data.permission)) { if (Array.isArray(response.data.permission)) {
permissionIds = response.data.permission.map((perm: any) => { permissionIds = response.data.permission.map((perm: any) => {
// Check if permission is an object or string
if (typeof perm === 'object' && perm !== null) { if (typeof perm === 'object' && perm !== null) {
return perm.id; return perm.id;
} }
return perm; return perm;
}); });
} }
setFormField((prev) => ({ setFormField((prev) => ({
...prev, ...prev,
name: response.data.name, name: response.data.name,
@ -354,7 +347,7 @@ const EditDialog = () => {
status_approval: response.data.status_approval, status_approval: response.data.status_approval,
type: response.data.type || '', type: response.data.type || '',
status: response.data.status, status: response.data.status,
permission: permissionIds permission: permissionIds
})); }));
} }
} catch (error) { } catch (error) {
@ -365,11 +358,11 @@ const EditDialog = () => {
}); });
} }
}; };
const timer = setTimeout(() => { const timer = setTimeout(() => {
fetchTransactionType(); fetchTransactionType();
}, 150); }, 150);
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [showEditDialog, selectedTransferType, GetData]); }, [showEditDialog, selectedTransferType, GetData]);
useEffect(() => { useEffect(() => {
@ -582,7 +575,7 @@ const EditDialog = () => {
<div className="w-full"> <div className="w-full">
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> <div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label className="form-label flex items-center gap-1 max-w-56"> <label className="form-label flex items-center gap-1 max-w-56">
Type Status Transaction Type
<span className="text-red-500"> *</span> <span className="text-red-500"> *</span>
</label> </label>
<div className="grow"> <div className="grow">
@ -596,14 +589,17 @@ const EditDialog = () => {
<SelectValue placeholder="Select" /> <SelectValue placeholder="Select" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="D">Disbursement </SelectItem> <SelectItem value="D">Disbursement </SelectItem>
<SelectItem value="O">Other </SelectItem> <SelectItem value="O">Other </SelectItem>
<SelectItem value="CA">Change Customer to Agent </SelectItem> <SelectItem value="CA">Change Group Emoney Customer to Agent </SelectItem>
<SelectItem value="AC">Change Agent to Customer </SelectItem> <SelectItem value="AC">Change Group Emoney Agent to Customer </SelectItem>
<SelectItem value="PA">Change Group Point Agent to Customer </SelectItem>
<SelectItem value="PC">Change Group Point Customer to Agent</SelectItem>
<SelectItem value="CE">Return Customer Emoney </SelectItem> <SelectItem value="CE">Return Customer Emoney </SelectItem>
<SelectItem value="AD">Return Agent Deposit </SelectItem> <SelectItem value="AD">Return Agent Deposit </SelectItem>
<SelectItem value="AM">Return Agent Merchant </SelectItem> <SelectItem value="AM">Return Agent Merchant </SelectItem>
<SelectItem value="AE">Return Agent Emoney </SelectItem> <SelectItem value="AE">Return Agent Emoney </SelectItem>
<SelectItem value="R">Reward Point </SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
@ -659,8 +655,7 @@ const EditDialog = () => {
</div> </div>
</div> </div>
</div> </div>
{/* Group Permission Section - Read Only Display */}
<div className="w-full"> <div className="w-full">
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> <div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label className="form-label flex items-center gap-1 max-w-56"> <label className="form-label flex items-center gap-1 max-w-56">
@ -671,12 +666,11 @@ const EditDialog = () => {
<Input <Input
type="text" type="text"
placeholder="No groups selected" placeholder="No groups selected"
value={selectedPermissionNames || ""} value={selectedPermissionNames || ''}
readOnly readOnly
className="bg-gray-100 mb-2" className="bg-gray-100 mb-2"
/> />
{/* Groups Selection Area */}
<div className="border rounded-md p-3 max-h-48 overflow-y-auto"> <div className="border rounded-md p-3 max-h-48 overflow-y-auto">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2"> <div className="grid grid-cols-1 md:grid-cols-2 gap-2">
{groups.map((group) => ( {groups.map((group) => (
@ -699,7 +693,7 @@ const EditDialog = () => {
</div> </div>
</div> </div>
</div> </div>
<div className="flex justify-end pt-2.5 gap-5"> <div className="flex justify-end pt-2.5 gap-5">
<Button variant={'default'} type="submit" disabled={isSubmitting}> <Button variant={'default'} type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Saving...' : 'Save Changes'} {isSubmitting ? 'Saving...' : 'Save Changes'}
@ -707,15 +701,14 @@ const EditDialog = () => {
</div> </div>
</div> </div>
</form> </form>
{/* Transaction Fee Section */} <ManageTransferFeeContextProvider transactionTypeId={selectedTransferType}>
<ManageTransferFeeContextProvider>
<Container> <Container>
<div className="grid gap-5 lg:gap-7.5 mt-5"> <div className="grid gap-5 lg:gap-7.5 mt-5">
<DataGridInner /> <DataGridInner />
</div> </div>
<AddFeeDialog /> <AddFeeDialog />
<DeleteFeeDialog /> <DeleteFeeDialog />
<EditFeeDialog /> <EditFeeDialog />
</Container> </Container>
</ManageTransferFeeContextProvider> </ManageTransferFeeContextProvider>
</div> </div>
@ -725,4 +718,4 @@ const EditDialog = () => {
); );
}; };
export { EditDialog }; export { EditDialog };

View File

@ -147,15 +147,31 @@ const ManageTransferTypeContextProvider = ({ children }: { children: React.React
meta: { headerClassName: 'w-[250px]' } meta: { headerClassName: 'w-[250px]' }
}, },
{ {
accessorFn: (row) => row.type, accessorFn: (row: {type: string }) => {
const mapping: Record<string, string> = {
D: 'Disbursement',
O: 'Other',
CA: 'Change Group Emoney Customer to Agent',
AC:'Change Group Emoney Agent to Customer',
PC: 'Change Group Point Agent to Customer',
PA: 'Change Group Point Customer to Agent',
CE:'Return Customer Emoney',
AD:'Return Agent Deposit',
AM: 'Return Agent Merchant',
AE: 'Return Agent Emoney',
R: 'Reward Point',
};
return mapping[row.type] || 'Unknown';
},
id: 'type', id: 'type',
header: ({ column }) => ( header: ({ column }) => (
<DataGridColumnHeader title="TransactionType Status" column={column} /> <DataGridColumnHeader title="Status Transaction Type" column={column} />
), ),
enableSorting: false, enableSorting: true,
enableHiding: false, enableHiding: false,
meta: { headerClassName: 'w-[250px]' } meta: { headerClassName: 'w-[250px]' }
}, },
{ {
accessorFn: (row) => (row.status === 'Y' ? 'Active' : 'Inactive'), accessorFn: (row) => (row.status === 'Y' ? 'Active' : 'Inactive'),
id: 'status', id: 'status',
@ -223,7 +239,7 @@ const ManageTransferTypeContextProvider = ({ children }: { children: React.React
order_direction: orderDirection, order_direction: orderDirection,
filter: JSON.stringify(filter) filter: JSON.stringify(filter)
}); });
console.log(response?.data.list); // console.log(response?.data.list);
return { data: response?.data.list, totalCount: response?.data.total_count }; return { data: response?.data.list, totalCount: response?.data.total_count };
}; };