Compare commits

..

21 Commits

Author SHA1 Message Date
victorAnumudu f52e06af65 employer uid added to request payload 2024-06-15 17:27:59 +01:00
ameye 697a467780 Merge branch 'employer-verify-update' of DigiFi/digifi-bko into master 2024-06-13 20:10:19 +00:00
victorAnumudu 9499422c9a employer verify application uid updated 2024-06-13 21:08:33 +01:00
ameye 148659b453 Merge branch 'edit-loan-modal' of DigiFi/digifi-bko into master 2024-06-13 18:58:52 +00:00
victorAnumudu 98454e2c80 edit loan modal added, employer verify api added 2024-06-13 19:56:17 +01:00
ameye 8a96b10e4b Merge branch 'ready-list-employer-column' of DigiFi/digifi-bko into master 2024-06-13 16:53:26 +00:00
ameye d17ca4f988 Merge branch 'edit-signatory-popout' of DigiFi/digifi-bko into master 2024-06-13 16:53:11 +00:00
victorAnumudu ea90bd6fc5 added employer column in ready table 2024-06-13 17:33:47 +01:00
victorAnumudu 4f99dacf76 edit signatory popout header changed 2024-06-11 09:19:38 +01:00
ameye c970467f16 Merge branch 'sessionTimeout' of DigiFi/digifi-bko into master 2024-06-10 16:48:26 +00:00
Elias 63090f0b74 minor changes 2024-06-07 19:04:20 +01:00
Elias afa81eacd8 session timeout after 7 minutes of inactivity 2024-06-07 17:59:03 +01:00
ameye eea90187e1 Merge branch 'add-signatory-modal' of DigiFi/digifi-bko into master 2024-06-07 13:17:04 +00:00
victorAnumudu 835cb6b074 add signatory API added 2024-06-07 14:12:37 +01:00
victorAnumudu a44c898a66 added modal 2024-06-07 10:10:52 +01:00
ameye 6f616c34cc Merge branch 'add-employer-form' of DigiFi/digifi-bko into master 2024-06-06 21:27:31 +00:00
victorAnumudu dc5aa92085 add employer API added 2024-06-06 22:19:45 +01:00
victorAnumudu 58875df9be Merge master into add-employer-form 2024-06-06 21:38:36 +01:00
victorAnumudu d8c9b59cde validated inputs to be submitted 2024-06-06 21:37:36 +01:00
victorAnumudu d53ad76e8b initial commit 2024-06-06 21:04:36 +01:00
ameye 2688e224fd Merge branch 'sidebar-menu-update' of DigiFi/digifi-bko into master 2024-06-06 17:19:24 +00:00
35 changed files with 1622 additions and 338 deletions
@@ -1,5 +1,10 @@
const QUERIES = { const QUERIES = {
USERS_LIST: 'users-list', USERS_LIST: 'users-list',
STARTED_LIST: 'started-list',
READY_LIST: 'ready-list',
PENDING_LIST: 'pending-list',
APPROVED_LIST: 'approved-list',
REJECTED_LIST: 'rejected-list',
EMPLOYERS_LIST: 'employers-list', EMPLOYERS_LIST: 'employers-list',
SIGNATORY_LIST: 'signatory-list', SIGNATORY_LIST: 'signatory-list',
} }
@@ -1,4 +1,4 @@
import { FC } from 'react'; import { FC, useEffect } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useAuth } from '../../../../app/modules/auth'; import { useAuth } from '../../../../app/modules/auth';
import { Languages } from './Languages'; import { Languages } from './Languages';
@@ -6,6 +6,42 @@ import { toAbsoluteUrl } from '../../../helpers';
const HeaderUserMenu: FC = () => { const HeaderUserMenu: FC = () => {
const { currentUser, logout } = useAuth(); const { currentUser, logout } = useAuth();
// Listen for user activity and trigger logout
useEffect(() => {
let timeout: number;
const inactiveTime: number = 7 * 60 * 1000; //default inactive period (milliseconds)
// Logout user after inactiveTime minutes of inactivity
const resetTimeout = () => {
clearTimeout(timeout);
// Set logout timeout
timeout = window.setTimeout(() => {
logout();
}, inactiveTime);
};
const handleUserActivity: any = () => {
resetTimeout(); // reset session on user activity
};
document.addEventListener('mousemove', handleUserActivity);
document.addEventListener('keydown', handleUserActivity);
document.addEventListener('click', handleUserActivity);
document.addEventListener('focus', handleUserActivity);
// Initialize timeout
resetTimeout();
// Remove event listeners on unmount
return () => {
clearTimeout(timeout);
document.removeEventListener('mousemove', handleUserActivity);
document.removeEventListener('keydown', handleUserActivity);
document.removeEventListener('click', handleUserActivity);
document.removeEventListener('focus', handleUserActivity);
};
}, [logout]);
return ( return (
<div <div
className="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-600 menu-state-bg menu-state-primary fw-bold py-4 fs-6 w-275px" className="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-600 menu-state-bg menu-state-primary fw-bold py-4 fs-6 w-275px"
+7 -4
View File
@@ -5,6 +5,7 @@ import {LayoutProvider, LayoutSplashScreen} from '../_digifi/layout/core'
import {MasterInit} from '../_digifi/layout/MasterInit' import {MasterInit} from '../_digifi/layout/MasterInit'
import {AuthInit} from './modules/auth' import {AuthInit} from './modules/auth'
import {ThemeModeProvider} from '../_digifi/partials' import {ThemeModeProvider} from '../_digifi/partials'
import { CustomModalProvider } from '../context/CustomModal'
const App = () => { const App = () => {
return ( return (
@@ -12,10 +13,12 @@ const App = () => {
<I18nProvider> <I18nProvider>
<LayoutProvider> <LayoutProvider>
<ThemeModeProvider> <ThemeModeProvider>
<AuthInit> <CustomModalProvider>
<Outlet /> <AuthInit>
<MasterInit /> <Outlet />
</AuthInit> <MasterInit />
</AuthInit>
</CustomModalProvider>
</ThemeModeProvider> </ThemeModeProvider>
</LayoutProvider> </LayoutProvider>
</I18nProvider> </I18nProvider>
@@ -10,7 +10,7 @@ import { Content } from '../../../../../_digifi/layout/components/content'
const UsersList = () => { const UsersList = () => {
const response = useAllResponse() const response = useAllResponse()
console.log('RESPONSE', response) // console.log('RESPONSE', response)
const {itemIdForUpdate} = useListView() const {itemIdForUpdate} = useListView()
return ( return (
<> <>
@@ -4,16 +4,16 @@ export type User = {
name?: string name?: string
avatar?: string avatar?: string
// email?: string // email?: string
position?: string // position?: string
role?: string // role?: string
last_login?: string // last_login?: string
two_steps?: boolean // two_steps?: boolean
joined_day?: string // joined_day?: string
online?: boolean // online?: boolean
initials?: { // initials?: {
label: string // label: string
state: string // state: string
} // }
uid?: string uid?: string
added?: string added?: string
updated?: string updated?: string
@@ -27,8 +27,8 @@ export type UsersQueryResponse = Response<Array<User>>
export const initialUser: User = { export const initialUser: User = {
avatar: 'avatars/300-6.jpg', avatar: 'avatars/300-6.jpg',
position: 'Art Director', // position: 'Art Director',
role: 'Administrator', // role: 'Administrator',
name: '', name: '',
email: '', email: '',
} }
@@ -56,12 +56,12 @@ const UserActionsCell: FC<Props> = ({id}) => {
</div> </div>
{/* end::Menu item */} {/* end::Menu item */}
{/* begin::Menu item */} {/* begin::Menu item */}
<div className='menu-item px-3'> {/* <div className='menu-item px-3'>
<a className='menu-link px-3' onClick={openEditModal}> <a className='menu-link px-3' onClick={openEditModal}>
Add Signatory Add Signatory
</a> </a>
</div> </div> */}
{/* end::Menu item */} {/* end::Menu item */}
{/* begin::Menu item */} {/* begin::Menu item */}
@@ -7,7 +7,7 @@ const UserEditModalHeader = () => {
return ( return (
<div className='modal-header'> <div className='modal-header'>
{/* begin::Modal title */} {/* begin::Modal title */}
<h2 className='fw-bolder'>Add User</h2> <h2 className='fw-bolder'>Edit Signatory</h2>
{/* end::Modal title */} {/* end::Modal title */}
{/* begin::Close */} {/* begin::Close */}
@@ -1,17 +1,19 @@
import {ListViewProvider, useListView} from './core/ListViewProvider' import {ListViewProvider, useListView} from './core/ListViewProvider'
import {QueryRequestProvider} from './core/QueryRequestProvider' import {QueryRequestProvider} from './core/QueryRequestProvider'
import {QueryResponseProvider, useAllResponse} from './core/QueryResponseProvider' import {QueryResponseProvider} from './core/QueryResponseProvider'
import {UsersListHeader} from './components/header/UsersListHeader' import {UsersListHeader} from './components/header/UsersListHeader'
import {UsersTable} from './table/UsersTable' import {UsersTable} from './table/UsersTable'
import {UserEditModal} from './user-edit-modal/UserEditModal' import {UserEditModal} from './user-edit-modal/UserEditModal'
import {KTCard} from '../../../../../_digifi/helpers' import {KTCard} from '../../../../../_digifi/helpers'
import { ToolbarWrapper } from '../../../../../_digifi/layout/components/toolbar' import { ToolbarWrapper } from '../../../../../_digifi/layout/components/toolbar'
import { Content } from '../../../../../_digifi/layout/components/content' import { Content } from '../../../../../_digifi/layout/components/content'
import { AddSignatoryModal } from './add-signatory-modal/Modal'
import { useCustomModal } from '../../../../../context/CustomModal'
const UsersList = () => { const UsersList = () => {
const response = useAllResponse()
console.log('RESPONSE', response)
const {itemIdForUpdate} = useListView() const {itemIdForUpdate} = useListView()
const {MODALNAMES, showCustomModal} = useCustomModal()
return ( return (
<> <>
<KTCard> <KTCard>
@@ -19,6 +21,7 @@ const UsersList = () => {
<UsersTable /> <UsersTable />
</KTCard> </KTCard>
{itemIdForUpdate !== undefined && <UserEditModal />} {itemIdForUpdate !== undefined && <UserEditModal />}
{(showCustomModal && showCustomModal.name == MODALNAMES.addSignatory) && <AddSignatoryModal />}
</> </>
) )
} }
@@ -0,0 +1,46 @@
import {useEffect} from 'react'
import { ModalHeader } from './ModalHeader'
import { ModalFormWrapper } from './ModalFormWrapper'
const AddSignatoryModal = () => {
useEffect(() => {
document.body.classList.add('modal-open')
return () => {
document.body.classList.remove('modal-open')
}
}, [])
return (
<>
<div
className='modal fade show d-block'
id='kt_modal_add_user'
role='dialog'
tabIndex={-1}
aria-modal='true'
>
{/* begin::Modal dialog */}
<div className='modal-dialog modal-dialog-centered mw-650px'>
{/* begin::Modal content */}
<div className='modal-content'>
<ModalHeader />
{/* begin::Modal body */}
<div className='modal-body scroll-y mx-5 mx-xl-15 my-7'>
<ModalFormWrapper />
</div>
{/* end::Modal body */}
</div>
{/* end::Modal content */}
</div>
{/* end::Modal dialog */}
</div>
{/* begin::Modal Backdrop */}
<div className='modal-backdrop fade show'></div>
{/* end::Modal Backdrop */}
</>
)
}
export {AddSignatoryModal}
@@ -0,0 +1,332 @@
import { FC, useState } from "react";
import * as Yup from "yup";
import { useFormik } from "formik";
import { isNotEmpty, toAbsoluteUrl } from "../../../../../../_digifi/helpers";
// import { initialUser, User } from "../core/_models";
import { initialUser, User } from "../../signatory-list/core/_models";
import clsx from "clsx";
import { useListView } from "../core/ListViewProvider";
import { UsersListLoading } from "../components/loading/UsersListLoading";
import { createSignatory, updateUser } from "../core/_requests";
import { useQueryResponse } from "../core/QueryResponseProvider";
import { useCustomModal } from "../../../../../../context/CustomModal";
type Props = {
isUserLoading: boolean;
user: User;
};
const editUserSchema = Yup.object().shape({
email: Yup.string()
.email('Wrong email format')
.min(3, 'Minimum 3 symbols')
.max(50, 'Maximum 50 symbols')
.required('required'),
name: Yup.string()
.min(3, "Minimum 3 symbols")
.max(50, "Maximum 50 symbols")
.required("required"),
phone: Yup.string()
.min(11, "Minimum 11 symbols")
.max(11, "Maximum 11 symbols")
.required("required"),
title: Yup.string()
.min(2, "Minimum 2 symbols")
.max(20, "Maximum 20 symbols")
.required("required"),
});
const ModalForm: FC<Props> = ({ user, isUserLoading }) => {
const {closeCustomModal, showCustomModal} = useCustomModal()
const { setItemIdForUpdate } = useListView();
const { refetch, isLoading } = useQueryResponse();
const [userForEdit] = useState<User>({
...user,
avatar: user.avatar || initialUser.avatar,
// role: user.role || initialUser.role,
// position: user.position || initialUser.position,
name: user.name || initialUser.name,
// email: user.email || initialUser.email,
});
const cancel = (withRefresh?: boolean) => {
if (withRefresh) {
refetch();
}
setItemIdForUpdate(undefined);
closeCustomModal()
};
const blankImg = toAbsoluteUrl("media/svg/avatars/blank.svg");
const userAvatarImg = toAbsoluteUrl(`media/${userForEdit.avatar}`);
const formik = useFormik({
initialValues: userForEdit,
validationSchema: editUserSchema,
onSubmit: async (values, { setSubmitting }) => {
setSubmitting(true);
try {
if (isNotEmpty(values.id)) {
await updateUser(values);
} else {
await createSignatory({employer_uid:showCustomModal?.data?.id, ...values});
}
} catch (ex) {
console.error(ex);
} finally {
setSubmitting(true);
cancel(true);
}
},
});
return (
<>
<form
id="kt_modal_add_user_form"
className="form"
onSubmit={formik.handleSubmit}
noValidate
>
{/* begin::Scroll */}
<div
className="d-flex flex-column scroll-y me-n7 pe-7"
id="kt_modal_add_user_scroll"
data-kt-scroll="true"
data-kt-scroll-activate="{default: false, lg: true}"
data-kt-scroll-max-height="auto"
data-kt-scroll-dependencies="#kt_modal_add_user_header"
data-kt-scroll-wrappers="#kt_modal_add_user_scroll"
data-kt-scroll-offset="300px"
>
{/* begin::Input group */}
{/* <div className='fv-row mb-7'>
<label className='d-block fw-bold fs-6 mb-5'>Avatar</label>
<div
className='image-input image-input-outline'
data-kt-image-input='true'
style={{backgroundImage: `url('${blankImg}')`}}
>
<div
className='image-input-wrapper w-125px h-125px'
style={{backgroundImage: `url('${userAvatarImg}')`}}
></div>
<label
className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow'
data-kt-image-input-action='change'
data-bs-toggle='tooltip'
title='Change avatar'
>
<i className='bi bi-pencil-fill fs-7'></i>
<input type='file' name='avatar' accept='.png, .jpg, .jpeg' />
<input type='hidden' name='avatar_remove' />
</label>
<span
className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow'
data-kt-image-input-action='cancel'
data-bs-toggle='tooltip'
title='Cancel avatar'
>
<i className='bi bi-x fs-2'></i>
</span>
<span
className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow'
data-kt-image-input-action='remove'
data-bs-toggle='tooltip'
title='Remove avatar'
>
<i className='bi bi-x fs-2'></i>
</span>
</div>
<div className='form-text'>Allowed file types: png, jpg, jpeg.</div>
</div> */}
{/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
{/* begin::Label */}
<label className="required fw-bold fs-6 mb-2">Name</label>
{/* end::Label */}
{/* begin::Input */}
<input
placeholder="Full name"
{...formik.getFieldProps("name")}
type="text"
name="name"
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.name && formik.errors.name },
{
"is-valid": formik.touched.name && !formik.errors.name,
}
)}
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
/>
{formik.touched.name && formik.errors.name && (
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.name}</span>
</div>
</div>
)}
{/* end::Input */}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
{/* begin::Label */}
<label className="required fw-bold fs-6 mb-2">Title</label>
{/* end::Label */}
{/* begin::Input */}
<input
placeholder="title"
{...formik.getFieldProps("title")}
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.title && formik.errors.title },
{
"is-valid": formik.touched.title && !formik.errors.title,
}
)}
type="text"
name="title"
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{formik.touched.title && formik.errors.title && (
// <div className="fv-plugins-message-container">
// <span role="alert">{formik.errors.title}</span>
// </div>
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.title}</span>
</div>
</div>
)}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
{/* begin::Label */}
<label className="required fw-bold fs-6 mb-2">Phone</label>
{/* end::Label */}
{/* begin::Input */}
<input
placeholder="Phone"
{...formik.getFieldProps("phone")}
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.title && formik.errors.title },
{
"is-valid": formik.touched.title && !formik.errors.title,
}
)}
type="text"
name="phone"
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{formik.touched.phone && formik.errors.phone && (
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.phone}</span>
</div>
</div>
)}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
{/* begin::Label */}
<label className="required fw-bold fs-6 mb-2">Email</label>
{/* end::Label */}
{/* begin::Input */}
<input
placeholder="Email"
{...formik.getFieldProps("email")}
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.email && formik.errors.email },
{
"is-valid": formik.touched.email && !formik.errors.email,
}
)}
type="email"
name="email"
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{formik.touched.email && formik.errors.email && (
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.email}</span>
</div>
</div>
)}
</div>
{/* end::Input group */}
</div>
{/* end::Scroll */}
{/* begin::Actions */}
<div className="text-center pt-15">
<button
type="reset"
onClick={() => cancel()}
className="btn btn-danger me-3"
data-kt-users-modal-action="cancel"
disabled={formik.isSubmitting || isUserLoading}
>
Cancel
</button>
<button
type="submit"
className="btn btn-primary"
data-kt-users-modal-action="submit"
disabled={
isUserLoading ||
formik.isSubmitting ||
!formik.isValid ||
!formik.touched
}
>
<span className="indicator-label">Add</span>
{(formik.isSubmitting || isUserLoading) && (
<span className="indicator-progress">
Please wait...{" "}
<span className="spinner-border spinner-border-sm align-middle ms-2"></span>
</span>
)}
</button>
</div>
{/* end::Actions */}
</form>
{(formik.isSubmitting || isUserLoading) && <UsersListLoading />}
</>
);
};
export { ModalForm };
@@ -0,0 +1,40 @@
import {useQuery} from 'react-query'
import {ModalForm} from './ModalForm'
import {isNotEmpty, QUERIES} from '../../../../../../_digifi/helpers'
import {useListView} from '../core/ListViewProvider'
import {getUserById} from '../core/_requests'
const ModalFormWrapper = () => {
const {itemIdForUpdate, setItemIdForUpdate} = useListView()
const enabledQuery: boolean = isNotEmpty(itemIdForUpdate)
const {
isLoading,
data: user,
error,
} = useQuery(
`${QUERIES.USERS_LIST}-user-${itemIdForUpdate}`,
() => {
return getUserById(itemIdForUpdate)
},
{
cacheTime: 0,
enabled: enabledQuery,
onError: (err) => {
setItemIdForUpdate(undefined)
console.error(err)
},
}
)
if (!itemIdForUpdate) {
return <ModalForm isUserLoading={isLoading} user={{id: undefined}} />
}
if (!isLoading && !error && user) {
return <ModalForm isUserLoading={isLoading} user={user} />
}
return null
}
export {ModalFormWrapper}
@@ -0,0 +1,34 @@
import {KTIcon} from '../../../../../../_digifi/helpers'
import {useListView} from '../core/ListViewProvider'
import { useCustomModal } from '../../../../../../context/CustomModal'
const ModalHeader = () => {
const {setItemIdForUpdate, itemIdForUpdate} = useListView()
const {closeCustomModal} = useCustomModal()
const onClose = () => {
setItemIdForUpdate(undefined)
closeCustomModal()
}
return (
<div className='modal-header'>
{/* begin::Modal title */}
<h2 className='fw-bolder'>Add Signatory</h2>
{/* end::Modal title */}
{/* begin::Close */}
<div
className='btn btn-icon btn-sm btn-active-icon-primary'
data-kt-users-modal-action='close'
onClick={onClose}
style={{cursor: 'pointer'}}
>
<KTIcon iconName='cross' className='fs-1' />
</div>
{/* end::Close */}
</div>
)
}
export {ModalHeader}
@@ -4,16 +4,16 @@ export type User = {
name?: string name?: string
avatar?: string avatar?: string
// email?: string // email?: string
position?: string // position?: string
role?: string // role?: string
last_login?: string // last_login?: string
two_steps?: boolean // two_steps?: boolean
joined_day?: string // joined_day?: string
online?: boolean // online?: boolean
initials?: { // initials?: {
label: string // label: string
state: string // state: string
} // }
uid?: string uid?: string
percent_interest?: string percent_interest?: string
max_loan?: string max_loan?: string
@@ -31,8 +31,8 @@ export type UsersQueryResponse = Response<Array<User>>
export const initialUser: User = { export const initialUser: User = {
avatar: 'avatars/300-6.jpg', avatar: 'avatars/300-6.jpg',
position: 'Art Director', // position: 'Art Director',
role: 'Administrator', // role: 'Administrator',
name: '', name: '',
email: '', email: '',
} }
@@ -1,6 +1,7 @@
import axios, { AxiosResponse } from "axios"; import axios, { AxiosResponse } from "axios";
import { ID, Response } from "../../../../../../_digifi/helpers"; import { ID, Response } from "../../../../../../_digifi/helpers";
import { User, UsersQueryResponse } from "./_models"; import { User, UsersQueryResponse } from "./_models";
import { UsersQueryResponse as SignatoryQueryResponse } from "../../signatory-list/core/_models";
const API_URL = import.meta.env.VITE_APP_THEME_API_URL; const API_URL = import.meta.env.VITE_APP_THEME_API_URL;
const USER_URL = `${API_URL}/user`; const USER_URL = `${API_URL}/user`;
@@ -19,6 +20,44 @@ const getEmployersList = (query: string): Promise<UsersQueryResponse> => { // FU
.then((d: AxiosResponse<UsersQueryResponse>) => d.data); .then((d: AxiosResponse<UsersQueryResponse>) => d.data);
}; };
// const createUser = (user: any): Promise<User | undefined> => { // FUNCTION TO ADD/CREATE NEW USER
// const formData = new FormData();
// delete user.email
// delete user.role
// delete user.position
// delete user.avatar
// delete user.id
// for (let data in user) {
// formData.append(data, user[data]);
// }
// return axios
// .post(`${NEW_USER_ENDPOINT}/employer`, formData)
// .then((response: AxiosResponse<Response<User>>) => response.data)
// .then((response: Response<User>) => response.data);
// };
const createUser = (user: any): Promise<UsersQueryResponse | undefined> => { // FUNCTION TO ADD/CREATE NEW USER
const formData = new FormData();
delete user.avatar
delete user.id
for (let data in user) {
formData.append(data, user[data]);
}
return axios.post(`${NEW_USER_ENDPOINT}/employer`, formData)
.then((response: AxiosResponse<UsersQueryResponse>) => response.data)
};
const createSignatory = (user: any): Promise<SignatoryQueryResponse | undefined> => { // FUNCTION TO ADD/CREATE NEW Signatory
const formData = new FormData();
delete user.avatar
delete user.id
for (let data in user) {
formData.append(data, user[data]);
}
return axios.post(`${NEW_USER_ENDPOINT}/employers/signatory`, formData)
.then((response: AxiosResponse<SignatoryQueryResponse>) => response.data)
};
const getUserById = (id: ID): Promise<User | undefined> => { const getUserById = (id: ID): Promise<User | undefined> => {
return axios return axios
.get(`${USER_URL}/${id}`) .get(`${USER_URL}/${id}`)
@@ -26,13 +65,6 @@ const getUserById = (id: ID): Promise<User | undefined> => {
.then((response: Response<User>) => response.data); .then((response: Response<User>) => response.data);
}; };
const createUser = (user: User): Promise<User | undefined> => {
return axios
.put(USER_URL, user)
.then((response: AxiosResponse<Response<User>>) => response.data)
.then((response: Response<User>) => response.data);
};
const updateUser = (user: User): Promise<User | undefined> => { const updateUser = (user: User): Promise<User | undefined> => {
return axios return axios
.post(`${USER_URL}/${user.id}`, user) .post(`${USER_URL}/${user.id}`, user)
@@ -51,9 +83,11 @@ const deleteSelectedUsers = (userIds: Array<ID>): Promise<void> => {
export { export {
getEmployersList, getEmployersList,
createUser,
createSignatory,
deleteUser, deleteUser,
deleteSelectedUsers, deleteSelectedUsers,
getUserById, getUserById,
createUser,
updateUser, updateUser,
}; };
@@ -6,6 +6,7 @@ import {ID, KTIcon, QUERIES} from '../../../../../../../_digifi/helpers'
import {useListView} from '../../core/ListViewProvider' import {useListView} from '../../core/ListViewProvider'
import {useQueryResponse} from '../../core/QueryResponseProvider' import {useQueryResponse} from '../../core/QueryResponseProvider'
import {deleteUser} from '../../core/_requests' import {deleteUser} from '../../core/_requests'
import { useCustomModal } from '../../../../../../../context/CustomModal'
type Props = { type Props = {
id: ID id: ID
@@ -16,6 +17,8 @@ const UserActionsCell: FC<Props> = ({id}) => {
const {query} = useQueryResponse() const {query} = useQueryResponse()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const {MODALNAMES, openCustomModal} = useCustomModal()
useEffect(() => { useEffect(() => {
MenuComponent.reinitialization() MenuComponent.reinitialization()
}, []) }, [])
@@ -56,6 +59,17 @@ const UserActionsCell: FC<Props> = ({id}) => {
</div> </div>
{/* end::Menu item */} {/* end::Menu item */}
{/* begin::Menu item */}
<div className='menu-item px-3'>
<a className='menu-link px-3' onClick={()=>{openCustomModal(MODALNAMES.addSignatory, {id})}}>
Add Signatory
</a>
</div>
{/* end::Menu item */}
<div className='px-3'>
<div className='separator separator-dashed'></div>
</div>
{/* begin::Menu item */} {/* begin::Menu item */}
<div className='menu-item px-3'> <div className='menu-item px-3'>
<a <a
@@ -2,7 +2,9 @@ import {useEffect} from 'react'
import {UserEditModalHeader} from './UserEditModalHeader' import {UserEditModalHeader} from './UserEditModalHeader'
import {UserEditModalFormWrapper} from './UserEditModalFormWrapper' import {UserEditModalFormWrapper} from './UserEditModalFormWrapper'
const UserEditModal = () => { const UserEditModal = () => {
useEffect(() => { useEffect(() => {
document.body.classList.add('modal-open') document.body.classList.add('modal-open')
return () => { return () => {
@@ -0,0 +1,575 @@
import { FC, useState } from "react";
import * as Yup from "yup";
import { useFormik } from "formik";
import { isNotEmpty, toAbsoluteUrl } from "../../../../../../_digifi/helpers";
import { initialUser, User } from "../core/_models";
import clsx from "clsx";
import { useListView } from "../core/ListViewProvider";
import { UsersListLoading } from "../components/loading/UsersListLoading";
import { createUser, updateUser } from "../core/_requests";
import { useAllResponse, useQueryResponse } from "../core/QueryResponseProvider";
type Props = {
isUserLoading: boolean;
user: User;
};
const editUserSchema = Yup.object().shape({
// email: Yup.string()
// .email('Wrong email format')
// .min(3, 'Minimum 3 symbols')
// .max(50, 'Maximum 50 symbols')
// .required('required'),
name: Yup.string()
.min(3, "Minimum 3 symbols")
.max(50, "Maximum 50 symbols")
.required("required"),
percent_interest: Yup.string()
.min(3, "Minimum 3 symbols")
.max(50, "Maximum 50 symbols")
.required("required"),
max_loan: Yup.string()
.min(3, "Minimum 3 symbols")
.max(50, "Maximum 50 symbols")
.required("required"),
tenor: Yup.string()
.min(3, "Minimum 3 symbols")
.max(50, "Maximum 50 symbols")
.required("required"),
retirement_age: Yup.string()
.min(3, "Minimum 3 symbols")
.max(50, "Maximum 50 symbols")
.required("is required"),
sector: Yup.string()
.min(3, "Minimum 3 symbols")
.max(50, "Maximum 50 symbols")
.required("required"),
salary_source: Yup.string()
.min(3, "Minimum 3 symbols")
.max(50, "Maximum 50 symbols")
.required("required"),
});
const UserEditModalForm: FC<Props> = ({ user, isUserLoading }) => {
const response:any = useAllResponse()
console.log('response', response)
const { setItemIdForUpdate } = useListView();
const { refetch, isLoading } = useQueryResponse();
const [userForEdit] = useState<User>({
...user,
avatar: user.avatar || initialUser.avatar,
role: user.role || initialUser.role,
position: user.position || initialUser.position,
name: user.name || initialUser.name,
email: user.email || initialUser.email,
});
const cancel = (withRefresh?: boolean) => {
if (withRefresh) {
refetch();
}
setItemIdForUpdate(undefined);
};
const blankImg = toAbsoluteUrl("media/svg/avatars/blank.svg");
const userAvatarImg = toAbsoluteUrl(`media/${userForEdit.avatar}`);
const formik = useFormik({
initialValues: userForEdit,
validationSchema: editUserSchema,
onSubmit: async (values, { setSubmitting }) => {
console.log('values', values)
setSubmitting(true);
// try {
// if (isNotEmpty(values.id)) {
// await updateUser(values);
// } else {
// await createUser(values);
// }
// } catch (ex) {
// console.error(ex);
// } finally {
// setSubmitting(true);
// cancel(true);
// }
},
});
return (
<>
<form
id="kt_modal_add_user_form"
className="form"
onSubmit={formik.handleSubmit}
noValidate
>
{/* begin::Scroll */}
<div
className="d-flex flex-column scroll-y me-n7 pe-7"
id="kt_modal_add_user_scroll"
data-kt-scroll="true"
data-kt-scroll-activate="{default: false, lg: true}"
data-kt-scroll-max-height="auto"
data-kt-scroll-dependencies="#kt_modal_add_user_header"
data-kt-scroll-wrappers="#kt_modal_add_user_scroll"
data-kt-scroll-offset="300px"
>
{/* begin::Input group */}
{/* <div className='fv-row mb-7'>
<label className='d-block fw-bold fs-6 mb-5'>Avatar</label>
<div
className='image-input image-input-outline'
data-kt-image-input='true'
style={{backgroundImage: `url('${blankImg}')`}}
>
<div
className='image-input-wrapper w-125px h-125px'
style={{backgroundImage: `url('${userAvatarImg}')`}}
></div>
<label
className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow'
data-kt-image-input-action='change'
data-bs-toggle='tooltip'
title='Change avatar'
>
<i className='bi bi-pencil-fill fs-7'></i>
<input type='file' name='avatar' accept='.png, .jpg, .jpeg' />
<input type='hidden' name='avatar_remove' />
</label>
<span
className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow'
data-kt-image-input-action='cancel'
data-bs-toggle='tooltip'
title='Cancel avatar'
>
<i className='bi bi-x fs-2'></i>
</span>
<span
className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow'
data-kt-image-input-action='remove'
data-bs-toggle='tooltip'
title='Remove avatar'
>
<i className='bi bi-x fs-2'></i>
</span>
</div>
<div className='form-text'>Allowed file types: png, jpg, jpeg.</div>
</div> */}
{/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
{/* begin::Label */}
<label className="required fw-bold fs-6 mb-2">Full Name</label>
{/* end::Label */}
{/* begin::Input */}
<input
placeholder="Full name"
{...formik.getFieldProps("name")}
type="text"
name="name"
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.name && formik.errors.name },
{
"is-valid": formik.touched.name && !formik.errors.name,
}
)}
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
/>
{formik.touched.name && formik.errors.name && (
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.name}</span>
</div>
</div>
)}
{/* end::Input */}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
{/* begin::Label */}
<label className="required fw-bold fs-6 mb-2">Interest</label>
{/* end::Label */}
{/* begin::Input */}
<input
placeholder="Interest"
{...formik.getFieldProps("interest")}
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.percent_interest && formik.errors.percent_interest },
{
"is-valid": formik.touched.percent_interest && !formik.errors.percent_interest,
}
)}
type="text"
name="percent_interest"
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{formik.touched.percent_interest && formik.errors.percent_interest && (
// <div className="fv-plugins-message-container">
// <span role="alert">{formik.errors.percent_interest}</span>
// </div>
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.percent_interest}</span>
</div>
</div>
)}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
{/* begin::Label */}
<label className="required fw-bold fs-6 mb-2">Max Loan</label>
{/* end::Label */}
{/* begin::Input */}
<input
placeholder="Max Loan"
{...formik.getFieldProps("max_loan")}
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.max_loan && formik.errors.max_loan },
{
"is-valid": formik.touched.max_loan && !formik.errors.max_loan,
}
)}
type="text"
name="max_loan"
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{formik.touched.max_loan && formik.errors.max_loan && (
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.max_loan}</span>
</div>
</div>
)}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
{/* begin::Label */}
<label className="required fw-bold fs-6 mb-2">Tenor</label>
{/* end::Label */}
{/* begin::Input */}
<input
placeholder="Tenor"
{...formik.getFieldProps("tenor")}
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.tenor && formik.errors.tenor },
{
"is-valid": formik.touched.tenor && !formik.errors.tenor,
}
)}
type="text"
name="tenor"
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{formik.touched.tenor && formik.errors.tenor && (
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.tenor}</span>
</div>
</div>
)}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
{/* begin::Label */}
<label className="required fw-bold fs-6 mb-2">Ret Age</label>
{/* end::Label */}
{/* begin::Input */}
<input
placeholder="Retirement Age"
{...formik.getFieldProps("retirement_age")}
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.retirement_age && formik.errors.retirement_age },
{
"is-valid": formik.touched.retirement_age && !formik.errors.retirement_age,
}
)}
type="text"
name="retirement_age"
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{formik.touched.retirement_age && formik.errors.retirement_age && (
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.retirement_age}</span>
</div>
</div>
)}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
<label className="required fw-bold fs-6 mb-2">Sector</label>
<select
{...formik.getFieldProps("sector")}
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.sector && formik.errors.sector },
{
"is-valid": formik.touched.sector && !formik.errors.sector,
}
)}
name="sector"
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
>
{isLoading ?
<option value=''>Loading...</option>
:
<>
<option value=''>Select Sector</option>
{response?.employer_sector?.map((item:any) => (
<option value={item.uid}>{item.name}</option>
))
}
</>
}
</select>
{/* end::Input */}
{formik.touched.sector && formik.errors.sector && (
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.sector}</span>
</div>
</div>
)}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
{/* begin::Label */}
<label className="required fw-bold fs-6 mb-2">Salary</label>
{/* end::Label */}
{/* begin::Input */}
<select
{...formik.getFieldProps("salary_source")}
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.salary_source && formik.errors.salary_source },
{
"is-valid": formik.touched.salary_source && !formik.errors.salary_source,
}
)}
name="salary_source"
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
>
{isLoading ?
<option value=''>Loading...</option>
:
<>
<option value=''>Select Salary Source</option>
{response?.salary_sources?.map((item:any) => (
<option value={item.uid}>{item.name}</option>
))
}
</>
}
</select>
{/* end::Input */}
{formik.touched.salary_source && formik.errors.salary_source && (
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.salary_source}</span>
</div>
</div>
)}
</div>
{/* end::Input group */}
{/* begin::Input group */}
{/* <div className='mb-7'>
<label className='required fw-bold fs-6 mb-5'>Role</label>
<div className='d-flex fv-row'>
<div className='form-check form-check-custom form-check-solid'>
<input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
value='Administrator'
id='kt_modal_update_role_option_0'
checked={formik.values.role === 'Administrator'}
disabled={formik.isSubmitting || isUserLoading}
/>
<label className='form-check-label' htmlFor='kt_modal_update_role_option_0'>
<div className='fw-bolder text-gray-800'>Administrator</div>
<div className='text-gray-600'>
Best for business owners and company administrators
</div>
</label>
</div>
</div>
<div className='separator separator-dashed my-5'></div>
<div className='d-flex fv-row'>
<div className='form-check form-check-custom form-check-solid'>
<input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
value='Developer'
id='kt_modal_update_role_option_1'
checked={formik.values.role === 'Developer'}
disabled={formik.isSubmitting || isUserLoading}
/>
<label className='form-check-label' htmlFor='kt_modal_update_role_option_1'>
<div className='fw-bolder text-gray-800'>Developer</div>
<div className='text-gray-600'>
Best for developers or people primarily using the API
</div>
</label>
</div>
</div>
<div className='separator separator-dashed my-5'></div>
<div className='d-flex fv-row'>
<div className='form-check form-check-custom form-check-solid'>
<input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
value='Analyst'
id='kt_modal_update_role_option_2'
checked={formik.values.role === 'Analyst'}
disabled={formik.isSubmitting || isUserLoading}
/>
<label className='form-check-label' htmlFor='kt_modal_update_role_option_2'>
<div className='fw-bolder text-gray-800'>Analyst</div>
<div className='text-gray-600'>
Best for people who need full access to analytics data, but don't need to update
business settings
</div>
</label>
</div>
</div>
<div className='separator separator-dashed my-5'></div>
<div className='d-flex fv-row'>
<div className='form-check form-check-custom form-check-solid'>
<input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
value='Support'
id='kt_modal_update_role_option_3'
checked={formik.values.role === 'Support'}
disabled={formik.isSubmitting || isUserLoading}
/>
<label className='form-check-label' htmlFor='kt_modal_update_role_option_3'>
<div className='fw-bolder text-gray-800'>Support</div>
<div className='text-gray-600'>
Best for employees who regularly refund payments and respond to disputes
</div>
</label>
</div>
</div>
<div className='separator separator-dashed my-5'></div>
<div className='d-flex fv-row'>
<div className='form-check form-check-custom form-check-solid'>
<input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
id='kt_modal_update_role_option_4'
value='Trial'
checked={formik.values.role === 'Trial'}
disabled={formik.isSubmitting || isUserLoading}
/>
<label className='form-check-label' htmlFor='kt_modal_update_role_option_4'>
<div className='fw-bolder text-gray-800'>Trial</div>
<div className='text-gray-600'>
Best for people who need to preview content data, but don't need to make any
updates
</div>
</label>
</div>
</div>
</div> */}
{/* end::Input group */}
</div>
{/* end::Scroll */}
{/* begin::Actions */}
<div className="text-center pt-15">
<button
type="reset"
onClick={() => cancel()}
className="btn btn-danger me-3"
data-kt-users-modal-action="cancel"
disabled={formik.isSubmitting || isUserLoading}
>
Cancel
</button>
<button
type="submit"
className="btn btn-primary"
data-kt-users-modal-action="submit"
disabled={
isUserLoading ||
formik.isSubmitting ||
!formik.isValid ||
!formik.touched
}
>
<span className="indicator-label">Add</span>
{(formik.isSubmitting || isUserLoading) && (
<span className="indicator-progress">
Please wait...{" "}
<span className="spinner-border spinner-border-sm align-middle ms-2"></span>
</span>
)}
</button>
</div>
{/* end::Actions */}
</form>
{(formik.isSubmitting || isUserLoading) && <UsersListLoading />}
</>
);
};
export { UserEditModalForm };
@@ -1,109 +1,137 @@
import {FC, useState} from 'react' import { FC, useState } from "react";
import * as Yup from 'yup' import * as Yup from "yup";
import {useFormik} from 'formik' import { useFormik } from "formik";
import {isNotEmpty, toAbsoluteUrl} from '../../../../../../_digifi/helpers' import { isNotEmpty, toAbsoluteUrl } from "../../../../../../_digifi/helpers";
import {initialUser, User} from '../core/_models' import { initialUser, User } from "../core/_models";
import clsx from 'clsx' import clsx from "clsx";
import {useListView} from '../core/ListViewProvider' import { useListView } from "../core/ListViewProvider";
import {UsersListLoading} from '../components/loading/UsersListLoading' import { UsersListLoading } from "../components/loading/UsersListLoading";
import {createUser, updateUser} from '../core/_requests' import { createUser, updateUser } from "../core/_requests";
import {useQueryResponse} from '../core/QueryResponseProvider' import { useAllResponse, useQueryResponse } from "../core/QueryResponseProvider";
type Props = { type Props = {
isUserLoading: boolean isUserLoading: boolean;
user: User user: User;
} };
const editUserSchema = Yup.object().shape({ const editUserSchema = Yup.object().shape({
email: Yup.string() // email: Yup.string()
.email('Wrong email format') // .email('Wrong email format')
.min(3, 'Minimum 3 symbols') // .min(3, 'Minimum 3 symbols')
.max(50, 'Maximum 50 symbols') // .max(50, 'Maximum 50 symbols')
.required('Email is required'), // .required('required'),
name: Yup.string() name: Yup.string()
.min(3, 'Minimum 3 symbols') .min(3, "Minimum 3 symbols")
.max(50, 'Maximum 50 symbols') .max(50, "Maximum 50 symbols")
.required('Name is required'), .required("required"),
}) percent_interest: Yup.number()
.typeError("Invalid number")
.min(1, "must be greater than 0")
// .test("no-e", "Invalid number", (value) => {
// if (value && /\d+e/.test(value)) {
// return false;
// }
// return true;
// })
.required("required"),
max_loan: Yup.number()
.typeError("Invalid number")
.min(1, "must be greater than 0")
.required("required"),
tenor: Yup.number()
.typeError("Invalid number")
.min(1, "must be greater than 0")
.required("required"),
retirement_age: Yup.number()
.typeError("Invalid number")
.min(1, "must be greater than 0")
.required("is required"),
sector: Yup.string()
.required("required"),
salary_source: Yup.string()
.required("required"),
});
const UserEditModalForm: FC<Props> = ({user, isUserLoading}) => { const UserEditModalForm: FC<Props> = ({ user, isUserLoading }) => {
const {setItemIdForUpdate} = useListView() const response:any = useAllResponse()
const {refetch} = useQueryResponse()
const { setItemIdForUpdate } = useListView();
const { refetch, isLoading } = useQueryResponse();
const [userForEdit] = useState<User>({ const [userForEdit] = useState<User>({
...user, ...user,
avatar: user.avatar || initialUser.avatar, avatar: user.avatar || initialUser.avatar,
role: user.role || initialUser.role, // role: user.role || initialUser.role,
position: user.position || initialUser.position, // position: user.position || initialUser.position,
name: user.name || initialUser.name, name: user.name || initialUser.name,
email: user.email || initialUser.email, // email: user.email || initialUser.email,
}) });
const cancel = (withRefresh?: boolean) => { const cancel = (withRefresh?: boolean) => {
if (withRefresh) { if (withRefresh) {
refetch() refetch();
} }
setItemIdForUpdate(undefined) setItemIdForUpdate(undefined);
} };
const blankImg = toAbsoluteUrl('media/svg/avatars/blank.svg') const blankImg = toAbsoluteUrl("media/svg/avatars/blank.svg");
const userAvatarImg = toAbsoluteUrl(`media/${userForEdit.avatar}`) const userAvatarImg = toAbsoluteUrl(`media/${userForEdit.avatar}`);
const formik = useFormik({ const formik = useFormik({
initialValues: userForEdit, initialValues: userForEdit,
validationSchema: editUserSchema, validationSchema: editUserSchema,
onSubmit: async (values, {setSubmitting}) => { onSubmit: async (values, { setSubmitting }) => {
setSubmitting(true) setSubmitting(true);
try { try {
if (isNotEmpty(values.id)) { if (isNotEmpty(values.id)) {
await updateUser(values) await updateUser(values);
} else { } else {
await createUser(values) await createUser(values);
} }
} catch (ex) { } catch (ex) {
console.error(ex) console.error(ex);
} finally { } finally {
setSubmitting(true) setSubmitting(true);
cancel(true) cancel(true);
} }
}, },
}) });
return ( return (
<> <>
<form id='kt_modal_add_user_form' className='form' onSubmit={formik.handleSubmit} noValidate> <form
id="kt_modal_add_user_form"
className="form"
onSubmit={formik.handleSubmit}
noValidate
>
{/* begin::Scroll */} {/* begin::Scroll */}
<div <div
className='d-flex flex-column scroll-y me-n7 pe-7' className="d-flex flex-column scroll-y me-n7 pe-7"
id='kt_modal_add_user_scroll' id="kt_modal_add_user_scroll"
data-kt-scroll='true' data-kt-scroll="true"
data-kt-scroll-activate='{default: false, lg: true}' data-kt-scroll-activate="{default: false, lg: true}"
data-kt-scroll-max-height='auto' data-kt-scroll-max-height="auto"
data-kt-scroll-dependencies='#kt_modal_add_user_header' data-kt-scroll-dependencies="#kt_modal_add_user_header"
data-kt-scroll-wrappers='#kt_modal_add_user_scroll' data-kt-scroll-wrappers="#kt_modal_add_user_scroll"
data-kt-scroll-offset='300px' data-kt-scroll-offset="300px"
> >
{/* begin::Input group */} {/* begin::Input group */}
<div className='fv-row mb-7'> {/* <div className='fv-row mb-7'>
{/* begin::Label */}
<label className='d-block fw-bold fs-6 mb-5'>Avatar</label> <label className='d-block fw-bold fs-6 mb-5'>Avatar</label>
{/* end::Label */}
{/* begin::Image input */}
<div <div
className='image-input image-input-outline' className='image-input image-input-outline'
data-kt-image-input='true' data-kt-image-input='true'
style={{backgroundImage: `url('${blankImg}')`}} style={{backgroundImage: `url('${blankImg}')`}}
> >
{/* begin::Preview existing avatar */}
<div <div
className='image-input-wrapper w-125px h-125px' className='image-input-wrapper w-125px h-125px'
style={{backgroundImage: `url('${userAvatarImg}')`}} style={{backgroundImage: `url('${userAvatarImg}')`}}
></div> ></div>
{/* end::Preview existing avatar */}
{/* begin::Label */} <label
{/* <label
className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow' className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow'
data-kt-image-input-action='change' data-kt-image-input-action='change'
data-bs-toggle='tooltip' data-bs-toggle='tooltip'
@@ -113,65 +141,56 @@ const UserEditModalForm: FC<Props> = ({user, isUserLoading}) => {
<input type='file' name='avatar' accept='.png, .jpg, .jpeg' /> <input type='file' name='avatar' accept='.png, .jpg, .jpeg' />
<input type='hidden' name='avatar_remove' /> <input type='hidden' name='avatar_remove' />
</label> */} </label>
{/* end::Label */}
{/* begin::Cancel */} <span
{/* <span
className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow' className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow'
data-kt-image-input-action='cancel' data-kt-image-input-action='cancel'
data-bs-toggle='tooltip' data-bs-toggle='tooltip'
title='Cancel avatar' title='Cancel avatar'
> >
<i className='bi bi-x fs-2'></i> <i className='bi bi-x fs-2'></i>
</span> */} </span>
{/* end::Cancel */}
{/* begin::Remove */} <span
{/* <span
className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow' className='btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow'
data-kt-image-input-action='remove' data-kt-image-input-action='remove'
data-bs-toggle='tooltip' data-bs-toggle='tooltip'
title='Remove avatar' title='Remove avatar'
> >
<i className='bi bi-x fs-2'></i> <i className='bi bi-x fs-2'></i>
</span> */} </span>
{/* end::Remove */}
</div> </div>
{/* end::Image input */} <div className='form-text'>Allowed file types: png, jpg, jpeg.</div>
</div> */}
{/* begin::Hint */}
{/* <div className='form-text'>Allowed file types: png, jpg, jpeg.</div> */}
{/* end::Hint */}
</div>
{/* end::Input group */} {/* end::Input group */}
{/* begin::Input group */} {/* begin::Input group */}
<div className='fv-row mb-7'> <div className="fv-row mb-7">
{/* begin::Label */} {/* begin::Label */}
<label className='required fw-bold fs-6 mb-2'>Full Name</label> <label className="required fw-bold fs-6 mb-2">Full Name</label>
{/* end::Label */} {/* end::Label */}
{/* begin::Input */} {/* begin::Input */}
<input <input
placeholder='Full name' placeholder="Full name"
{...formik.getFieldProps('name')} {...formik.getFieldProps("name")}
type='text' type="text"
name='name' name="name"
className={clsx( className={clsx(
'form-control form-control-solid mb-3 mb-lg-0', "form-control form-control-solid mb-3 mb-lg-0",
{'is-invalid': formik.touched.name && formik.errors.name}, { "is-invalid": formik.touched.name && formik.errors.name },
{ {
'is-valid': formik.touched.name && !formik.errors.name, "is-valid": formik.touched.name && !formik.errors.name,
} }
)} )}
autoComplete='off' autoComplete="off"
disabled={formik.isSubmitting || isUserLoading} disabled={formik.isSubmitting || isUserLoading}
/> />
{formik.touched.name && formik.errors.name && ( {formik.touched.name && formik.errors.name && (
<div className='fv-plugins-message-container'> <div className="fv-plugins-message-container">
<div className='fv-help-block'> <div className="fv-help-block">
<span role='alert'>{formik.errors.name}</span> <span role="alert">{formik.errors.name}</span>
</div> </div>
</div> </div>
)} )}
@@ -180,219 +199,253 @@ const UserEditModalForm: FC<Props> = ({user, isUserLoading}) => {
{/* end::Input group */} {/* end::Input group */}
{/* begin::Input group */} {/* begin::Input group */}
<div className='fv-row mb-7'> <div className="fv-row mb-7">
{/* begin::Label */} {/* begin::Label */}
<label className='required fw-bold fs-6 mb-2'>Email</label> <label className="required fw-bold fs-6 mb-2">Interest</label>
{/* end::Label */} {/* end::Label */}
{/* begin::Input */} {/* begin::Input */}
<input <input
placeholder='Email' placeholder="Interest"
{...formik.getFieldProps('email')} {...formik.getFieldProps("percent_interest")}
className={clsx( className={clsx(
'form-control form-control-solid mb-3 mb-lg-0', "form-control form-control-solid mb-3 mb-lg-0",
{'is-invalid': formik.touched.email && formik.errors.email}, { "is-invalid": formik.touched.percent_interest && formik.errors.percent_interest },
{ {
'is-valid': formik.touched.email && !formik.errors.email, "is-valid": formik.touched.percent_interest && !formik.errors.percent_interest,
} }
)} )}
type='email' type="text"
name='email' name="percent_interest"
autoComplete='off' autoComplete="off"
disabled={formik.isSubmitting || isUserLoading} disabled={formik.isSubmitting || isUserLoading}
/> />
{/* end::Input */} {/* end::Input */}
{formik.touched.email && formik.errors.email && ( {formik.touched.percent_interest && formik.errors.percent_interest && (
<div className='fv-plugins-message-container'> // <div className="fv-plugins-message-container">
<span role='alert'>{formik.errors.email}</span> // <span role="alert">{formik.errors.percent_interest}</span>
// </div>
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.percent_interest}</span>
</div>
</div> </div>
)} )}
</div> </div>
{/* end::Input group */} {/* end::Input group */}
{/* begin::Input group */} {/* begin::Input group */}
<div className='mb-7'> <div className="fv-row mb-7">
{/* begin::Label */} {/* begin::Label */}
<label className='required fw-bold fs-6 mb-5'>Role</label> <label className="required fw-bold fs-6 mb-2">Max Loan</label>
{/* end::Label */} {/* end::Label */}
{/* begin::Roles */}
{/* begin::Input row */}
<div className='d-flex fv-row'>
{/* begin::Radio */}
<div className='form-check form-check-custom form-check-solid'>
{/* begin::Input */}
<input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
value='Administrator'
id='kt_modal_update_role_option_0'
checked={formik.values.role === 'Administrator'}
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */} {/* begin::Input */}
{/* begin::Label */} <input
<label className='form-check-label' htmlFor='kt_modal_update_role_option_0'> placeholder="Max Loan"
<div className='fw-bolder text-gray-800'>Administrator</div> {...formik.getFieldProps("max_loan")}
<div className='text-gray-600'> className={clsx(
Best for business owners and company administrators "form-control form-control-solid mb-3 mb-lg-0",
</div> { "is-invalid": formik.touched.max_loan && formik.errors.max_loan },
</label> {
{/* end::Label */} "is-valid": formik.touched.max_loan && !formik.errors.max_loan,
}
)}
type="text"
name="max_loan"
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{formik.touched.max_loan && formik.errors.max_loan && (
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.max_loan}</span>
</div>
</div> </div>
{/* end::Radio */} )}
</div>
{/* end::Input row */}
<div className='separator separator-dashed my-5'></div>
{/* begin::Input row */}
<div className='d-flex fv-row'>
{/* begin::Radio */}
<div className='form-check form-check-custom form-check-solid'>
{/* begin::Input */}
<input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
value='Developer'
id='kt_modal_update_role_option_1'
checked={formik.values.role === 'Developer'}
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{/* begin::Label */}
<label className='form-check-label' htmlFor='kt_modal_update_role_option_1'>
<div className='fw-bolder text-gray-800'>Developer</div>
<div className='text-gray-600'>
Best for developers or people primarily using the API
</div>
</label>
{/* end::Label */}
</div>
{/* end::Radio */}
</div>
{/* end::Input row */}
<div className='separator separator-dashed my-5'></div>
{/* begin::Input row */}
<div className='d-flex fv-row'>
{/* begin::Radio */}
<div className='form-check form-check-custom form-check-solid'>
{/* begin::Input */}
<input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
value='Analyst'
id='kt_modal_update_role_option_2'
checked={formik.values.role === 'Analyst'}
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{/* begin::Label */}
<label className='form-check-label' htmlFor='kt_modal_update_role_option_2'>
<div className='fw-bolder text-gray-800'>Analyst</div>
<div className='text-gray-600'>
Best for people who need full access to analytics data, but don't need to update
business settings
</div>
</label>
{/* end::Label */}
</div>
{/* end::Radio */}
</div>
{/* end::Input row */}
<div className='separator separator-dashed my-5'></div>
{/* begin::Input row */}
<div className='d-flex fv-row'>
{/* begin::Radio */}
<div className='form-check form-check-custom form-check-solid'>
{/* begin::Input */}
<input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
value='Support'
id='kt_modal_update_role_option_3'
checked={formik.values.role === 'Support'}
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{/* begin::Label */}
<label className='form-check-label' htmlFor='kt_modal_update_role_option_3'>
<div className='fw-bolder text-gray-800'>Support</div>
<div className='text-gray-600'>
Best for employees who regularly refund payments and respond to disputes
</div>
</label>
{/* end::Label */}
</div>
{/* end::Radio */}
</div>
{/* end::Input row */}
<div className='separator separator-dashed my-5'></div>
{/* begin::Input row */}
<div className='d-flex fv-row'>
{/* begin::Radio */}
<div className='form-check form-check-custom form-check-solid'>
{/* begin::Input */}
<input
className='form-check-input me-3'
{...formik.getFieldProps('role')}
name='role'
type='radio'
id='kt_modal_update_role_option_4'
value='Trial'
checked={formik.values.role === 'Trial'}
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{/* begin::Label */}
<label className='form-check-label' htmlFor='kt_modal_update_role_option_4'>
<div className='fw-bolder text-gray-800'>Trial</div>
<div className='text-gray-600'>
Best for people who need to preview content data, but don't need to make any
updates
</div>
</label>
{/* end::Label */}
</div>
{/* end::Radio */}
</div>
{/* end::Input row */}
{/* end::Roles */}
</div> </div>
{/* end::Input group */} {/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
{/* begin::Label */}
<label className="required fw-bold fs-6 mb-2">Tenor</label>
{/* end::Label */}
{/* begin::Input */}
<input
placeholder="Tenor"
{...formik.getFieldProps("tenor")}
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.tenor && formik.errors.tenor },
{
"is-valid": formik.touched.tenor && !formik.errors.tenor,
}
)}
type="text"
name="tenor"
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{formik.touched.tenor && formik.errors.tenor && (
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.tenor}</span>
</div>
</div>
)}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
{/* begin::Label */}
<label className="required fw-bold fs-6 mb-2">Ret Age</label>
{/* end::Label */}
{/* begin::Input */}
<input
placeholder="Retirement Age"
{...formik.getFieldProps("retirement_age")}
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.retirement_age && formik.errors.retirement_age },
{
"is-valid": formik.touched.retirement_age && !formik.errors.retirement_age,
}
)}
type="text"
name="retirement_age"
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
/>
{/* end::Input */}
{formik.touched.retirement_age && formik.errors.retirement_age && (
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.retirement_age}</span>
</div>
</div>
)}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
<label className="required fw-bold fs-6 mb-2">Sector</label>
<select
{...formik.getFieldProps("sector")}
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.sector && formik.errors.sector },
{
"is-valid": formik.touched.sector && !formik.errors.sector,
}
)}
name="sector"
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
>
{isLoading ?
<option value=''>Loading...</option>
:
<>
<option value=''>Select Sector</option>
{response?.employer_sector?.map((item:any) => (
<option value={item.uid}>{item.name}</option>
))
}
</>
}
</select>
{/* end::Input */}
{formik.touched.sector && formik.errors.sector && (
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.sector}</span>
</div>
</div>
)}
</div>
{/* end::Input group */}
{/* begin::Input group */}
<div className="fv-row mb-7">
{/* begin::Label */}
<label className="required fw-bold fs-6 mb-2">Salary</label>
{/* end::Label */}
{/* begin::Input */}
<select
{...formik.getFieldProps("salary_source")}
className={clsx(
"form-control form-control-solid mb-3 mb-lg-0",
{ "is-invalid": formik.touched.salary_source && formik.errors.salary_source },
{
"is-valid": formik.touched.salary_source && !formik.errors.salary_source,
}
)}
name="salary_source"
autoComplete="off"
disabled={formik.isSubmitting || isUserLoading}
>
{isLoading ?
<option value=''>Loading...</option>
:
<>
<option value=''>Select Salary Source</option>
{response?.salary_sources?.map((item:any) => (
<option value={item.uid}>{item.name}</option>
))
}
</>
}
</select>
{/* end::Input */}
{formik.touched.salary_source && formik.errors.salary_source && (
<div className="fv-plugins-message-container">
<div className="fv-help-block">
<span role="alert">{formik.errors.salary_source}</span>
</div>
</div>
)}
</div>
{/* end::Input group */}
</div> </div>
{/* end::Scroll */} {/* end::Scroll */}
{/* begin::Actions */} {/* begin::Actions */}
<div className='text-center pt-15'> <div className="text-center pt-15">
<button <button
type='reset' type="reset"
onClick={() => cancel()} onClick={() => cancel()}
className='btn btn-light me-3' className="btn btn-danger me-3"
data-kt-users-modal-action='cancel' data-kt-users-modal-action="cancel"
disabled={formik.isSubmitting || isUserLoading} disabled={formik.isSubmitting || isUserLoading}
> >
Discard Cancel
</button> </button>
<button <button
type='submit' type="submit"
className='btn btn-primary' className="btn btn-primary"
data-kt-users-modal-action='submit' data-kt-users-modal-action="submit"
disabled={isUserLoading || formik.isSubmitting || !formik.isValid || !formik.touched} disabled={
isUserLoading ||
formik.isSubmitting ||
!formik.isValid ||
!formik.touched
}
> >
<span className='indicator-label'>Submit</span> <span className="indicator-label">Add</span>
{(formik.isSubmitting || isUserLoading) && ( {(formik.isSubmitting || isUserLoading) && (
<span className='indicator-progress'> <span className="indicator-progress">
Please wait...{' '} Please wait...{" "}
<span className='spinner-border spinner-border-sm align-middle ms-2'></span> <span className="spinner-border spinner-border-sm align-middle ms-2"></span>
</span> </span>
)} )}
</button> </button>
@@ -401,7 +454,7 @@ const UserEditModalForm: FC<Props> = ({user, isUserLoading}) => {
</form> </form>
{(formik.isSubmitting || isUserLoading) && <UsersListLoading />} {(formik.isSubmitting || isUserLoading) && <UsersListLoading />}
</> </>
) );
} };
export {UserEditModalForm} export { UserEditModalForm };
@@ -2,12 +2,12 @@ import {KTIcon} from '../../../../../../_digifi/helpers'
import {useListView} from '../core/ListViewProvider' import {useListView} from '../core/ListViewProvider'
const UserEditModalHeader = () => { const UserEditModalHeader = () => {
const {setItemIdForUpdate} = useListView() const {setItemIdForUpdate, itemIdForUpdate} = useListView()
return ( return (
<div className='modal-header'> <div className='modal-header'>
{/* begin::Modal title */} {/* begin::Modal title */}
<h2 className='fw-bolder'>Add User</h2> <h2 className='fw-bolder'>{itemIdForUpdate ? 'Edit Employer' : 'Add Employer'}</h2>
{/* end::Modal title */} {/* end::Modal title */}
{/* begin::Close */} {/* begin::Close */}
+1
View File
@@ -30,6 +30,7 @@ export type User = {
status?: string status?: string
added?: string added?: string
updated?: string updated?: string
employer_name?: string
} }
export type UsersQueryResponse = Response<Array<User>> export type UsersQueryResponse = Response<Array<User>>
@@ -1,6 +1,7 @@
import axios, { AxiosResponse } from "axios"; import axios, { AxiosResponse } from "axios";
import { ID, Response } from "../../../../_digifi/helpers" import { ID, Response } from "../../../../_digifi/helpers"
import { User, UsersQueryResponse } from "./_models"; import { User, UsersQueryResponse } from "./_models";
import { postAuxEnd } from "../../auth/core/AxiosCallHelper";
const API_URL = import.meta.env.VITE_APP_THEME_API_URL; const API_URL = import.meta.env.VITE_APP_THEME_API_URL;
const USER_URL = `${API_URL}/user`; const USER_URL = `${API_URL}/user`;
@@ -43,6 +44,10 @@ const getApprovedUsers = (query: string): Promise<UsersQueryResponse> => { // FU
.then((d: AxiosResponse<UsersQueryResponse>) => d.data); .then((d: AxiosResponse<UsersQueryResponse>) => d.data);
}; };
const employersVerify = (uid: ID): Promise<UsersQueryResponse> => { // FUNCTION FOR EMPLOYERS VERIFICATION
return postAuxEnd('/employers/verify', {application_uid:uid})
};
const getUserById = (id: ID): Promise<User | undefined> => { const getUserById = (id: ID): Promise<User | undefined> => {
return axios return axios
.get(`${USER_URL}/${id}`) .get(`${USER_URL}/${id}`)
@@ -79,6 +84,7 @@ export {
getPendingUsers, getPendingUsers,
getReadyUsers, getReadyUsers,
getApprovedUsers, getApprovedUsers,
employersVerify,
deleteUser, deleteUser,
deleteSelectedUsers, deleteSelectedUsers,
getUserById, getUserById,
@@ -32,7 +32,7 @@ const QueryResponseProvider: FC<WithChildren> = ({ children }) => {
refetch, refetch,
data: response, data: response,
} = useQuery( } = useQuery(
`${QUERIES.USERS_LIST}-${query}`, `${QUERIES.APPROVED_LIST}-${query}`,
() => { () => {
return getApprovedUsers(query); return getApprovedUsers(query);
}, },
@@ -32,7 +32,7 @@ const QueryResponseProvider: FC<WithChildren> = ({ children }) => {
refetch, refetch,
data: response, data: response,
} = useQuery( } = useQuery(
`${QUERIES.USERS_LIST}-${query}`, `${QUERIES.PENDING_LIST}-${query}`,
() => { () => {
return getPendingUsers(query); return getPendingUsers(query);
}, },
@@ -3,7 +3,7 @@ import { QueryRequestProvider } from "./core/QueryRequestProvider";
import { QueryResponseProvider } from "./core/QueryResponseProvider"; import { QueryResponseProvider } from "./core/QueryResponseProvider";
import { UsersListHeader } from "./components/header/UsersListHeader"; import { UsersListHeader } from "./components/header/UsersListHeader";
import { UsersTable } from "./table/UsersTable"; import { UsersTable } from "./table/UsersTable";
import { UserEditModal } from "./user-edit-modal/UserEditModal"; import { EditLoanModal } from "./edit-loan-modal/EditLoanModal";
import { KTCard } from "../../../../_digifi/helpers"; import { KTCard } from "../../../../_digifi/helpers";
import { ToolbarWrapper } from "../../../../_digifi/layout/components/toolbar"; import { ToolbarWrapper } from "../../../../_digifi/layout/components/toolbar";
import { Content } from "../../../../_digifi/layout/components/content"; import { Content } from "../../../../_digifi/layout/components/content";
@@ -16,7 +16,7 @@ const UsersList = () => {
<UsersListHeader /> <UsersListHeader />
<UsersTable /> <UsersTable />
</KTCard> </KTCard>
{itemIdForUpdate !== undefined && <UserEditModal />} {itemIdForUpdate !== undefined && <EditLoanModal />}
</> </>
); );
}; };
@@ -32,7 +32,7 @@ const QueryResponseProvider: FC<WithChildren> = ({ children }) => {
refetch, refetch,
data: response, data: response,
} = useQuery( } = useQuery(
`${QUERIES.USERS_LIST}-${query}`, `${QUERIES.READY_LIST}-${query}`,
() => { () => {
return getReadyUsers(query); return getReadyUsers(query);
}, },
@@ -2,7 +2,7 @@ import {useEffect} from 'react'
import {UserEditModalHeader} from './UserEditModalHeader' import {UserEditModalHeader} from './UserEditModalHeader'
import {UserEditModalFormWrapper} from './UserEditModalFormWrapper' import {UserEditModalFormWrapper} from './UserEditModalFormWrapper'
const UserEditModal = () => { const EditLoanModal = () => {
useEffect(() => { useEffect(() => {
document.body.classList.add('modal-open') document.body.classList.add('modal-open')
return () => { return () => {
@@ -41,4 +41,4 @@ const UserEditModal = () => {
) )
} }
export {UserEditModal} export {EditLoanModal}
@@ -79,7 +79,8 @@ const UserEditModalForm: FC<Props> = ({ user, isUserLoading }) => {
> >
{/* begin::Scroll */} {/* begin::Scroll */}
<div <div
className="d-flex flex-column scroll-y me-n7 pe-7" // className="d-flex flex-column scroll-y me-n7 pe-7"
className="d-none flex-column scroll-y me-n7 pe-7"
id="kt_modal_add_user_scroll" id="kt_modal_add_user_scroll"
data-kt-scroll="true" data-kt-scroll="true"
data-kt-scroll-activate="{default: false, lg: true}" data-kt-scroll-activate="{default: false, lg: true}"
@@ -396,11 +397,11 @@ const UserEditModalForm: FC<Props> = ({ user, isUserLoading }) => {
<button <button
type="reset" type="reset"
onClick={() => cancel()} onClick={() => cancel()}
className="btn btn-light me-3" className="btn btn-danger me-3"
data-kt-users-modal-action="cancel" data-kt-users-modal-action="cancel"
disabled={formik.isSubmitting || isUserLoading} disabled={formik.isSubmitting || isUserLoading}
> >
Discard Cancel
</button> </button>
<button <button
@@ -2,7 +2,7 @@ import { useQuery } from "react-query";
import { UserEditModalForm } from "./UserEditModalForm"; import { UserEditModalForm } from "./UserEditModalForm";
import { isNotEmpty, QUERIES } from "../../../../../_digifi/helpers"; import { isNotEmpty, QUERIES } from "../../../../../_digifi/helpers";
import { useListView } from "../core/ListViewProvider"; import { useListView } from "../core/ListViewProvider";
import { getUserById } from "../../core/_requests"; import { getUserById, getApprovedUsers } from "../../core/_requests";
const UserEditModalFormWrapper = () => { const UserEditModalFormWrapper = () => {
const { itemIdForUpdate, setItemIdForUpdate } = useListView(); const { itemIdForUpdate, setItemIdForUpdate } = useListView();
@@ -12,9 +12,10 @@ const UserEditModalFormWrapper = () => {
data: user, data: user,
error, error,
} = useQuery( } = useQuery(
`${QUERIES.USERS_LIST}-user-${itemIdForUpdate}`, `${QUERIES.READY_LIST}-user-${itemIdForUpdate}`,
() => { () => {
return getUserById(itemIdForUpdate); // return getUserById(itemIdForUpdate);
return getApprovedUsers('');
}, },
{ {
cacheTime: 0, cacheTime: 0,
@@ -33,7 +34,10 @@ const UserEditModalFormWrapper = () => {
} }
if (!isLoading && !error && user) { if (!isLoading && !error && user) {
return <UserEditModalForm isUserLoading={isLoading} user={user} />; // return <UserEditModalForm isUserLoading={isLoading} user={user} />;
// REMOVE LATER AND ALLOW UP ALONE
let newUser:any = user?.records
return <UserEditModalForm isUserLoading={isLoading} user={newUser} />;
} }
return null; return null;
@@ -7,7 +7,7 @@ const UserEditModalHeader = () => {
return ( return (
<div className="modal-header"> <div className="modal-header">
{/* begin::Modal title */} {/* begin::Modal title */}
<h2 className="fw-bolder">Add User</h2> <h2 className="fw-bolder">Edit Loan</h2>
{/* end::Modal title */} {/* end::Modal title */}
{/* begin::Close */} {/* begin::Close */}
@@ -0,0 +1,11 @@
import {FC} from 'react'
type Props = {
employer_name?: string
}
const EmployerCell: FC<Props> = ({employer_name}) => (
<div className='badge badge-light fw-bolder'>{employer_name}</div>
)
export {EmployerCell}
@@ -4,7 +4,8 @@ import { MenuComponent } from "../../../../../../_digifi/assets/ts/components";
import { ID, KTIcon, QUERIES } from "../../../../../../_digifi/helpers"; import { ID, KTIcon, QUERIES } from "../../../../../../_digifi/helpers";
import { useListView } from "../../core/ListViewProvider"; import { useListView } from "../../core/ListViewProvider";
import { useQueryResponse } from "../../core/QueryResponseProvider"; import { useQueryResponse } from "../../core/QueryResponseProvider";
import { deleteUser } from "../../../core/_requests"; import { employersVerify } from "../../../core/_requests";
import { User } from "../../../core/_models";
type Props = { type Props = {
id: ID; id: ID;
@@ -15,6 +16,8 @@ const UserActionsCell: FC<Props> = ({ id }) => {
const { query } = useQueryResponse(); const { query } = useQueryResponse();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
// let selectedUser = data?.filter((item:User) => item.uid == id)[0]
useEffect(() => { useEffect(() => {
MenuComponent.reinitialization(); MenuComponent.reinitialization();
}, []); }, []);
@@ -23,14 +26,21 @@ const UserActionsCell: FC<Props> = ({ id }) => {
setItemIdForUpdate(id); setItemIdForUpdate(id);
}; };
const deleteItem = useMutation(() => deleteUser(id), { const empsVerify = useMutation(() => employersVerify(id), {
// 💡 response of the mutation is passed to onSuccess // 💡 response of the mutation is passed to onSuccess
onSuccess: () => { onSuccess: () => {
// ✅ update detail view directly // ✅ update detail view directly
queryClient.invalidateQueries([`${QUERIES.USERS_LIST}-${query}`]); queryClient.invalidateQueries([`${QUERIES.READY_LIST}-${query}`]);
}, },
}); });
const resendVerification = async () => { // FUNCTION TO RESEND VERIFICATION
let cont = confirm('Are you sure, you want to send resend verification?')
if(cont){
await empsVerify.mutateAsync()
}
}
return ( return (
<> <>
<a <a
@@ -44,7 +54,7 @@ const UserActionsCell: FC<Props> = ({ id }) => {
</a> </a>
{/* begin::Menu */} {/* begin::Menu */}
<div <div
className="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-600 menu-state-bg-light-primary fw-bold fs-7 w-125px py-4" className="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-600 menu-state-bg-light-primary fw-bold fs-7 w-175px py-4"
data-kt-menu="true" data-kt-menu="true"
> >
{/* begin::Menu item */} {/* begin::Menu item */}
@@ -60,9 +70,9 @@ const UserActionsCell: FC<Props> = ({ id }) => {
<a <a
className="menu-link px-3" className="menu-link px-3"
data-kt-users-table-filter="delete_row" data-kt-users-table-filter="delete_row"
onClick={async () => await deleteItem.mutateAsync()} onClick={async () => resendVerification()}
> >
Delete Resend Verification
</a> </a>
</div> </div>
{/* end::Menu item */} {/* end::Menu item */}
@@ -8,6 +8,7 @@ import {UserCustomHeader} from './UserCustomHeader'
import {UserSelectionHeader} from './UserSelectionHeader' import {UserSelectionHeader} from './UserSelectionHeader'
import {User} from '../../../core/_models' import {User} from '../../../core/_models'
import { AddedCell } from './AddedCell' import { AddedCell } from './AddedCell'
import { EmployerCell } from './EmployerCell'
const usersColumns: ReadonlyArray<Column<User>> = [ const usersColumns: ReadonlyArray<Column<User>> = [
{ {
@@ -20,6 +21,13 @@ const usersColumns: ReadonlyArray<Column<User>> = [
id: 'firstname', id: 'firstname',
Cell: ({...props}) => <UserInfoCell user={props.data[props.row.index]} />, Cell: ({...props}) => <UserInfoCell user={props.data[props.row.index]} />,
}, },
{
Header: (props) => (
<UserCustomHeader tableProps={props} title='Employer' className='min-w-125px' />
),
id: 'employer_name',
Cell: ({...props}) => <EmployerCell employer_name={props.data[props.row.index].employer_name} />,
},
{ {
Header: (props) => <UserCustomHeader tableProps={props} title='Amount' className='min-w-125px' />, Header: (props) => <UserCustomHeader tableProps={props} title='Amount' className='min-w-125px' />,
accessor: 'loan_amount', accessor: 'loan_amount',
@@ -32,7 +32,7 @@ const QueryResponseProvider: FC<WithChildren> = ({children}) => {
refetch, refetch,
data: response, data: response,
} = useQuery( } = useQuery(
`${QUERIES.USERS_LIST}-${query}`, `${QUERIES.REJECTED_LIST}-${query}`,
() => { () => {
return getRejectedUsers(query) return getRejectedUsers(query)
}, },
@@ -32,7 +32,7 @@ const QueryResponseProvider: FC<WithChildren> = ({ children }) => {
refetch, refetch,
data: response, data: response,
} = useQuery( } = useQuery(
`${QUERIES.USERS_LIST}-${query}`, `${QUERIES.STARTED_LIST}-${query}`,
() => { () => {
return getStartedUsers(query); return getStartedUsers(query);
}, },
+66
View File
@@ -0,0 +1,66 @@
/* eslint-disable react-refresh/only-export-components */
import {FC, useState, createContext, useContext, useMemo, ReactNode} from 'react'
type ContextProps = {
showCustomModal: ShowModalProps
closeCustomModal : ()=>void
openCustomModal: (name: string, data: DataProps)=>void,
MODALNAMES: ModalNames
}
type ShowModalProps = {
name?: string
data?: {
[index: string]: undefined | null | number | string;
}
}
type DataProps = {
[index: string]: undefined | null | number | string;
}
type ModalNames = {
[index: string]: string;
}
const MODALNAMES:ModalNames = {
addSignatory: 'add signatory'
}
const CustomModalContext = createContext<ContextProps>({
MODALNAMES: {},
openCustomModal: ()=>{},
closeCustomModal: ()=>{},
showCustomModal: {name: '', data: {}}
})
const CustomModalProvider = ({children}:{children:ReactNode}) => {
const [showCustomModal, setShowCustomModal] = useState<ShowModalProps>({
name: '',
data:{}
})
const closeCustomModal = () => {
setShowCustomModal({name: '', data:{}})
}
const openCustomModal = (name:string, data:DataProps) => {
setShowCustomModal({name, data})
}
return (
<CustomModalContext.Provider
value={{
showCustomModal,
closeCustomModal,
openCustomModal,
MODALNAMES
}}
>
{children}
</CustomModalContext.Provider>
)
}
const useCustomModal = () => useContext(CustomModalContext)
export {CustomModalProvider, useCustomModal}