433 lines
23 KiB
TypeScript
433 lines
23 KiB
TypeScript
import { apiConfig } from '@/config/api.config';
|
|
import { Alert, useDataGrid } from '@/components';
|
|
import axios from 'axios';
|
|
import { useAuthContext } from '@/auth';
|
|
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';
|
|
import { initialMember } from "../Columns";
|
|
import AdmAccess from './AdmAccess';
|
|
import CustomerWallet from './CustomerWallet';
|
|
const BASE_URL_MASTER_DATA = apiConfig.service_master_data;
|
|
const URL_NATIONALITY = apiConfig.nationality;
|
|
const BASE_URL_CUSTOMER = apiConfig.service_customer;
|
|
|
|
const DetailMember = ({ showAddDialog, setShowAddDialog, handleSubmit, initialData, handleReject, page, fetchCustomers,
|
|
handleClose, profession, dialogType
|
|
}: 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 [groups, setGroups] = useState([]);
|
|
const [isAdmin, setisAdmin] = useState(false);
|
|
const [identity_type] = useState([
|
|
{ id: 'Eleitoral ID', name: 'Eleitoral ID' },
|
|
{ id: 'Bihete de Identidade', name: 'Bihete de Identidade' },
|
|
{ id: 'Passport', name: 'Passport' },
|
|
]);
|
|
const [genders] = useState([ { name: 'Male',id: 'M' }, { name: 'Female',id: 'F' }])
|
|
const [previewImg, setPreviewImg] = useState({
|
|
status: false,
|
|
img: ''
|
|
});
|
|
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: ''
|
|
});
|
|
const { getUser } = useAuthContext();
|
|
|
|
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"|| name === 'photomerchant') { // FOR FILE ONLY
|
|
setFormData({ ...formData, [name]: e.target.files[0] });
|
|
} else if(name === "nationality") {
|
|
setFormData({ ...formData, [name]: value });
|
|
let getNationality = await axios.get(`${URL_NATIONALITY}/${value}`);
|
|
setNationality(getNationality.data.data)
|
|
} else {
|
|
if (name === 'municipio_id' || name === 'posto_adms_id' || name === 'suco_id') await getMasterAfter(name, value);
|
|
if (name==='msisdn') setFormData({ ...formData, [name]: value.replace(/\D/g, '') })
|
|
else setFormData({ ...formData, [name]: value });
|
|
}
|
|
};
|
|
|
|
async function getMasterAfter(name: string, id: any) {
|
|
if (name === 'municipio_id') {
|
|
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_id') {
|
|
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_id') {
|
|
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 userlogin:any = await getUser();
|
|
let userloginrole = userlogin?.data?.role?.name;
|
|
if (userloginrole === 'Admin') {
|
|
setisAdmin(true);
|
|
setViewOnly(true)
|
|
}
|
|
|
|
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);
|
|
|
|
let getMunicipios = await axios.get(`${BASE_URL_MASTER_DATA}/municipios/list`, {
|
|
params: {
|
|
limit: 70,
|
|
page: 1,
|
|
with_deleted: false,
|
|
order_field: 'name',
|
|
order_direction: 'ASC',
|
|
}
|
|
});
|
|
setMunicipios(getMunicipios.data.data.list)
|
|
if (formData.municipio_id) await handleChange({target: { name: "municipio_id",value: formData.municipio_id }});
|
|
if (formData.posto_adms_id) await handleChange({target: { name: "posto_adms_id",value: formData.posto_adms_id }});
|
|
if (formData.suco_id) await handleChange({target: { name: "suco_id",value: formData.suco_id }});
|
|
if (formData.aldeia_id) await handleChange({target: { name: "aldeia_id",value: formData.aldeia_id }});
|
|
} catch (error:any) {
|
|
console.log(error);
|
|
toast.error(error.message)
|
|
}
|
|
}
|
|
|
|
function buttonOnSubmit(e:any) {
|
|
e.preventDefault();
|
|
const requiredFields:any = {
|
|
msisdn: "msisdn",
|
|
email: "email",
|
|
fullname: "fullname",
|
|
username: "username",
|
|
address: "Address",
|
|
nationality: "Nationality",
|
|
date_birth: "Date Of Birth",
|
|
gender: "Gender"
|
|
};
|
|
|
|
const missingFields = Object.keys(requiredFields).filter((key) => !formData[key]);
|
|
|
|
if (missingFields.length > 0) {
|
|
const missingNames = missingFields.map((key:any) => requiredFields[key]).join(", ");
|
|
return toast.warning(`Required fields cannot be empty: ${missingNames}!`);
|
|
}
|
|
// dialogType === 'update'
|
|
handleSubmit(formData);
|
|
}
|
|
|
|
function btnPrevDef(e:any) {
|
|
e.preventDefault();
|
|
viewOnly ? setViewOnly(false) : setViewOnly(true)
|
|
}
|
|
|
|
const onReject = () => {
|
|
if (formData.destinationGroup === "Premium" && !formData.approval_description_premium) return toast.warning(`Approval Premium Description needed!`)
|
|
if (formData.destinationGroup === "Agent" && !formData.approval_description_premium) return toast.warning(`Approval Agent Description needed!`)
|
|
handleReject(formData);
|
|
// handleClose();
|
|
}
|
|
|
|
return (
|
|
<Dialog open={showAddDialog} onOpenChange={(open) => handleAddDialog(open)}>
|
|
<DialogContent className="container-fixed max-w-[85%] flex flex-col p-2">
|
|
<DialogHeader>
|
|
<DialogTitle>Member - {dialogType ==='create' ? "Create" : "View/Edit"}</DialogTitle>
|
|
<DialogDescription></DialogDescription>
|
|
</DialogHeader>
|
|
<DialogBody ref={parentRef}>
|
|
<div className="flex flex-col">
|
|
<form>
|
|
{alert.show && (<Alert variant="danger"><h3>{alert.message}</h3></Alert>)}
|
|
|
|
{/* <div className="card-body grid gap-5"> */}
|
|
<div className="card-body grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
|
|
{(formData.id || dialogType === "create") ? generateList(formData, handleChange, groups, 'group_id', 'Group', true, dialogType === "update"||false): ''}
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Username', 'username', 'text', true, viewOnly): ''}
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Full Name', 'fullname', 'text', true, viewOnly): ''}
|
|
{(formData.id || dialogType === "create") ? generateImage(formData, handleChange, 'Photo', 'photouser', previewImg, setPreviewImg): ''}
|
|
{(formData.id || dialogType === "create") ? generateList(formData, handleChange, genders, 'gender', 'Gender', true, false): ''}
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Date of Birth', 'date_birth', 'date', true, viewOnly): ''}
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Phone Number', 'msisdn', 'text', true, (dialogType === "create"||formData.group_name === 'Agent')?false:true): ''}
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Email', 'email', 'text', true, viewOnly): ''}
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Mother Fullname', 'mother_fullname', 'text', false, viewOnly): ''}
|
|
{(formData.id || dialogType === "create") ? generateList(formData, handleChange, identity_type, 'identity_type', 'Identity Type', false, viewOnly): ''}
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Address', 'address', 'text', true, viewOnly): ''}
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Identity Number', 'identity_number', 'text', false, viewOnly): ''}
|
|
<div className="bg-white space-y-6">
|
|
{(formData.id || dialogType === "create") ? generateList(formData, handleChange, profession, 'profession', 'Profession', false, false): ''}
|
|
{(formData.id || dialogType === "create") ? generateList(formData, handleChange, municipios, 'municipio_id', 'Municipio', false, false): ''}
|
|
{(formData.id || dialogType === "create") ? generateList(formData, handleChange, postoAdm, 'posto_adms_id', 'Posto', false, false): ''}
|
|
{(formData.id || dialogType === "create") ? generateList(formData, handleChange, sucos, 'suco_id', 'Suco', false, false): ''}
|
|
{(formData.id || dialogType === "create") ? generateList(formData, handleChange, aldeias, 'aldeia_id', 'Aldeia', false, false): ''}
|
|
{(formData.id || dialogType === "create") ? 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>
|
|
<div className="bg-white p-6 rounded-md shadow-md space-y-6">
|
|
<h2 className="text-lg font-semibold">Bank Information</h2>
|
|
{(formData.id || dialogType === "create") ? generateList(formData, handleChange, banks, 'bank_name', 'Bank Name', false, false): ''}
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Bank Account', 'bank_account', 'text', false, viewOnly): ''}
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'iBank Number', 'ibank_number', 'text', false, viewOnly): ''}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-white p-6 rounded-md shadow-md space-y-6">
|
|
<h2 className="text-lg font-semibold">Agent Information</h2>
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'License Number', 'license_number', 'text', false, viewOnly): ''}
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Merchant Name', 'agent_name', 'text', false, viewOnly): ''}
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Merchant Address', 'merchantaddress', 'text', false, viewOnly): ''}
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Longitude Merchant', 'longitudemerchant', 'text', false, viewOnly): ''}
|
|
{(formData.id || dialogType === "create") ? generateInput(formData, handleChange, 'Latitude Merchant', 'latitudemerchant', 'text', false, viewOnly): ''}
|
|
{(formData.id || dialogType === "create") ? generateImage(formData, handleChange, 'Photo Merchant', 'photomerchant', previewImg, setPreviewImg): ''}
|
|
{(formData.id || dialogType === "create") ? generateImage(formData, handleChange, 'File Selfie', 'file_selfie', previewImg, setPreviewImg): ''}
|
|
{(formData.id || dialogType === "create") ? generateImage(formData, handleChange, 'File Document', 'file_document_id', previewImg, setPreviewImg): ''}
|
|
{(formData.id || dialogType === "create") ? generateImage(formData, handleChange, 'File Document & Selfie', 'file_document_id_selfie', previewImg, setPreviewImg): ''}
|
|
{(formData.id || dialogType === "create") ? generateImage(formData, handleChange, 'File Commercial License', 'file_commercial_license', previewImg, setPreviewImg): ''}
|
|
</div>
|
|
|
|
{(formData.id&&(page==='kyc'&&formData.destinationGroup === "Premium")) ? generateInput(formData, handleChange, 'Approval Premium Description', 'approval_description_premium', 'text', false, viewOnly): ''}
|
|
{(formData.id&&(page==='kyc'&&formData.destinationGroup === "Agent")) ? generateInput(formData, handleChange, 'Approval Agent Description', 'approval_description_agent', 'text', false, viewOnly): ''}
|
|
|
|
{(formData.id && page!=='kyc') ? (
|
|
<AdmAccess isAdmin={isAdmin} page={page}groups={groups}formData={formData}fetchCustomers={fetchCustomers}handleClose={handleClose}viewOnly={viewOnly}setViewOnly={setViewOnly}/>
|
|
): ""}
|
|
{(formData.id && page!=='kyc') ? (
|
|
<CustomerWallet customerid={formData.id}/>
|
|
) : ""}
|
|
|
|
<div className="col-span-1 md:col-span-2 flex justify-end gap-5">
|
|
<Button type="button" disabled={isAdmin} 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 type="button" onClick={onReject} variant="destructive" color="warning">Reject</Button>
|
|
) : ('')
|
|
}
|
|
<Button type="submit" onClick={(e) => buttonOnSubmit(e)} color="primary" variant="default" disabled={isAdmin}>
|
|
{ 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) {
|
|
return isoString.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>
|
|
</>
|
|
)
|
|
}
|
|
|
|
function generateList(formData:any, handleChange: any, list:any, name:string, label: string, required: boolean, disabled: 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 disabled={disabled} 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>
|
|
</>
|
|
)
|
|
}
|
|
|
|
function generateImage(formData:any, handleChange:any, label:string, name:string, previewImg: any, setPreviewImg:any) {
|
|
const imagePreview = (file:any) => {
|
|
if (file && file.type && file.type.startsWith('image/')) {
|
|
const previewURL = URL.createObjectURL(file);
|
|
return previewURL
|
|
}
|
|
return file
|
|
};
|
|
|
|
function handlePrevImg(par: boolean) {
|
|
if (par) setPreviewImg({ status: true, img: ''})
|
|
if (!par) setPreviewImg({ status: false, img: '' })
|
|
}
|
|
|
|
function onClickImg(img: any) {
|
|
setPreviewImg({ status: true, img })
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{previewImg.status ? (
|
|
<PreviewImage previewImgDialog={previewImg.status} handlePreviewImgDialog={handlePrevImg}
|
|
modalImage={previewImg.img}/>
|
|
):''}
|
|
<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, cursor: 'pointer' }} onClick={() => onClickImg(imagePreview(formData[name]))}/>
|
|
{/* <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>
|
|
</>
|
|
)
|
|
}
|
|
|
|
function PreviewImage({previewImgDialog, handlePreviewImgDialog, modalImage}:any) {
|
|
return (
|
|
<>
|
|
<Dialog open={previewImgDialog} onOpenChange={(open) => handlePreviewImgDialog(open)}>
|
|
<DialogContent className="">
|
|
<DialogTitle aria-describedby={''}></DialogTitle>
|
|
<DialogBody>
|
|
<button
|
|
onClick={() => handlePreviewImgDialog(false)}
|
|
className="absolute top-2 right-2 text-white bg-red-600 hover:bg-red-800 rounded-full px-3 py-1 text-xl font-bold"
|
|
>
|
|
×
|
|
</button>
|
|
<img
|
|
src={modalImage}
|
|
alt="Preview"
|
|
className="max-w-full max-h-[80vh] rounded-lg"
|
|
/>
|
|
</DialogBody>
|
|
|
|
</DialogContent>
|
|
</Dialog>
|
|
</>
|
|
)
|
|
}
|