Merge branch 'master' of https://git.shiblysolution.id/TPAY/dashboard
This commit is contained in:
98
src/pages/members/kyc-delete-member/blocks/DetailDialog.tsx
Normal file
98
src/pages/members/kyc-delete-member/blocks/DetailDialog.tsx
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogBody,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { useManageKycDeletionContext } from '../hooks';
|
||||||
|
import { apiConfig } from '@/config/api.config';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const API_URL = apiConfig.service_customer;
|
||||||
|
const DetailDialog = () => {
|
||||||
|
const { showDetailDialog, setShowDetailDialog, detailKyc } = useManageKycDeletionContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={showDetailDialog} onOpenChange={setShowDetailDialog}>
|
||||||
|
<DialogContent className="container-fixed max-w-[1024px] flex flex-col p-5 overflow-hidden">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Customer Deletion Details </DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogBody>
|
||||||
|
{/* Tab Content */}
|
||||||
|
{detailKyc && detailKyc != null ? (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
{generateInput(detailKyc, null, 'Group', 'group_name', 'text', false, true)}
|
||||||
|
{generateInput(detailKyc, null, 'Username', 'username', 'text', false, true)}
|
||||||
|
{generateInput(detailKyc, null, 'Full Name', 'fullname', 'text', false, true)}
|
||||||
|
{generateInput(detailKyc, null, 'Gender', 'customers_gender', 'text', false, true)}
|
||||||
|
{generateInput(detailKyc, null, 'Date of Birth', 'birthdate', 'date', false, true)}
|
||||||
|
{generateInput(detailKyc, null, 'Mother Name', 'registered_mother_fullname', 'text', false, true)}
|
||||||
|
{generateInput(detailKyc, null, 'MSISDN', 'registered_msisdn', 'text', false, true)}
|
||||||
|
{generateInput(detailKyc, null, 'Reason Deletion', 'reason_deletion', 'text', false, true)}
|
||||||
|
{generateInput(detailKyc, null, 'Reason Note', 'reason_note', 'text', false, true)}
|
||||||
|
{generateInput(detailKyc, null, 'Status Approval', 'status_approve', 'text', false, true)}
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-2 mt-3">
|
||||||
|
<Button type="button" variant="outline" onClick={() => setShowDetailDialog(false)}>Cancel</Button>
|
||||||
|
<Button onClick={()=>{}} variant="destructive" color="warning">Reject</Button>
|
||||||
|
<Button onClick={()=>{}} variant="default" color="primary">Approve</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (<div></div>)}
|
||||||
|
</DialogBody>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DetailDialog;
|
||||||
|
|
||||||
|
function generateInput(formData:any, handleChange:any, label:string, name:string, type: string, required: boolean, disabled: boolean) {
|
||||||
|
function generateDate(isoString: string) {
|
||||||
|
return isoString.slice(0, 10); // "2000-01-18"
|
||||||
|
}
|
||||||
|
|
||||||
|
type Code = 'W' | 'Y' | 'N' | 'T' | 'P' | 'D' | 'L';
|
||||||
|
interface Reason {
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusMap: Record<Code, Reason> = {
|
||||||
|
W: {label: 'Waiting Approval'},
|
||||||
|
Y: {label: 'Approve'},
|
||||||
|
N: {label: 'Reject'},
|
||||||
|
T: {label: 'Tidak lagi menggunakan layanan'},
|
||||||
|
P: {label: 'Privasi dan keamanan'},
|
||||||
|
D: {label: 'Akun ganda'},
|
||||||
|
L: {label: 'Lainnya'}
|
||||||
|
};
|
||||||
|
if (name == 'reason_deletion' || name == 'status_approve') {
|
||||||
|
const status = formData[name] as Code
|
||||||
|
const fixStatus = statusMap[status] ?? {label: formData[name]}
|
||||||
|
formData[name] = fixStatus.label
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="w-full mt-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}<span className="text-red-500">{required?"*":""}</span>
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
className="input"
|
||||||
|
readOnly={disabled}
|
||||||
|
required={required}
|
||||||
|
type={type}
|
||||||
|
name={name}
|
||||||
|
value={formData[name]?(type === 'date' ? generateDate(formData[name]) : formData[name]):""}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ import { createContext, useCallback, useMemo, useState } from 'react';
|
|||||||
import ListToolbar from '../blocks/ListToolbar';
|
import ListToolbar from '../blocks/ListToolbar';
|
||||||
import { useCallApi } from '@/hooks';
|
import { useCallApi } from '@/hooks';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import DetailDialog from '../blocks/DetailDialog';
|
||||||
|
|
||||||
interface ManageKycDeletionProps {
|
interface ManageKycDeletionProps {
|
||||||
id: string;
|
id: string;
|
||||||
@ -28,10 +29,13 @@ interface ContextProps {
|
|||||||
filter: any
|
filter: any
|
||||||
) => Promise<{ data: ManageKycDeletionProps[]; totalCount: number } | undefined>;
|
) => Promise<{ data: ManageKycDeletionProps[]; totalCount: number } | undefined>;
|
||||||
showDetailDialog: boolean;
|
showDetailDialog: boolean;
|
||||||
handleDetailDialog: (show: boolean, selected_user: ManageKycDeletionProps | null) => void;
|
setShowDetailDialog: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
handleDetailDialog: (show: boolean, selected_user: string | null) => void;
|
||||||
showAddDialog: boolean;
|
showAddDialog: boolean;
|
||||||
handleAddDialog: (show: boolean) => void;
|
handleAddDialog: (show: boolean) => void;
|
||||||
selectedUser: ManageKycDeletionProps | null;
|
selectedIdCustomer: string | null;
|
||||||
|
detailKyc: any | null;
|
||||||
|
setDetailKyc: React.Dispatch<React.SetStateAction<any>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialProps: ContextProps = {
|
const initialProps: ContextProps = {
|
||||||
@ -40,7 +44,10 @@ const initialProps: ContextProps = {
|
|||||||
handleDetailDialog: () => {},
|
handleDetailDialog: () => {},
|
||||||
showAddDialog: false,
|
showAddDialog: false,
|
||||||
handleAddDialog: () => {},
|
handleAddDialog: () => {},
|
||||||
selectedUser: null
|
selectedIdCustomer: null,
|
||||||
|
setShowDetailDialog: () => { },
|
||||||
|
detailKyc: async () => {},
|
||||||
|
setDetailKyc: () => { },
|
||||||
};
|
};
|
||||||
|
|
||||||
const ManageKycDeletionContext = createContext<ContextProps>(initialProps);
|
const ManageKycDeletionContext = createContext<ContextProps>(initialProps);
|
||||||
@ -79,7 +86,8 @@ export const renderStatusBadge = (statusRaw: string | null | undefined) => {
|
|||||||
const ManageKycDeletionContextProvider = ({ children }: { children: React.ReactNode }) => {
|
const ManageKycDeletionContextProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
const [showDetailDialog, setShowDetailDialog] = useState(false);
|
const [showDetailDialog, setShowDetailDialog] = useState(false);
|
||||||
const [showAddDialog, setShowAddDialog] = useState(false);
|
const [showAddDialog, setShowAddDialog] = useState(false);
|
||||||
const [selectedUser, setSelectedUser] = useState<ManageKycDeletionProps | null>(null);
|
const [selectedIdCustomer, setSelectedIdCustomer] = useState<string | null>(null);
|
||||||
|
const [detailKyc, setDetailKyc] = useState<any>();
|
||||||
const [manageKyc, setManageKyc] = useState<ManageKycDeletionProps[]>([]);
|
const [manageKyc, setManageKyc] = useState<ManageKycDeletionProps[]>([]);
|
||||||
const { GetData } = useCallApi();
|
const { GetData } = useCallApi();
|
||||||
|
|
||||||
@ -121,8 +129,13 @@ const ManageKycDeletionContextProvider = ({ children }: { children: React.ReactN
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDetailDialog = useCallback((show: boolean, selected_user: ManageKycDeletionProps | null) => {
|
const handleDetailDialog = useCallback(async (show: boolean, selected_id_customer: string | null) => {
|
||||||
setSelectedUser(show ? selected_user : null);
|
setSelectedIdCustomer(show ? selected_id_customer : null);
|
||||||
|
let detailCustomer = await GetData(`${API_URL}/customer_deletion/detail/${selected_id_customer}`, {})
|
||||||
|
setDetailKyc(detailCustomer?.data)
|
||||||
|
console.log('detailCustomer: ',detailCustomer)
|
||||||
|
console.log('detailCustomer2: ',detailCustomer?.data)
|
||||||
|
console.log('detailKyc: ', detailKyc)
|
||||||
setShowDetailDialog(show);
|
setShowDetailDialog(show);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -226,10 +239,14 @@ const ManageKycDeletionContextProvider = ({ children }: { children: React.ReactN
|
|||||||
handleDetailDialog,
|
handleDetailDialog,
|
||||||
showAddDialog,
|
showAddDialog,
|
||||||
handleAddDialog,
|
handleAddDialog,
|
||||||
selectedUser
|
selectedIdCustomer,
|
||||||
|
setShowDetailDialog,
|
||||||
|
setDetailKyc,
|
||||||
|
detailKyc
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Toaster expand visibleToasts={9} duration={3000} />
|
<Toaster expand visibleToasts={9} duration={3000} />
|
||||||
|
<DetailDialog />
|
||||||
|
|
||||||
<DataGridProvider
|
<DataGridProvider
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
|||||||
@ -7,15 +7,6 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue
|
SelectValue
|
||||||
} from '@/components/ui/select';
|
} from '@/components/ui/select';
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
|
||||||
import {
|
|
||||||
Command,
|
|
||||||
CommandEmpty,
|
|
||||||
CommandGroup,
|
|
||||||
CommandInput,
|
|
||||||
CommandItem,
|
|
||||||
CommandList
|
|
||||||
} from '@/components/ui/command';
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogBody,
|
DialogBody,
|
||||||
@ -33,6 +24,8 @@ import { toast } from 'sonner';
|
|||||||
import { useCallApi } from '@/hooks';
|
import { useCallApi } from '@/hooks';
|
||||||
import { doSaveLogActivity } from '@/actions/GlobalActions';
|
import { doSaveLogActivity } from '@/actions/GlobalActions';
|
||||||
import { getAuth } from '@/auth';
|
import { getAuth } from '@/auth';
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||||
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command';
|
||||||
|
|
||||||
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;
|
||||||
@ -41,14 +34,12 @@ const API_URL_CUSTOMER = apiConfig.service_customer;
|
|||||||
interface WalletProps {
|
interface WalletProps {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CustomerProps {
|
interface CustomerProps {
|
||||||
id: string;
|
id: string;
|
||||||
username: string;
|
username: string;
|
||||||
msisdn: string;
|
msisdn: string;
|
||||||
fullname?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TransactionTypeProps {
|
interface TransactionTypeProps {
|
||||||
@ -61,18 +52,32 @@ const EditFeeDialog = () => {
|
|||||||
const { showEditFeeDialog, handleEditFeeDialog, selectedTransferFee } =
|
const { showEditFeeDialog, handleEditFeeDialog, selectedTransferFee } =
|
||||||
useManageTransferFeeContext();
|
useManageTransferFeeContext();
|
||||||
const { reload } = useDataGrid();
|
const { reload } = useDataGrid();
|
||||||
const [wallets, setWallets] = useState<WalletProps[]>([]);
|
|
||||||
const { GetData, PutData } = useCallApi();
|
const { GetData, PutData } = useCallApi();
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
||||||
const [transactionTypes, setTransactionTypes] = useState<TransactionTypeProps[]>([]);
|
|
||||||
const parsedUser = getAuth()?.user;
|
const parsedUser = getAuth()?.user;
|
||||||
|
|
||||||
|
const [wallets, setWallets] = useState<WalletProps[]>([]);
|
||||||
const [customers, setCustomers] = useState<CustomerProps[]>([]);
|
const [customers, setCustomers] = useState<CustomerProps[]>([]);
|
||||||
|
const [transactionTypes, setTransactionTypes] = useState<TransactionTypeProps[]>([]);
|
||||||
|
const [transactionTypeName, setTransactionTypeName] = useState('');
|
||||||
|
const customersWithNames = customers.map((customer) => ({
|
||||||
|
id: customer.id,
|
||||||
|
name: customer.username
|
||||||
|
}));
|
||||||
|
const [customerSearchTerm, setCustomerSearchTerm] = useState('');
|
||||||
|
const [showCustomerSearch, setShowCustomerSearch] = useState(false);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const [alert, setAlert] = useState({
|
const [alert, setAlert] = useState({
|
||||||
show: false,
|
show: false,
|
||||||
message: ''
|
message: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Loading states
|
||||||
|
const [isLoadingTransferFee, setIsLoadingTransferFee] = useState(false);
|
||||||
|
const [isLoadingTransactionType, setIsLoadingTransactionType] = useState(false);
|
||||||
|
const [isLoadingWallets, setIsLoadingWallets] = useState(false);
|
||||||
|
const [isLoadingCustomers, setIsLoadingCustomers] = useState(false);
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
@ -109,7 +114,7 @@ const EditFeeDialog = () => {
|
|||||||
updated_at: formattedTime
|
updated_at: formattedTime
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [showEditFeeDialog]);
|
}, [showEditFeeDialog, parsedUser?.username]);
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
if (selectedTransferFee) {
|
if (selectedTransferFee) {
|
||||||
@ -178,10 +183,11 @@ const EditFeeDialog = () => {
|
|||||||
reload();
|
reload();
|
||||||
handleEditFeeDialog(false, null);
|
handleEditFeeDialog(false, null);
|
||||||
const createActivity = {
|
const createActivity = {
|
||||||
module: 'Manage Transfer Type',
|
module: 'Manage Transfer Fee',
|
||||||
description: `Edit Transfer Type => ${selectedTransferFee}`,
|
description: `Edit Transfer Fee => ${formField.name}`,
|
||||||
action: 'U'
|
action: 'U'
|
||||||
};
|
};
|
||||||
|
doSaveLogActivity(createActivity);
|
||||||
} else {
|
} else {
|
||||||
setAlert({ show: true, message: response?.message || 'Failed to update transfer fee' });
|
setAlert({ show: true, message: response?.message || 'Failed to update transfer fee' });
|
||||||
}
|
}
|
||||||
@ -196,6 +202,7 @@ const EditFeeDialog = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const fetchWallets = useCallback(async () => {
|
const fetchWallets = useCallback(async () => {
|
||||||
|
setIsLoadingWallets(true);
|
||||||
const params = {
|
const params = {
|
||||||
limit: 100,
|
limit: 100,
|
||||||
page: 1,
|
page: 1,
|
||||||
@ -213,6 +220,8 @@ const EditFeeDialog = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching wallets', error);
|
console.error('Error fetching wallets', error);
|
||||||
setWallets([]);
|
setWallets([]);
|
||||||
|
} finally {
|
||||||
|
setIsLoadingWallets(false);
|
||||||
}
|
}
|
||||||
}, [GetData]);
|
}, [GetData]);
|
||||||
|
|
||||||
@ -225,6 +234,7 @@ const EditFeeDialog = () => {
|
|||||||
if (!showEditFeeDialog) return;
|
if (!showEditFeeDialog) 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;
|
||||||
const response = await GetData(`${API_URL_CUSTOMER}/customer/list`, {
|
const response = await GetData(`${API_URL_CUSTOMER}/customer/list`, {
|
||||||
@ -237,16 +247,19 @@ const EditFeeDialog = () => {
|
|||||||
setCustomers(response?.data.list || []);
|
setCustomers(response?.data.list || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching customers', error);
|
console.error('Error fetching customers', error);
|
||||||
|
} finally {
|
||||||
|
setIsLoadingCustomers(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getCustomerList([{ id: 'msisdn', desc: false }]);
|
getCustomerList([{ id: 'id', desc: false }]);
|
||||||
}, [showEditFeeDialog, GetData]);
|
}, [showEditFeeDialog, GetData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!showEditFeeDialog) return;
|
if (!showEditFeeDialog) 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`, {
|
||||||
@ -259,6 +272,8 @@ const EditFeeDialog = () => {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -272,6 +287,7 @@ const EditFeeDialog = () => {
|
|||||||
|
|
||||||
const fetchTransactionFee = useCallback(
|
const fetchTransactionFee = useCallback(
|
||||||
async (id: string) => {
|
async (id: string) => {
|
||||||
|
setIsLoadingTransferFee(true);
|
||||||
try {
|
try {
|
||||||
const response = await GetData(`${API_URL}/transactionfees/getdata/${id}`, {});
|
const response = await GetData(`${API_URL}/transactionfees/getdata/${id}`, {});
|
||||||
|
|
||||||
@ -300,10 +316,16 @@ const EditFeeDialog = () => {
|
|||||||
updated_by: parsedUser?.username,
|
updated_by: parsedUser?.username,
|
||||||
updated_at: new Date().toISOString().slice(0, 19).replace('T', ' ')
|
updated_at: new Date().toISOString().slice(0, 19).replace('T', ' ')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (response.data.transaction_type?.name) {
|
||||||
|
setTransactionTypeName(response.data.transaction_type.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching transaction fee details', error);
|
console.error('Error fetching transaction fee details', error);
|
||||||
setAlert({ show: true, message: 'Failed to fetch transaction fee details' });
|
setAlert({ show: true, message: 'Failed to fetch transaction fee details' });
|
||||||
|
} finally {
|
||||||
|
setIsLoadingTransferFee(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[GetData, parsedUser?.username]
|
[GetData, parsedUser?.username]
|
||||||
@ -321,12 +343,49 @@ const EditFeeDialog = () => {
|
|||||||
hasFetchedRef.current = false;
|
hasFetchedRef.current = false;
|
||||||
}
|
}
|
||||||
}, [selectedTransferFee, showEditFeeDialog, fetchTransactionFee]);
|
}, [selectedTransferFee, showEditFeeDialog, fetchTransactionFee]);
|
||||||
|
|
||||||
const handleCloseDialog = () => {
|
const handleCloseDialog = () => {
|
||||||
setFormField(initialState);
|
setFormField(initialState);
|
||||||
setAlert({ show: false, message: '' });
|
setAlert({ show: false, message: '' });
|
||||||
|
setCustomerSearchTerm('');
|
||||||
|
setOpen(false);
|
||||||
handleEditFeeDialog(false, null);
|
handleEditFeeDialog(false, null);
|
||||||
};
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
if (!showEditFeeDialog) {
|
||||||
|
setCustomerSearchTerm('');
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
}, [showEditFeeDialog]);
|
||||||
|
const renderSelectWithLoading = (
|
||||||
|
value: string,
|
||||||
|
onChangeHandler: (value: string) => void,
|
||||||
|
options: { id: string; name: string }[] | null,
|
||||||
|
placeholder: string,
|
||||||
|
isLoading: boolean
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<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
|
<Dialog
|
||||||
@ -363,364 +422,426 @@ const EditFeeDialog = () => {
|
|||||||
</Alert>
|
</Alert>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<form action="" onSubmit={doUpdateTransferFee}>
|
{isLoadingTransferFee ? (
|
||||||
<div className="card flex flex-col gap-5">
|
<div className="flex flex-col items-center justify-center p-8">
|
||||||
<div className="card-body grid gap-5 p-0">
|
<div className="animate-pulse flex space-x-4 w-full">
|
||||||
<div className="w-full">
|
<div className="flex-1 space-y-4 py-1">
|
||||||
<label className="form-label">
|
<div className="h-4 bg-gray-200 rounded w-3/4"></div>
|
||||||
Transfer Fee Name <span className="text-red-500">*</span>
|
<div className="space-y-2">
|
||||||
</label>
|
<div className="h-4 bg-gray-200 rounded"></div>
|
||||||
<Input
|
<div className="h-4 bg-gray-200 rounded w-5/6"></div>
|
||||||
className="input"
|
</div>
|
||||||
type="text"
|
|
||||||
autoComplete="off"
|
|
||||||
value={formField.name}
|
|
||||||
onChange={({ target }) =>
|
|
||||||
setFormField((prev) => ({ ...prev, name: target.value }))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div className="w-full">
|
<p className="mt-4 text-gray-500">Loading transfer fee details...</p>
|
||||||
<label className="form-label">
|
</div>
|
||||||
Description <span className="text-red-500">*</span>
|
) : (
|
||||||
</label>
|
<form action="" onSubmit={doUpdateTransferFee}>
|
||||||
<Input
|
<div className="card flex flex-col gap-5">
|
||||||
className="input"
|
<div className="card-body grid gap-5 p-0">
|
||||||
type="text"
|
|
||||||
autoComplete="off"
|
|
||||||
value={formField.description}
|
|
||||||
onChange={({ target }) =>
|
|
||||||
setFormField((prev) => ({ ...prev, description: target.value }))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full">
|
|
||||||
<label className="form-label">Minimum Amount</label>
|
|
||||||
<NumericFormat
|
|
||||||
className="input"
|
|
||||||
value={formField.minimum_amount}
|
|
||||||
thousandSeparator="."
|
|
||||||
decimalSeparator=","
|
|
||||||
allowNegative={false}
|
|
||||||
onValueChange={(values) => {
|
|
||||||
setFormField((prev) => ({
|
|
||||||
...prev,
|
|
||||||
minimum_amount: values.floatValue || 0
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
placeholder="Enter Minimum Amount"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full">
|
|
||||||
<label className="form-label">Maximum Amount</label>
|
|
||||||
<NumericFormat
|
|
||||||
className="input"
|
|
||||||
value={formField.maximum_amount}
|
|
||||||
thousandSeparator="."
|
|
||||||
decimalSeparator=","
|
|
||||||
allowNegative={false}
|
|
||||||
onValueChange={(values) => {
|
|
||||||
setFormField((prev) => ({
|
|
||||||
...prev,
|
|
||||||
maximum_amount: values.floatValue || 0
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
placeholder="Enter Maximum Amount"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full">
|
|
||||||
<label className="form-label">
|
|
||||||
Period Start <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<Input
|
|
||||||
className="input"
|
|
||||||
type="date"
|
|
||||||
autoComplete="off"
|
|
||||||
value={formField.period_start}
|
|
||||||
onChange={({ target }) =>
|
|
||||||
setFormField((prev) => ({ ...prev, period_start: target.value }))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full">
|
|
||||||
<label className="form-label">
|
|
||||||
Period End <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<Input
|
|
||||||
className="input"
|
|
||||||
type="date"
|
|
||||||
autoComplete="off"
|
|
||||||
value={formField.period_end}
|
|
||||||
onChange={({ target }) =>
|
|
||||||
setFormField((prev) => ({ ...prev, period_end: target.value }))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full">
|
|
||||||
<label className="form-label">Deduct Amount</label>
|
|
||||||
<NumericFormat
|
|
||||||
className="input"
|
|
||||||
value={formField.deduct_amount}
|
|
||||||
thousandSeparator="."
|
|
||||||
decimalSeparator=","
|
|
||||||
allowNegative={false}
|
|
||||||
onValueChange={(values) => {
|
|
||||||
setFormField((prev) => ({
|
|
||||||
...prev,
|
|
||||||
deduct_amount: values.floatValue || 0
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
placeholder="Enter Deduct Amount"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full">
|
|
||||||
<label className="form-label">Deduct Percentage</label>
|
|
||||||
<NumericFormat
|
|
||||||
className="input"
|
|
||||||
value={formField.deduct_percentage}
|
|
||||||
thousandSeparator="."
|
|
||||||
decimalSeparator=","
|
|
||||||
allowNegative={false}
|
|
||||||
onValueChange={(values) => {
|
|
||||||
setFormField((prev) => ({
|
|
||||||
...prev,
|
|
||||||
deduct_percentage: values.floatValue || 0
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
placeholder="Enter Deduct Percentage"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full">
|
|
||||||
<label className="form-label">Fee Amount</label>
|
|
||||||
<NumericFormat
|
|
||||||
className="input"
|
|
||||||
value={formField.fee_amount}
|
|
||||||
thousandSeparator="."
|
|
||||||
decimalSeparator=","
|
|
||||||
allowNegative={false}
|
|
||||||
onValueChange={(values) => {
|
|
||||||
setFormField((prev) => ({
|
|
||||||
...prev,
|
|
||||||
fee_amount: values.floatValue || 0
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
placeholder="Enter Fee Amount"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full">
|
|
||||||
<label className="form-label">
|
|
||||||
Deduct From <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<Select
|
|
||||||
value={formField.deduct_from}
|
|
||||||
onValueChange={(value) => setFormField({ ...formField, deduct_from: value })}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Select Deduct From" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="D">Destination Member</SelectItem>
|
|
||||||
<SelectItem value="S">Source Member</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full">
|
|
||||||
<label className="form-label">
|
|
||||||
Deduct From Destination <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<Select
|
|
||||||
value={formField.deduct_from_account}
|
|
||||||
onValueChange={(value) =>
|
|
||||||
setFormField({ ...formField, deduct_from_account: value })
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Select Wallet" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{wallets.map((wallet) => (
|
|
||||||
<SelectItem value={wallet.id} key={wallet.id}>
|
|
||||||
{wallet.name || wallet.description}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full">
|
|
||||||
<label className="form-label">
|
|
||||||
Credit To <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<Select
|
|
||||||
value={formField.credit_to}
|
|
||||||
onValueChange={(value) =>
|
|
||||||
setFormField({
|
|
||||||
...formField,
|
|
||||||
credit_to: value,
|
|
||||||
credit_destination:
|
|
||||||
value === 'I' ? '' : '00000000-0000-0000-0000-000000000000'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Select Credit To" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="D">Destination Member</SelectItem>
|
|
||||||
<SelectItem value="S">Source Member</SelectItem>
|
|
||||||
<SelectItem value="I">Input Customer</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{formField.credit_to === 'I' && (
|
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<label className="form-label">
|
<label className="form-label">
|
||||||
Credit Destination <span className="text-red-500">*</span>
|
Transaction Type ID <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<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>
|
||||||
|
)}
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="transaction_type"
|
||||||
|
value={formField.transaction_type}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full">
|
||||||
|
<label className="form-label">
|
||||||
|
Transfer Fee Name <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
className="input"
|
||||||
|
type="text"
|
||||||
|
autoComplete="off"
|
||||||
|
value={formField.name}
|
||||||
|
onChange={({ target }) =>
|
||||||
|
setFormField((prev) => ({ ...prev, name: target.value }))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full">
|
||||||
|
<label className="form-label">
|
||||||
|
Description <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
className="input"
|
||||||
|
type="text"
|
||||||
|
autoComplete="off"
|
||||||
|
value={formField.description}
|
||||||
|
onChange={({ target }) =>
|
||||||
|
setFormField((prev) => ({ ...prev, description: target.value }))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full">
|
||||||
|
<label className="form-label">Minimum Amount</label>
|
||||||
|
<NumericFormat
|
||||||
|
className="input"
|
||||||
|
value={formField.minimum_amount}
|
||||||
|
thousandSeparator="."
|
||||||
|
decimalSeparator=","
|
||||||
|
allowNegative={false}
|
||||||
|
onValueChange={(values) => {
|
||||||
|
setFormField((prev) => ({
|
||||||
|
...prev,
|
||||||
|
minimum_amount: values.floatValue || 0
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
placeholder="Enter Minimum Amount"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full">
|
||||||
|
<label className="form-label">Maximum Amount</label>
|
||||||
|
<NumericFormat
|
||||||
|
className="input"
|
||||||
|
value={formField.maximum_amount}
|
||||||
|
thousandSeparator="."
|
||||||
|
decimalSeparator=","
|
||||||
|
allowNegative={false}
|
||||||
|
onValueChange={(values) => {
|
||||||
|
setFormField((prev) => ({
|
||||||
|
...prev,
|
||||||
|
maximum_amount: values.floatValue || 0
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
placeholder="Enter Maximum Amount"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full">
|
||||||
|
<label className="form-label">
|
||||||
|
Period Start <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
className="input"
|
||||||
|
type="date"
|
||||||
|
autoComplete="off"
|
||||||
|
value={formField.period_start}
|
||||||
|
onChange={({ target }) =>
|
||||||
|
setFormField((prev) => ({ ...prev, period_start: target.value }))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full">
|
||||||
|
<label className="form-label">
|
||||||
|
Period End <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
className="input"
|
||||||
|
type="date"
|
||||||
|
autoComplete="off"
|
||||||
|
value={formField.period_end}
|
||||||
|
onChange={({ target }) =>
|
||||||
|
setFormField((prev) => ({ ...prev, period_end: target.value }))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full">
|
||||||
|
<label className="form-label">Deduct Amount</label>
|
||||||
|
<NumericFormat
|
||||||
|
className="input"
|
||||||
|
value={formField.deduct_amount}
|
||||||
|
thousandSeparator="."
|
||||||
|
decimalSeparator=","
|
||||||
|
allowNegative={false}
|
||||||
|
onValueChange={(values) => {
|
||||||
|
setFormField((prev) => ({
|
||||||
|
...prev,
|
||||||
|
deduct_amount: values.floatValue || 0
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
placeholder="Enter Deduct Amount"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full">
|
||||||
|
<label className="form-label">Deduct Percentage</label>
|
||||||
|
<NumericFormat
|
||||||
|
className="input"
|
||||||
|
value={formField.deduct_percentage}
|
||||||
|
thousandSeparator="."
|
||||||
|
decimalSeparator=","
|
||||||
|
allowNegative={false}
|
||||||
|
onValueChange={(values) => {
|
||||||
|
setFormField((prev) => ({
|
||||||
|
...prev,
|
||||||
|
deduct_percentage: values.floatValue || 0
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
placeholder="Enter Deduct Percentage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full">
|
||||||
|
<label className="form-label">Fee Amount</label>
|
||||||
|
<NumericFormat
|
||||||
|
className="input"
|
||||||
|
value={formField.fee_amount}
|
||||||
|
thousandSeparator="."
|
||||||
|
decimalSeparator=","
|
||||||
|
allowNegative={false}
|
||||||
|
onValueChange={(values) => {
|
||||||
|
setFormField((prev) => ({
|
||||||
|
...prev,
|
||||||
|
fee_amount: values.floatValue || 0
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
placeholder="Enter Fee Amount"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full">
|
||||||
|
<label className="form-label">
|
||||||
|
Deduct From <span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<Select
|
<Select
|
||||||
value={formField.credit_destination}
|
value={formField.deduct_from}
|
||||||
onValueChange={(value) =>
|
onValueChange={(value) => setFormField({ ...formField, deduct_from: value })}
|
||||||
setFormField({ ...formField, credit_destination: value })
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Select Customer" />
|
<SelectValue placeholder="Select Deduct From" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{customers.map((customer) => (
|
<SelectItem value="D">Destination Member</SelectItem>
|
||||||
<SelectItem value={customer.id} key={customer.id}>
|
<SelectItem value="S">Source Member</SelectItem>
|
||||||
{customer.fullname || `${customer.username} - ${customer.msisdn}`}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
<div className="w-full">
|
||||||
<div className="w-full">
|
<label className="form-label">
|
||||||
<label className="form-label">
|
Deduct From Destination <span className="text-red-500">*</span>
|
||||||
Credit Destination Account <span className="text-red-500">*</span>
|
</label>
|
||||||
</label>
|
{renderSelectWithLoading(
|
||||||
<Select
|
formField.deduct_from_account,
|
||||||
value={formField.credit_destination_account}
|
(value) => setFormField({ ...formField, deduct_from_account: value }),
|
||||||
onValueChange={(value) =>
|
wallets,
|
||||||
setFormField({ ...formField, credit_destination_account: value })
|
'Select Wallet',
|
||||||
}
|
isLoadingWallets
|
||||||
>
|
)}
|
||||||
<SelectTrigger>
|
</div>
|
||||||
<SelectValue placeholder="Select Wallet" />
|
|
||||||
</SelectTrigger>
|
<div className="w-full">
|
||||||
<SelectContent>
|
<label className="form-label">
|
||||||
{wallets.map((wallet) => (
|
Credit To <span className="text-red-500">*</span>
|
||||||
<SelectItem value={wallet.id} key={wallet.id}>
|
</label>
|
||||||
{wallet.name || wallet.description}
|
<Select
|
||||||
</SelectItem>
|
value={formField.credit_to}
|
||||||
))}
|
onValueChange={(value) =>
|
||||||
</SelectContent>
|
setFormField({
|
||||||
</Select>
|
...formField,
|
||||||
|
credit_to: value,
|
||||||
|
credit_destination:
|
||||||
|
value === 'I' ? '' : '00000000-0000-0000-0000-000000000000'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select Credit To" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="D">Destination Member</SelectItem>
|
||||||
|
<SelectItem value="S">Source Member</SelectItem>
|
||||||
|
<SelectItem value="I">Input Customer</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{formField.credit_to === 'I' && (
|
||||||
|
<div className="w-full">
|
||||||
|
<label className="form-label">
|
||||||
|
Credit Destination <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<div
|
||||||
|
className="flex w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 cursor-pointer"
|
||||||
|
onClick={() => setOpen(!open)}
|
||||||
|
>
|
||||||
|
<span className="truncate">
|
||||||
|
{customers.find(customer => customer.id === formField.credit_destination)?.username || 'Search customer...'}
|
||||||
|
</span>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="h-4 w-4 opacity-50">
|
||||||
|
<path d="m6 9 6 6 6-6"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{open && (
|
||||||
|
<div className="absolute left-0 right-0 top-full z-50 mt-1 max-h-52 overflow-auto rounded-md border border-gray-200 bg-white shadow-lg">
|
||||||
|
<div className="sticky top-0 bg-white p-2 border-b">
|
||||||
|
<Input
|
||||||
|
className="h-8 text-sm"
|
||||||
|
type="text"
|
||||||
|
placeholder="Search customer..."
|
||||||
|
value={customerSearchTerm}
|
||||||
|
onChange={(e) => setCustomerSearchTerm(e.target.value)}
|
||||||
|
autoComplete="off"
|
||||||
|
// Prevent clicks from closing the dropdown
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
// Auto focus the input when opened
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{customers
|
||||||
|
.filter(customer =>
|
||||||
|
customer.username.toLowerCase().includes(customerSearchTerm.toLowerCase()) ||
|
||||||
|
customer.msisdn.includes(customerSearchTerm)
|
||||||
|
)
|
||||||
|
.map(customer => (
|
||||||
|
<div
|
||||||
|
key={customer.id}
|
||||||
|
className="cursor-pointer px-3 py-1.5 text-sm hover:bg-gray-100"
|
||||||
|
onClick={() => {
|
||||||
|
setFormField({
|
||||||
|
...formField,
|
||||||
|
credit_destination: customer.id
|
||||||
|
});
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{customer.username}
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
|
{customers.filter(customer =>
|
||||||
|
customer.username.toLowerCase().includes(customerSearchTerm.toLowerCase()) ||
|
||||||
|
customer.msisdn.includes(customerSearchTerm)
|
||||||
|
).length === 0 && (
|
||||||
|
<div className="px-3 py-2 text-sm text-gray-500">No customer found</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</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 className="w-full">
|
|
||||||
<label className="form-label">
|
|
||||||
Status <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<Select
|
|
||||||
value={formField.status}
|
|
||||||
onValueChange={(value) => setFormField({ ...formField, status: value })}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Select Status" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="Y">Active</SelectItem>
|
|
||||||
<SelectItem value="N">Inactive</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full">
|
|
||||||
<label className="form-label">
|
<div className="w-full">
|
||||||
Status Include <span className="text-red-500">*</span>
|
<label className="form-label">
|
||||||
</label>
|
Credit Destination Account <span className="text-red-500">*</span>
|
||||||
<Select
|
</label>
|
||||||
value={formField.status_include}
|
{renderSelectWithLoading(
|
||||||
onValueChange={(value) => setFormField({ ...formField, status_include: value })}
|
formField.credit_destination_account,
|
||||||
>
|
(value) => setFormField({ ...formField, credit_destination_account: value }),
|
||||||
<SelectTrigger>
|
wallets,
|
||||||
<SelectValue placeholder="Select Status" />
|
'Select Wallet',
|
||||||
</SelectTrigger>
|
isLoadingWallets
|
||||||
<SelectContent>
|
)}
|
||||||
<SelectItem value="Y">Yes</SelectItem>
|
</div>
|
||||||
<SelectItem value="N">No</SelectItem>
|
|
||||||
</SelectContent>
|
<div className="w-full">
|
||||||
</Select>
|
<label className="form-label">
|
||||||
</div>
|
Status <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
<div className="w-full">
|
<Select
|
||||||
<label className="form-label">
|
value={formField.status}
|
||||||
Priority <span className="text-red-500">*</span>
|
onValueChange={(value) => setFormField({ ...formField, status: value })}
|
||||||
</label>
|
>
|
||||||
<Select
|
<SelectTrigger>
|
||||||
value={formField.priority}
|
<SelectValue placeholder="Select Status" />
|
||||||
onValueChange={(value) => setFormField({ ...formField, priority: value })}
|
</SelectTrigger>
|
||||||
>
|
<SelectContent>
|
||||||
<SelectTrigger>
|
<SelectItem value="Y">Active</SelectItem>
|
||||||
<SelectValue placeholder="Select Priority" />
|
<SelectItem value="N">Inactive</SelectItem>
|
||||||
</SelectTrigger>
|
</SelectContent>
|
||||||
<SelectContent>
|
</Select>
|
||||||
<SelectItem value="Y">Yes</SelectItem>
|
</div>
|
||||||
<SelectItem value="N">No</SelectItem>
|
|
||||||
</SelectContent>
|
<div className="w-full">
|
||||||
</Select>
|
<label className="form-label">
|
||||||
</div>
|
Status Include <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
<div className="flex justify-end pt-2.5 gap-5">
|
<Select
|
||||||
<Button variant={'default'} type="submit" disabled={isSubmitting}>
|
value={formField.status_include}
|
||||||
{isSubmitting ? 'Saving...' : 'Save Changes'}
|
onValueChange={(value) => setFormField({ ...formField, status_include: value })}
|
||||||
</Button>
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select Status" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="Y">Yes</SelectItem>
|
||||||
|
<SelectItem value="N">No</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full">
|
||||||
|
<label className="form-label">
|
||||||
|
Priority <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<Select
|
||||||
|
value={formField.priority}
|
||||||
|
onValueChange={(value) => setFormField({ ...formField, priority: value })}
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select Priority" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="Y">Yes</SelectItem>
|
||||||
|
<SelectItem value="N">No</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end pt-2.5 gap-5">
|
||||||
|
<Button
|
||||||
|
variant={'outline'}
|
||||||
|
type="button"
|
||||||
|
onClick={resetForm}
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={'default'}
|
||||||
|
type="submit"
|
||||||
|
disabled={
|
||||||
|
isSubmitting ||
|
||||||
|
isLoadingTransferFee ||
|
||||||
|
isLoadingTransactionType ||
|
||||||
|
isLoadingWallets ||
|
||||||
|
isLoadingCustomers
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{isSubmitting ? 'Saving...' : 'Save Changes'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
</form>
|
)}
|
||||||
</DialogBody>
|
</DialogBody>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { EditFeeDialog };
|
export {EditFeeDialog};
|
||||||
Reference in New Issue
Block a user