fix detail member
This commit is contained in:
@ -5,12 +5,15 @@ import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||
import { ManageKycContextProvider } from './hooks';
|
||||
import { columns, initialMember } from './Columns';
|
||||
import { useAuthContext } from '@/auth';
|
||||
import { LoaderTransparant } from '@/components';
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import ConfirmDialog from '@/components/confirm';
|
||||
import axios from 'axios';
|
||||
import { toast } from 'sonner';
|
||||
const BASE_URL = apiConfig.service_customer;
|
||||
import CustomerDialog from '../manage-members/CustomerDetailModal';
|
||||
const BASE_URL_MASTER_DATA = apiConfig.service_master_data;
|
||||
// import CustomerDialog from '../manage-members/CustomerDetailModal';
|
||||
import DetailMember from '../manage-members/blocks/DetailMember';
|
||||
import { Breadcrumbs, Link } from '@mui/material';
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
@ -37,6 +40,7 @@ const Kyc = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [members, setMembers] = useState([]);
|
||||
const [member, setMember] = useState(initialMember);
|
||||
const [profession, setProfession] = useState([]);
|
||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [dialogType, setDialogType] = useState('');
|
||||
@ -44,12 +48,12 @@ const Kyc = () => {
|
||||
const { getUser } = useAuthContext();
|
||||
|
||||
useEffect(() => {
|
||||
fetchGroups();
|
||||
fetchCustomers();
|
||||
}, []);
|
||||
|
||||
async function fetchGroups() {
|
||||
async function fetchCustomers() {
|
||||
try {
|
||||
let groups = await axios.get(`${BASE_URL}/customer/list`, {
|
||||
let customers = await axios.get(`${BASE_URL}/customer/list`, {
|
||||
params: {
|
||||
limit: 10,
|
||||
page: 1,
|
||||
@ -60,12 +64,22 @@ const Kyc = () => {
|
||||
}
|
||||
});
|
||||
let temp = 1;
|
||||
let resMembers = groups.data.data.list.map((el: any) => {
|
||||
let resMembers = customers.data.data.list.map((el: any) => {
|
||||
el.no = temp++;
|
||||
el.name = el.fullname;
|
||||
return el;
|
||||
});
|
||||
setMembers(resMembers);
|
||||
let getProfession: any = await axios.get(`${BASE_URL_MASTER_DATA}/profession/list`, {
|
||||
params: {
|
||||
limit: 50,
|
||||
page: 1,
|
||||
with_deleted: false,
|
||||
order_field: 'name',
|
||||
order_direction: 'ASC',
|
||||
}
|
||||
});
|
||||
setProfession(getProfession.data.data.list)
|
||||
} catch (error: any) {
|
||||
toast.error(error.message);
|
||||
console.log(error);
|
||||
@ -90,6 +104,7 @@ const Kyc = () => {
|
||||
}
|
||||
|
||||
const handleYes = async () => {
|
||||
setLoading(true)
|
||||
const userLogin: any = await getUser();
|
||||
const updateData: any = member;
|
||||
const customerId = member.id;
|
||||
@ -134,17 +149,24 @@ const Kyc = () => {
|
||||
description: description
|
||||
});
|
||||
}
|
||||
await fetchGroups();
|
||||
await fetchCustomers();
|
||||
setDialogOpen(false);
|
||||
setIsDialogOpen(false);
|
||||
toast.success('Success Update Kyc Member');
|
||||
toast.success(`Success Update & ${dialogType} Kyc Member`);
|
||||
} catch (error: any) {
|
||||
setDialogOpen(false);
|
||||
setIsDialogOpen(false);
|
||||
toast.error(error.message);
|
||||
setLoading(false)
|
||||
}
|
||||
};
|
||||
|
||||
function setShowAddDialog(el: any) {
|
||||
setIsDialogOpen(el)
|
||||
}
|
||||
|
||||
if (loading) return <LoaderTransparant />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
@ -161,12 +183,23 @@ const Kyc = () => {
|
||||
onNo={() => setDialogOpen(false)}
|
||||
/>
|
||||
{ member.id ? (
|
||||
<CustomerDialog
|
||||
open={isDialogOpen}
|
||||
// <CustomerDialog
|
||||
// open={isDialogOpen}
|
||||
// handleClose={closeDialog}
|
||||
// handleSubmit={handleSubmit}
|
||||
// initialData={member}
|
||||
// viewStats={true}
|
||||
// page={'kyc'}
|
||||
// />
|
||||
<DetailMember
|
||||
showAddDialog={isDialogOpen}
|
||||
setShowAddDialog={setShowAddDialog}
|
||||
handleClose={closeDialog}
|
||||
handleReject={handleReject}
|
||||
handleSubmit={handleSubmit}
|
||||
initialData={member}
|
||||
viewStats={true}
|
||||
fetchCustomers={fetchCustomers}
|
||||
profession={profession}
|
||||
page={'kyc'}
|
||||
/>
|
||||
): ""}
|
||||
|
||||
@ -295,8 +295,7 @@ const CustomerDialog = ({ open, handleClose, handleSubmit, initialData, viewStat
|
||||
<TextField disabled={viewOnly} fullWidth margin="dense" label="iBank Number" name="ibank_number" value={formData.ibank_number} onChange={handleChange} />
|
||||
<Divider className="pt-7"/>
|
||||
{/* {getAdmAccess(page, formData, handleClose, fetchCustomers)} */}
|
||||
{
|
||||
page === 'kyc' ? (
|
||||
{ page === 'kyc' ? (
|
||||
<>
|
||||
<Typography sx={{color:'grey'}}>Approval</Typography>
|
||||
<TextField fullWidth required={page === 'kyc'?true:false} margin="dense" label="Approval Description" name="description" value={formData.description} onChange={handleChange} />
|
||||
@ -589,23 +588,21 @@ function showCustomerWallet(customerid: any) {
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Typography variant="subtitle2" color="text.secondary">
|
||||
Name
|
||||
</Typography>
|
||||
<Typography variant="subtitle2" color="text.secondary">Name</Typography>
|
||||
<Typography variant="body1" fontWeight={500}>
|
||||
{item.wallet.name}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="subtitle2" color="text.secondary">
|
||||
Description
|
||||
</Typography>
|
||||
<Typography variant="subtitle2" color="text.secondary">Description</Typography>
|
||||
<Typography variant="body1">{item.wallet.description}</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="subtitle2" color="text.secondary">
|
||||
Transaction Today
|
||||
</Typography>
|
||||
<Typography variant="subtitle2" color="text.secondary">Balance</Typography>
|
||||
<Typography variant="body1">{item.amount}</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="subtitle2" color="text.secondary">Transaction Today</Typography>
|
||||
<Typography variant="body1">{item.transaction_number_today}</Typography>
|
||||
</Box>
|
||||
</ListItem>
|
||||
|
||||
@ -3,20 +3,22 @@ import { apiConfig } from '@/config/api.config';
|
||||
import { columns, Members, initialMember } from './Columns';
|
||||
import { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import CustomerDialog from './CustomerDetailModal';
|
||||
// import CustomerDialog from './CustomerDetailModal';
|
||||
import DetailMember from './blocks/DetailMember';
|
||||
import ConfirmDialog from '@/components/confirm';
|
||||
import { useAuthContext } from '@/auth';
|
||||
import { ScreenLoader } from '@/components';
|
||||
import { LoaderTransparant } from '@/components';
|
||||
import { toast } from 'sonner';
|
||||
import { Breadcrumbs, Link } from '@mui/material';
|
||||
const BASE_URL_MASTER_DATA = apiConfig.service_master_data;
|
||||
const BASE_URL = apiConfig.service_customer;
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
const ManageMembers = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [members, setMembers] = useState([]);
|
||||
const [selectedMember, setSelectedMember] = useState('');
|
||||
const [member, setMember] = useState(initialMember);
|
||||
const [profession, setProfession] = useState([]);
|
||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [dialogType, setDialogType] = useState('');
|
||||
@ -34,7 +36,7 @@ const ManageMembers = () => {
|
||||
|
||||
async function fetchCustomers() {
|
||||
try {
|
||||
let groups = await axios.get(`${BASE_URL}/customer/list`, {
|
||||
let customers = await axios.get(`${BASE_URL}/customer/list`, {
|
||||
params: {
|
||||
limit: 20,
|
||||
page: 1,
|
||||
@ -44,12 +46,22 @@ const ManageMembers = () => {
|
||||
}
|
||||
});
|
||||
let temp = 1;
|
||||
let resMembers = groups.data.data.list.map((el: any) => {
|
||||
let resMembers = customers.data.data.list.map((el: any) => {
|
||||
el.no = temp++;
|
||||
el.name = el.fullname;
|
||||
return el;
|
||||
});
|
||||
setMembers(resMembers);
|
||||
let getProfession: any = await axios.get(`${BASE_URL_MASTER_DATA}/profession/list`, {
|
||||
params: {
|
||||
limit: 50,
|
||||
page: 1,
|
||||
with_deleted: false,
|
||||
order_field: 'name',
|
||||
order_direction: 'ASC',
|
||||
}
|
||||
});
|
||||
setProfession(getProfession.data.data.list)
|
||||
} catch (error: any) {
|
||||
toast.error(error.message);
|
||||
console.log(error);
|
||||
@ -75,6 +87,7 @@ const ManageMembers = () => {
|
||||
}
|
||||
|
||||
const handleYes = async () => {
|
||||
setLoading(true)
|
||||
const userLogin: any = await getUser();
|
||||
const updateData: any = member;
|
||||
updateData.updated_by = userLogin.data ? userLogin.data.id : '';
|
||||
@ -113,18 +126,22 @@ const ManageMembers = () => {
|
||||
}
|
||||
});
|
||||
// if (dialogType === 'create') await axios.post(`${BASE_URL}/customers/create`, member)
|
||||
await fetchCustomers();
|
||||
setDialogOpen(false);
|
||||
setIsDialogOpen(false);
|
||||
toast.success('Success Update Member');
|
||||
} catch (error: any) {
|
||||
setDialogOpen(false);
|
||||
setIsDialogOpen(false);
|
||||
toast.error(error.message);
|
||||
} finally {
|
||||
setDialogOpen(false);
|
||||
closeDialog();
|
||||
await fetchCustomers();
|
||||
setLoading(false)
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <ScreenLoader />;
|
||||
function setShowAddDialog(el: any) {
|
||||
setIsDialogOpen(el)
|
||||
}
|
||||
|
||||
if (loading) return <LoaderTransparant />;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -141,14 +158,23 @@ const ManageMembers = () => {
|
||||
onYes={handleYes}
|
||||
onNo={() => setDialogOpen(false)}
|
||||
/>
|
||||
{ member.id ? (
|
||||
<CustomerDialog
|
||||
open={isDialogOpen}
|
||||
handleClose={closeDialog}
|
||||
handleSubmit={handleSubmit}
|
||||
initialData={member}
|
||||
fetchCustomers={fetchCustomers}
|
||||
/>
|
||||
{ member.id !== '' ? (
|
||||
// <CustomerDialog
|
||||
// open={isDialogOpen}
|
||||
// handleClose={closeDialog}
|
||||
// handleSubmit={handleSubmit}
|
||||
// initialData={member}
|
||||
// fetchCustomers={fetchCustomers}
|
||||
// />
|
||||
<DetailMember
|
||||
showAddDialog={isDialogOpen}
|
||||
setShowAddDialog={setShowAddDialog}
|
||||
handleClose={closeDialog}
|
||||
handleSubmit={handleSubmit}
|
||||
initialData={member}
|
||||
fetchCustomers={fetchCustomers}
|
||||
profession={profession}
|
||||
/>
|
||||
): ""}
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900 mb-3 grid gap-5 lg:gap-7.5 mx-8 w-auto">Manage Members</h1>
|
||||
<div className='grid gap-5 lg:gap-7.5 mx-8 w-auto'>
|
||||
|
||||
248
src/pages/members/manage-members/blocks/AdmAccess.tsx
Normal file
248
src/pages/members/manage-members/blocks/AdmAccess.tsx
Normal file
@ -0,0 +1,248 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import { toast } from 'sonner';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle
|
||||
} from '@/components/ui/dialog';
|
||||
import {
|
||||
Select,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectValue
|
||||
} from '@/components/ui/select';
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import ConfirmDialog from '@/components/confirm';
|
||||
const BASE_URL_CUSTOMER = apiConfig.service_customer;
|
||||
|
||||
// ACCESS ADM
|
||||
export default function AdmAccess(
|
||||
page: string,
|
||||
data: any,
|
||||
handleClose: any,
|
||||
fetchCustomers: any,
|
||||
viewOnly: any,
|
||||
setViewOnly: any
|
||||
) {
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [dialogType, setDialogType] = useState('');
|
||||
const [changeGroup, setChangeGroup] = useState('');
|
||||
const [changeGroupD, setChangeGroupD] = useState(false);
|
||||
const [groups, setGroups] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchGroups();
|
||||
}, []);
|
||||
|
||||
const fetchGroups = async () => {
|
||||
try {
|
||||
let getGroups = await axios.get(`${BASE_URL_CUSTOMER}/groups/list`, {
|
||||
params: {
|
||||
limit: 50,
|
||||
page: 1,
|
||||
with_deleted: false,
|
||||
order_field: 'name',
|
||||
order_direction: 'ASC'
|
||||
}
|
||||
});
|
||||
setGroups(getGroups.data.data.list);
|
||||
} catch (error: any) {
|
||||
toast.error(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const handleYes = async () => {
|
||||
try {
|
||||
if (dialogType === 'update status') {
|
||||
let statusNext = getPinStatus(data.status).res;
|
||||
if (statusNext)
|
||||
await axios.put(`${BASE_URL_CUSTOMER}/customer/statuspin`, {
|
||||
customerid: data.id,
|
||||
status: statusNext
|
||||
});
|
||||
else toast.error('Handle Active/Suspend only');
|
||||
await fetchCustomers();
|
||||
toast.success('Success Update Status');
|
||||
}
|
||||
if (dialogType === 'reset pin') {
|
||||
if (data.id)
|
||||
await axios.post(`${BASE_URL_CUSTOMER}/customer/resetpin`, { customerid: data.id });
|
||||
else throw { message: 'data.id not found' };
|
||||
await fetchCustomers();
|
||||
toast.success('Pin will send to customer MSISDN');
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.error(error.message);
|
||||
} finally {
|
||||
setDialogOpen(false);
|
||||
handleClose();
|
||||
}
|
||||
};
|
||||
|
||||
function buttonStatus(e: any) {
|
||||
e.preventDefault();
|
||||
setDialogType('update status');
|
||||
setDialogOpen(true);
|
||||
}
|
||||
|
||||
function buttonResetPin(e: any) {
|
||||
e.preventDefault();
|
||||
setDialogType('reset pin');
|
||||
setDialogOpen(true);
|
||||
}
|
||||
|
||||
async function buttonChangeGroup() {
|
||||
try {
|
||||
let dataObj = {
|
||||
customerid: data.id,
|
||||
destination_group: changeGroup
|
||||
};
|
||||
if (data.group_id === changeGroup)
|
||||
throw { message: `You update same group as the exist customer group` };
|
||||
if (dataObj.customerid && dataObj.destination_group) {
|
||||
await axios.post(`${BASE_URL_CUSTOMER}/customer/change-group`, dataObj);
|
||||
}
|
||||
toast.success('Success Change group');
|
||||
await fetchCustomers();
|
||||
setChangeGroupD(false);
|
||||
handleClose();
|
||||
} catch (error: any) {
|
||||
console.log(error);
|
||||
toast.error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
function openChangeGroupDialog(e: any) {
|
||||
e.preventDefault();
|
||||
setChangeGroup(data.group_id);
|
||||
setChangeGroupD(true);
|
||||
}
|
||||
|
||||
function btnConfirmDialog(status: boolean) {
|
||||
setDialogOpen(status);
|
||||
}
|
||||
|
||||
if (page !== 'kyc') {
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-md shadow-md space-y-6">
|
||||
<h2 className="text-lg font-semibold">Access Administration</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Left Side */}
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm">Pin Status: {getPinStatus(data.status).msg}</p>
|
||||
<Button variant="default" onClick={(e) => buttonStatus(e)}>
|
||||
{getPinStatus(data.status).btn}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm">Reset PIN</p>
|
||||
<Button variant="default" onClick={(e) => buttonResetPin(e)}>
|
||||
Reset PIN
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side */}
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm">Change Group</p>
|
||||
<Button variant="default" onClick={(e) => openChangeGroupDialog(e)}>
|
||||
Change Group
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm">Edit Member</p>
|
||||
<Button variant="default" onClick={() => setViewOnly(!viewOnly)}>
|
||||
{viewOnly ? 'Open Edit' : 'Close Edit'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Change Group Dialog */}
|
||||
<Dialog open={changeGroupD} onOpenChange={setChangeGroupD}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-orange-500">
|
||||
Are you sure to change customer Group?
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-3 p-5">
|
||||
<p className="text-sm font-medium">Destination Group</p>
|
||||
<Select value={changeGroup} onValueChange={setChangeGroup}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select group" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{groups?.map((el: any) => (
|
||||
<SelectItem key={el.id} value={el.id}>
|
||||
{el.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setChangeGroupD(false)}>
|
||||
No
|
||||
</Button>
|
||||
<Button onClick={buttonChangeGroup}>Yes</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<ConfirmDialog
|
||||
open={dialogOpen}
|
||||
// onClose={() => btnConfirmDialog(false)}
|
||||
onOpenChange={() => setDialogOpen(false)}
|
||||
title="Confirm Action"
|
||||
content={`Are you sure you want to ${dialogType}?`}
|
||||
onYes={handleYes}
|
||||
onNo={() => btnConfirmDialog(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function getPinStatus(status: string) {
|
||||
if (status === 'Y')
|
||||
return {
|
||||
msg: 'Active',
|
||||
btn: 'Block PIN',
|
||||
res: 'Block'
|
||||
};
|
||||
if (status === 'N')
|
||||
return {
|
||||
msg: 'Not Active',
|
||||
btn: 'Activate PIN',
|
||||
res: null
|
||||
};
|
||||
if (status === 'P')
|
||||
return {
|
||||
msg: 'Suspend PIN',
|
||||
btn: 'Unblock PIN',
|
||||
res: 'UnBlock'
|
||||
};
|
||||
if (status === 'O')
|
||||
return {
|
||||
msg: 'Suspend OTP',
|
||||
btn: 'Unblock OTP',
|
||||
res: null
|
||||
};
|
||||
return {
|
||||
msg: 'None',
|
||||
btn: 'No status found',
|
||||
res: null
|
||||
};
|
||||
}
|
||||
69
src/pages/members/manage-members/blocks/CustomerWallet.tsx
Normal file
69
src/pages/members/manage-members/blocks/CustomerWallet.tsx
Normal file
@ -0,0 +1,69 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import { toast } from 'sonner';
|
||||
const BASE_URL_CUSTOMER = apiConfig.service_customer;
|
||||
|
||||
export default function CustomerWallet(customerid: any) {
|
||||
const [customerWallet, setCustomerWallet] = useState([]);
|
||||
if (!customerid) return '';
|
||||
|
||||
useEffect(() => {
|
||||
fetchCustomerWallet();
|
||||
}, []);
|
||||
|
||||
async function fetchCustomerWallet() {
|
||||
try {
|
||||
let getCustWallet = await axios.get(`${BASE_URL_CUSTOMER}/customer/wallet`, {
|
||||
params: { customerid: customerid }
|
||||
});
|
||||
setCustomerWallet(getCustWallet.data.data.data);
|
||||
} catch (error: any) {
|
||||
// toast.error(error.message);
|
||||
toast.error(`Wallet Not Found`);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-md shadow-md space-y-4">
|
||||
<h2 className="text-lg font-semibold">Wallet Member</h2>
|
||||
<Card className="p-4">
|
||||
{customerWallet.length ? (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full text-sm text-left">
|
||||
<thead className="text-xs text-gray-500 border-b">
|
||||
<tr>
|
||||
<th className="p-2">No</th>
|
||||
<th className="p-2">Name</th>
|
||||
<th className="p-2">Balance</th>
|
||||
<th className="p-2">Month Limit</th>
|
||||
<th className="p-2">Credit Limit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{customerWallet.map((item:any, index) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
className={`${
|
||||
index % 2 === 0 ? "bg-gray-50" : "bg-white"
|
||||
} hover:bg-gray-100`}
|
||||
>
|
||||
<td className="p-2 font-medium">{index+1}</td>
|
||||
<td className="p-2">{item.wallet}</td>
|
||||
<td className="p-2">{item.amount}</td>
|
||||
<td className="p-2">{item.monthly_limit}</td>
|
||||
<td className="p-2">{item.credit_limit}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">No wallet</p>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
364
src/pages/members/manage-members/blocks/DetailMember.tsx
Normal file
364
src/pages/members/manage-members/blocks/DetailMember.tsx
Normal file
@ -0,0 +1,364 @@
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import { Alert, useDataGrid } from '@/components';
|
||||
import { useCallApi } from '@/hooks';
|
||||
import { getAuth } from '@/auth';
|
||||
import axios from 'axios';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import {
|
||||
Dialog,
|
||||
DialogBody,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle
|
||||
} from '@/components/ui/dialog';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from '@/components/ui/select';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
const BASE_URL_MASTER_DATA = apiConfig.service_master_data;
|
||||
const URL_NATIONALITY = apiConfig.nationality;
|
||||
import { initialMember } from "../Columns";
|
||||
import AdmAccess from './AdmAccess';
|
||||
import CustomerWallet from './CustomerWallet';
|
||||
|
||||
const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialData, handleReject, page, fetchCustomers,
|
||||
handleClose, profession
|
||||
}: any) => {
|
||||
const [formData, setFormData] = useState(initialData || initialMember);
|
||||
const [viewOnly, setViewOnly] = useState(false);
|
||||
const [nationality, setNationality] = useState([]);
|
||||
const [municipios, setMunicipios] = useState([]);
|
||||
const [aldeias, setAldeias] = useState([]);
|
||||
const [postoAdm, setPostoAdm] = useState([]);
|
||||
const [sucos, setSucos] = useState<any>([]);
|
||||
const [genders] = useState([ { name: 'Male',id: 'M' }, { name: 'Female',id: 'F' }])
|
||||
const [status] = useState([
|
||||
{ name: 'Active',id: 'Y' }, { name: 'Inactive',id: 'N' }, { name: 'Suspend PIN',id: 'P' }, { name: 'Suspend OTP',id: 'O' }
|
||||
])
|
||||
const [banks] = useState([
|
||||
{ name: 'BNCTL',id: 'BNCTL' }, { name: 'BRI',id: 'BRI' }, { name: 'BNU',id: 'BNU' }, { name: 'Mandiri',id: 'Mandiri' }
|
||||
])
|
||||
const parentRef = useRef<any | null>(null);
|
||||
const [alert, setAlert] = useState({
|
||||
show: false,
|
||||
message: ''
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setFormData(initialData || {});
|
||||
// fetchMasterData()
|
||||
}, [initialData]);
|
||||
|
||||
const handleChange = async (e: any) => {
|
||||
const { name, value } = e.target;
|
||||
if (name === "file_selfie" || name === "photouser" || name === 'file_document_id' || name === "file_document_id_selfie" ||
|
||||
name === "file_commercial_license") { // FOR FILE ONLY
|
||||
setFormData({ ...formData, [name]: e.target.files[0] });
|
||||
} else if(name === "nationality") {
|
||||
let getNationality = await axios.get(`${URL_NATIONALITY}/${value}`);
|
||||
setNationality(getNationality.data.data)
|
||||
setFormData({ ...formData, [name]: value });
|
||||
} else {
|
||||
setFormData({ ...formData, [name]: value });
|
||||
if (name === 'municipio' || name === 'posto_adms' || name === 'suco') await getMasterAfter(name, value);
|
||||
}
|
||||
};
|
||||
|
||||
async function getMasterAfter(name: string, id: any) {
|
||||
if (name === 'municipio') {
|
||||
let getMunicipiosPosto = await axios.get(`${BASE_URL_MASTER_DATA}/municipios/postoadms/${id}`, {
|
||||
params: {
|
||||
limit: 50,
|
||||
page: 1,
|
||||
with_deleted: false,
|
||||
order_field: 'name',
|
||||
order_direction: 'ASC',
|
||||
}
|
||||
});
|
||||
setPostoAdm(getMunicipiosPosto.data.data)
|
||||
}
|
||||
if (name === 'posto_adms') {
|
||||
let getPostoSuco = await axios.get(`${BASE_URL_MASTER_DATA}/postoadms/sucos/${id}`, {
|
||||
params: {
|
||||
limit: 50,
|
||||
page: 1,
|
||||
with_deleted: false,
|
||||
order_field: 'name',
|
||||
order_direction: 'ASC',
|
||||
}
|
||||
});
|
||||
setSucos(getPostoSuco.data.data)
|
||||
}
|
||||
if (name === 'suco') {
|
||||
let getSucoAldeias = await axios.get(`${BASE_URL_MASTER_DATA}/sucos/aldeias/${id}`, {
|
||||
params: {
|
||||
limit: 50,
|
||||
page: 1,
|
||||
with_deleted: false,
|
||||
order_field: 'name',
|
||||
order_direction: 'ASC',
|
||||
}
|
||||
});
|
||||
setAldeias(getSucoAldeias.data.data)
|
||||
}
|
||||
}
|
||||
|
||||
const handleAddDialog = (show:boolean) => {
|
||||
if (show) {
|
||||
setShowAddDialog(show)
|
||||
} else {
|
||||
handleClose()
|
||||
setShowAddDialog(show)
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchMasterData() {
|
||||
try {
|
||||
let getMunicipios = await axios.get(`${BASE_URL_MASTER_DATA}/municipios/list`, {
|
||||
params: {
|
||||
limit: 50,
|
||||
page: 1,
|
||||
with_deleted: false,
|
||||
order_field: 'name',
|
||||
order_direction: 'ASC',
|
||||
}
|
||||
});
|
||||
setMunicipios(getMunicipios.data.data.list)
|
||||
let getPostoAdms = await axios.get(`${BASE_URL_MASTER_DATA}/postoadms/list`, {
|
||||
params: {
|
||||
limit: 50,
|
||||
page: 1,
|
||||
with_deleted: false,
|
||||
order_field: 'name',
|
||||
order_direction: 'ASC',
|
||||
}
|
||||
});
|
||||
setPostoAdm(getPostoAdms.data.data.list)
|
||||
let getSucos = await axios.get(`${BASE_URL_MASTER_DATA}/sucos/list`, {
|
||||
params: {
|
||||
limit: 50,
|
||||
page: 1,
|
||||
with_deleted: false,
|
||||
order_field: 'name',
|
||||
order_direction: 'ASC',
|
||||
}
|
||||
});
|
||||
setSucos(getSucos.data.data.list)
|
||||
let getAldeias = await axios.get(`${BASE_URL_MASTER_DATA}/aldeias/list`, {
|
||||
params: {
|
||||
limit: 50,
|
||||
page: 1,
|
||||
with_deleted: false,
|
||||
order_field: 'name',
|
||||
order_direction: 'ASC',
|
||||
}
|
||||
});
|
||||
setAldeias(getAldeias.data.data.list)
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
function buttonOnSubmit(e:any) {
|
||||
e.preventDefault();
|
||||
if (page === 'kyc' && !formData.description) return toast.warning(`Description for approval needed!`)
|
||||
handleSubmit(formData);
|
||||
}
|
||||
|
||||
function btnPrevDef(e:any) {
|
||||
e.preventDefault();
|
||||
viewOnly ? setViewOnly(false) : setViewOnly(true)
|
||||
}
|
||||
|
||||
const onReject = () => {
|
||||
handleReject(formData);
|
||||
handleClose();
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={showAddDialog} onOpenChange={(open) => handleAddDialog(open)}>
|
||||
<DialogContent className="container-fixed max-w-[768px] flex flex-col p-5">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Member - View/Edit</DialogTitle>
|
||||
<DialogDescription></DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogBody ref={parentRef}>
|
||||
<div className="flex flex-col">
|
||||
{alert.show && (
|
||||
<Alert variant="danger">
|
||||
<h3>{alert.message}</h3>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* <form> */}
|
||||
{/* onSubmit={(e) => buttonOnSubmit(e, formData)} */}
|
||||
<div className="card-body grid gap-5">
|
||||
|
||||
{formData.id ? generateInput(formData, handleChange, 'Group', 'group_name', 'text', true, true): ''}
|
||||
{formData.id ? generateInput(formData, handleChange, 'Username', 'username', 'text', true, true): ''}
|
||||
{formData.id ? generateInput(formData, handleChange, 'Full Name', 'fullname', 'text', true, viewOnly): ''}
|
||||
{formData.id ? generateImage(formData, handleChange, 'Photo', 'photouser'): ''}
|
||||
{formData.id ? generateList(formData, handleChange, genders, 'gender', 'Gender', null, true): ''}
|
||||
{formData.id ? generateInput(formData, handleChange, 'Date of Birth', 'date_birth', 'date', true, viewOnly): ''}
|
||||
{formData.id ? generateInput(formData, handleChange, 'Phone Number', 'msisdn', 'text', true, viewOnly): ''}
|
||||
{formData.id ? generateInput(formData, handleChange, 'Email', 'email', 'text', true, viewOnly): ''}
|
||||
{/* {generateList(formData, handleChange, status, 'status', 'Status', null, true)} */}
|
||||
{formData.id ? generateList(formData, handleChange, profession, 'profession', 'Profession', null, false): ''}
|
||||
{formData.id ? generateList(formData, handleChange, municipios, 'municipio', 'Municipio', null, false): ''}
|
||||
{formData.id ? generateList(formData, handleChange, postoAdm, 'posto_adms', 'Posto', 'posto_adms_id', false): ''}
|
||||
{formData.id ? generateList(formData, handleChange, sucos, 'suco', 'Suco', 'sucos_id', false): ''}
|
||||
{formData.id ? generateList(formData, handleChange, aldeias, 'aldeia', 'Aldeia', null, false): ''}
|
||||
{formData.id ? generateInput(formData, handleChange, 'Nationality', 'nationality', 'text', true, viewOnly): ''}
|
||||
<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>
|
||||
<ul className="">
|
||||
{nationality.map((item: any, index) => (
|
||||
<li key={index} value={item.name}
|
||||
onClick={() => handleChange({target:{name: 'nationality', value: item.name }})}
|
||||
className="bg-gray-100 px-4 py-2 rounded hover:bg-gray-200 cursor-default transition">
|
||||
{item.name}
|
||||
</li>
|
||||
))}
|
||||
{formData.nationality && nationality.length === 0 && (
|
||||
<li className="text-gray-500">No results found.</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
{formData.id ? generateInput(formData, handleChange, 'Address', 'address', 'text', true, viewOnly): ''}
|
||||
{formData.id ? generateInput(formData, handleChange, 'Mother Fullname', 'mother_fullname', 'text', true, viewOnly): ''}
|
||||
{formData.id ? generateInput(formData, handleChange, 'Identity Number', 'identity_number', 'text', false, viewOnly): ''}
|
||||
|
||||
{formData.id ? generateInput(formData, handleChange, 'License Number', 'license_number', 'text', false, viewOnly): ''}
|
||||
{formData.id ? generateInput(formData, handleChange, 'Merchant Name', 'agent_name', 'text', false, viewOnly): ''}
|
||||
{formData.id ? generateInput(formData, handleChange, 'Merchant Address', 'merchantaddress', 'text', false, viewOnly): ''}
|
||||
{formData.id ? generateInput(formData, handleChange, 'Longitude Merchant', 'longitudemerchant', 'text', false, viewOnly): ''}
|
||||
{formData.id ? generateInput(formData, handleChange, 'Latitude Merchant', 'latitudemerchant', 'text', false, viewOnly): ''}
|
||||
{formData.id ? generateImage(formData, handleChange, 'File Selfie', 'file_selfie'): ''}
|
||||
{formData.id ? generateImage(formData, handleChange, 'File Document', 'file_document_id'): ''}
|
||||
{formData.id ? generateImage(formData, handleChange, 'File Document & Selfie', 'file_document_id_selfie'): ''}
|
||||
{formData.id ? generateImage(formData, handleChange, 'File Commercial License', 'file_commercial_license'): ''}
|
||||
|
||||
{formData.id ? generateList(formData, handleChange, banks, 'bank_name', 'Bank Name', null, false): ''}
|
||||
{formData.id ? generateInput(formData, handleChange, 'Bank Account', 'bank_account', 'text', false, viewOnly): ''}
|
||||
{formData.id ? generateInput(formData, handleChange, 'iBank Number', 'ibank_number', 'text', false, viewOnly): ''}
|
||||
|
||||
{formData.id ? generateInput(formData, handleChange, 'Approval Description', 'description', 'text', false, viewOnly): ''}
|
||||
|
||||
{(formData.id && page!=='kyc') ? AdmAccess(page, formData, handleClose,fetchCustomers, viewOnly, setViewOnly): ""}
|
||||
{(formData.id && page!=='kyc') ? CustomerWallet(formData.id) : ""}
|
||||
|
||||
<div className="flex justify-end gap-5">
|
||||
<Button onClick={(e:any) => btnPrevDef(e)} color="secondary">{ viewOnly ? (`Open Edit`) : (`Close Edit`) }</Button>
|
||||
<Button type="button" variant="outline" onClick={() => handleAddDialog(false)}>Cancel</Button>
|
||||
{/* <Button onClick={(e) => buttonOnSubmit(e, formData)} variant="default">Save Changes</Button> */}
|
||||
{
|
||||
formData.isneedapproval == 1 && page === 'kyc' ? (
|
||||
<Button onClick={onReject} variant="destructive" color="warning">Reject</Button>
|
||||
) : ('')
|
||||
}
|
||||
<Button onClick={(e) => buttonOnSubmit(e)} color="primary" variant="default">
|
||||
{ formData.id ? (formData.isneedapproval == 1 && page === 'kyc' ? (`Edit & Approve`) : (`Edit`)) : ("Create") }
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/* </form> */}
|
||||
</div>
|
||||
</DialogBody>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default DetailMember;
|
||||
{/* <span className="text-red-500 text-[10px] lowercase align-middle ml-1">(agent)</span> */}
|
||||
|
||||
function generateInput(formData:any, handleChange:any, label:string, name:string, type: string, required: boolean, disabled: boolean) {
|
||||
function generateDate(isoString: string) {
|
||||
const date = new Date(isoString);
|
||||
return date.toISOString().slice(0, 10); // "2000-01-18"
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="w-full">
|
||||
<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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// ON DEV (DI SELECT MASI HILANG)
|
||||
function generateList(formData:any, handleChange: any, list:any, name:string, label: string, difId: any, required: boolean) {
|
||||
return (
|
||||
<>
|
||||
<div className="w-full">
|
||||
<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-600">{required?"*":""}</span></label>
|
||||
<Select required={required} value={formData[name]} onValueChange={(e) => (handleChange({ target : { name, value: e }}))}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={`Select ${label}`} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{list.map((el: any, idx: any) => (
|
||||
<SelectItem key={idx} value={el.id}>{el.name}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// ON DEV (UPDATENYA)
|
||||
function generateImage(formData:any, handleChange:any, label:string, name:string) {
|
||||
const imagePreview = (file:any) => {
|
||||
if (file && file.type && file.type.startsWith('image/')) {
|
||||
const previewURL = URL.createObjectURL(file);
|
||||
return previewURL
|
||||
}
|
||||
return file
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between w-full max-w-xl">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
{label}<span className="text-red-500"></span>
|
||||
</label>
|
||||
<div className="flex items-center space-x-4 w-3/4 justify-end">
|
||||
{ formData[name] ? (
|
||||
<div className="border-2 border-dashed border-red-300 rounded-md p-2">
|
||||
<img width={300} height={250} srcSet={imagePreview(formData[name])} src={imagePreview(formData[name])} alt={name} style={{borderRadius: 10}}/>
|
||||
{/* <img width={300} height={250} srcSet={formData[name]} src={formData[name]} alt={name} style={{borderRadius: 10}}/> */}
|
||||
</div>
|
||||
) : (<span>No Data</span>)}
|
||||
<label className="bg-red-600 text-white px-2 py-1 rounded-full flex items-center cursor-pointer hover:bg-red-800">
|
||||
<span style={{fontSize: 13}}>Upload</span>
|
||||
<input type="file" className="hidden" name={name} onChange={handleChange} accept="image/*" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user