Compare commits

...

13 Commits

21 changed files with 112 additions and 30 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,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 */}
@@ -38,7 +38,7 @@ const editUserSchema = Yup.object().shape({
const ModalForm: FC<Props> = ({ user, isUserLoading }) => { const ModalForm: FC<Props> = ({ user, isUserLoading }) => {
const {closeCustomModal} = useCustomModal() const {closeCustomModal, showCustomModal} = useCustomModal()
const { setItemIdForUpdate } = useListView(); const { setItemIdForUpdate } = useListView();
const { refetch, isLoading } = useQueryResponse(); const { refetch, isLoading } = useQueryResponse();
@@ -72,7 +72,7 @@ const ModalForm: FC<Props> = ({ user, isUserLoading }) => {
if (isNotEmpty(values.id)) { if (isNotEmpty(values.id)) {
await updateUser(values); await updateUser(values);
} else { } else {
await createSignatory(values); await createSignatory({employer_uid:showCustomModal?.data?.id, ...values});
} }
} catch (ex) { } catch (ex) {
console.error(ex); console.error(ex);
@@ -61,7 +61,7 @@ const UserActionsCell: FC<Props> = ({id}) => {
{/* 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={()=>{openCustomModal(MODALNAMES.addSignatory, {})}}> <a className='menu-link px-3' onClick={()=>{openCustomModal(MODALNAMES.addSignatory, {id})}}>
Add Signatory Add Signatory
</a> </a>
</div> </div>
+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);
}, },
+2 -2
View File
@@ -11,12 +11,12 @@ type ContextProps = {
type ShowModalProps = { type ShowModalProps = {
name?: string name?: string
data?: { data?: {
[index: string]: string | number | undefined; [index: string]: undefined | null | number | string;
} }
} }
type DataProps = { type DataProps = {
[index: string]: string | number | undefined; [index: string]: undefined | null | number | string;
} }
type ModalNames = { type ModalNames = {