Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f9e3674ece | |||
| 3b6b0b580c | |||
| 50b12f2da4 | |||
| 19198b09ba | |||
| 22894a62d8 | |||
| 5ad9413dc1 | |||
| 7449dea29c | |||
| 886c7dd8b3 | |||
| fb6dc18073 | |||
| 7d955381c7 | |||
| 8df8fa45db | |||
| 4e60059290 | |||
| c6fe8c7d5a | |||
| 5eb07b2057 | |||
| 3a35a34266 | |||
| de5cb74241 | |||
| c711e000b3 | |||
| 0a28d478d8 | |||
| 0d9318ddd9 | |||
| c53c37611a | |||
| f7d82c0958 |
@@ -1,5 +1,7 @@
|
||||
const QUERIES = {
|
||||
USERS_LIST: 'users-list',
|
||||
EMPLOYERS_LIST: 'employers-list',
|
||||
SIGNATORY_LIST: 'signatory-list',
|
||||
}
|
||||
|
||||
export {QUERIES}
|
||||
|
||||
@@ -22,8 +22,11 @@ export type SearchState = {
|
||||
}
|
||||
|
||||
export type Response<T> = {
|
||||
call_return? : string | any
|
||||
data?: T
|
||||
records?: Array<any>
|
||||
records?: T
|
||||
salary_sources?: Array<any>
|
||||
employer_sector?: Array<any>
|
||||
payload?: {
|
||||
message?: string
|
||||
errors?: {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export const formatNumbers = (number: string | undefined): string | null => {
|
||||
if(!number){
|
||||
return null
|
||||
}
|
||||
return number.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
};
|
||||
@@ -1,18 +1,23 @@
|
||||
import clsx from 'clsx'
|
||||
import {KTIcon, toAbsoluteUrl} from '../../../helpers'
|
||||
import {HeaderNotificationsMenu, HeaderUserMenu, Search, ThemeModeSwitcher} from '../../../partials'
|
||||
import {useLayout} from '../../core'
|
||||
import clsx from 'clsx';
|
||||
import { KTIcon, toAbsoluteUrl } from '../../../helpers';
|
||||
import {
|
||||
HeaderNotificationsMenu,
|
||||
HeaderUserMenu,
|
||||
Search,
|
||||
ThemeModeSwitcher,
|
||||
} from '../../../partials';
|
||||
import { useLayout } from '../../core';
|
||||
|
||||
const itemClass = 'ms-1 ms-md-4'
|
||||
const itemClass = 'ms-1 ms-md-4';
|
||||
const btnClass =
|
||||
'btn btn-icon btn-custom btn-icon-muted btn-active-light btn-active-color-primary w-35px h-35px'
|
||||
const userAvatarClass = 'symbol-35px'
|
||||
const btnIconClass = 'fs-2'
|
||||
'btn btn-icon btn-custom btn-icon-muted btn-active-light btn-active-color-primary w-35px h-35px';
|
||||
const userAvatarClass = 'symbol-35px';
|
||||
const btnIconClass = 'fs-2';
|
||||
|
||||
const Navbar = () => {
|
||||
const {config} = useLayout()
|
||||
const { config } = useLayout();
|
||||
return (
|
||||
<div className='app-navbar flex-shrink-0'>
|
||||
<div className="app-navbar flex-shrink-0">
|
||||
{/* <div className={clsx('app-navbar-item align-items-stretch', itemClass)}>
|
||||
<Search />
|
||||
</div> */}
|
||||
@@ -43,33 +48,38 @@ const Navbar = () => {
|
||||
</div> */}
|
||||
|
||||
<div className={clsx('app-navbar-item', itemClass)}>
|
||||
<ThemeModeSwitcher toggleBtnClass={clsx('btn-active-light-primary btn-custom')} />
|
||||
<ThemeModeSwitcher
|
||||
toggleBtnClass={clsx('btn-active-light-primary btn-custom')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={clsx('app-navbar-item', itemClass)}>
|
||||
<div
|
||||
className={clsx('cursor-pointer symbol', userAvatarClass)}
|
||||
data-kt-menu-trigger="{default: 'click'}"
|
||||
data-kt-menu-attach='parent'
|
||||
data-kt-menu-placement='bottom-end'
|
||||
data-kt-menu-attach="parent"
|
||||
data-kt-menu-placement="bottom-end"
|
||||
>
|
||||
<img src={toAbsoluteUrl('media/avatars/300-3.jpg')} alt='' />
|
||||
<img src={toAbsoluteUrl('media/avatars/300-3.jpg')} alt="" />
|
||||
</div>
|
||||
<HeaderUserMenu />
|
||||
</div>
|
||||
|
||||
{config.app?.header?.default?.menu?.display && (
|
||||
<div className='app-navbar-item d-lg-none ms-2 me-n3' title='Show header menu'>
|
||||
<div
|
||||
className="app-navbar-item d-lg-none ms-2 me-n3"
|
||||
title="Show header menu"
|
||||
>
|
||||
<div
|
||||
className='btn btn-icon btn-active-color-primary w-35px h-35px'
|
||||
id='kt_app_header_menu_toggle'
|
||||
className="btn btn-icon btn-active-color-primary w-35px h-35px"
|
||||
id="kt_app_header_menu_toggle"
|
||||
>
|
||||
<KTIcon iconName='text-align-left' className={btnIconClass} />
|
||||
<KTIcon iconName="text-align-left" className={btnIconClass} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export {Navbar}
|
||||
export { Navbar };
|
||||
|
||||
@@ -17,7 +17,7 @@ const SidebarMenuMain = () => {
|
||||
{/*<SidebarMenuItem to='/builder' icon='switch' title='Layout Builder' fontIcon='bi-layers' />*/}
|
||||
<div className='menu-item'>
|
||||
<div className='menu-content pt-8 pb-2'>
|
||||
<span className='menu-section text-muted text-uppercase fs-8 ls-1'>Crafted</span>
|
||||
<span className='menu-section text-muted text-uppercase fs-8 ls-1'>Loan</span>
|
||||
</div>
|
||||
</div>
|
||||
<SidebarMenuItemWithSub
|
||||
@@ -87,7 +87,7 @@ const SidebarMenuMain = () => {
|
||||
|
||||
<div className='menu-item'>
|
||||
<div className='menu-content pt-8 pb-2'>
|
||||
<span className='menu-section text-muted text-uppercase fs-8 ls-1'>Apps</span>
|
||||
<span className='menu-section text-muted text-uppercase fs-8 ls-1'>Tools</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -103,11 +103,30 @@ const SidebarMenuMain = () => {
|
||||
</SidebarMenuItemWithSub> */}
|
||||
|
||||
<SidebarMenuItem
|
||||
to='/apps/user-management/users'
|
||||
to='/tools/user-management/users'
|
||||
icon='abstract-28'
|
||||
title='User management'
|
||||
fontIcon='bi-layers'
|
||||
/>
|
||||
|
||||
|
||||
<div className='menu-item'>
|
||||
<div className='menu-content pt-8 pb-2'>
|
||||
<span className='menu-section text-muted text-uppercase fs-8 ls-1'>Employers</span>
|
||||
</div>
|
||||
</div>
|
||||
<SidebarMenuItem
|
||||
to='employers/employerslist'
|
||||
icon='abstract-28'
|
||||
title='List'
|
||||
fontIcon='bi-layers'
|
||||
/>
|
||||
<SidebarMenuItem
|
||||
to='employers/signatory'
|
||||
icon='abstract-28'
|
||||
title='Signatory'
|
||||
fontIcon='bi-layers'
|
||||
/>
|
||||
{/*<div className='menu-item'>*/}
|
||||
{/* <a*/}
|
||||
{/* target='_blank'*/}
|
||||
|
||||
@@ -1,64 +1,63 @@
|
||||
|
||||
import {FC} from 'react'
|
||||
import {Link} from 'react-router-dom'
|
||||
import {useAuth} from '../../../../app/modules/auth'
|
||||
import {Languages} from './Languages'
|
||||
import {toAbsoluteUrl} from '../../../helpers'
|
||||
import { FC } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useAuth } from '../../../../app/modules/auth';
|
||||
import { Languages } from './Languages';
|
||||
import { toAbsoluteUrl } from '../../../helpers';
|
||||
|
||||
const HeaderUserMenu: FC = () => {
|
||||
const {currentUser, logout} = useAuth()
|
||||
const { currentUser, logout } = useAuth();
|
||||
return (
|
||||
<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'
|
||||
data-kt-menu='true'
|
||||
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"
|
||||
data-kt-menu="true"
|
||||
>
|
||||
<div className='menu-item px-3'>
|
||||
<div className='menu-content d-flex align-items-center px-3'>
|
||||
<div className='symbol symbol-50px me-5'>
|
||||
<img alt='Logo' src={toAbsoluteUrl('media/avatars/300-3.jpg')} />
|
||||
<div className="menu-item px-3">
|
||||
<div className="menu-content d-flex align-items-center px-3">
|
||||
<div className="symbol symbol-50px me-5">
|
||||
<img alt="Logo" src={toAbsoluteUrl('media/avatars/300-3.jpg')} />
|
||||
</div>
|
||||
|
||||
<div className='d-flex flex-column'>
|
||||
<div className='fw-bolder d-flex align-items-center fs-5'>
|
||||
<div className="d-flex flex-column">
|
||||
<div className="fw-bolder d-flex align-items-center fs-5">
|
||||
{currentUser?.first_name} {currentUser?.first_name}
|
||||
{/*<span className='badge badge-light-success fw-bolder fs-8 px-2 py-1 ms-2'>Pro</span>*/}
|
||||
</div>
|
||||
<a href='#' className='fw-bold text-muted text-hover-primary fs-7'>
|
||||
<a href="#" className="fw-bold text-muted text-hover-primary fs-7">
|
||||
{currentUser?.email}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='separator my-2'></div>
|
||||
{/* <div className="separator my-2"></div> */}
|
||||
|
||||
<div className='menu-item px-5'>
|
||||
{/* <div className='menu-item px-5'>
|
||||
<Link to={'/crafted/pages/profile'} className='menu-link px-5'>
|
||||
My Profile
|
||||
</Link>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div className='menu-item px-5'>
|
||||
{/* <div className='menu-item px-5'>
|
||||
<a href='#' className='menu-link px-5'>
|
||||
<span className='menu-text'>My Projects</span>
|
||||
<span className='menu-badge'>
|
||||
<span className='badge badge-light-danger badge-circle fw-bolder fs-7'>3</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div
|
||||
className='menu-item px-5'
|
||||
data-kt-menu-trigger='hover'
|
||||
data-kt-menu-placement='left-start'
|
||||
data-kt-menu-flip='bottom'
|
||||
{/* <div
|
||||
className="menu-item px-5"
|
||||
data-kt-menu-trigger="hover"
|
||||
data-kt-menu-placement="left-start"
|
||||
data-kt-menu-flip="bottom"
|
||||
>
|
||||
<a href='#' className='menu-link px-5'>
|
||||
<span className='menu-title'>My Subscription</span>
|
||||
<span className='menu-arrow'></span>
|
||||
</a>
|
||||
|
||||
<div className='menu-sub menu-sub-dropdown w-175px py-4'>
|
||||
<div className="menu-sub menu-sub-dropdown w-175px py-4">
|
||||
<div className='menu-item px-3'>
|
||||
<a href='#' className='menu-link px-5'>
|
||||
Referrals
|
||||
@@ -88,7 +87,7 @@ const HeaderUserMenu: FC = () => {
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className='separator my-2'></div>
|
||||
<div className="separator my-2"></div>
|
||||
|
||||
<div className='menu-item px-3'>
|
||||
<div className='menu-content px-3'>
|
||||
@@ -105,31 +104,31 @@ const HeaderUserMenu: FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div className='menu-item px-5'>
|
||||
{/* <div className='menu-item px-5'>
|
||||
<a href='#' className='menu-link px-5'>
|
||||
My Statements
|
||||
</a>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div className='separator my-2'></div>
|
||||
<div className="separator my-2"></div>
|
||||
|
||||
<Languages />
|
||||
{/* <Languages /> */}
|
||||
|
||||
<div className='menu-item px-5 my-1'>
|
||||
{/* <div className='menu-item px-5 my-1'>
|
||||
<Link to='/crafted/account/settings' className='menu-link px-5'>
|
||||
Account Settings
|
||||
</Link>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div className='menu-item px-5'>
|
||||
<a onClick={logout} className='menu-link px-5'>
|
||||
<div className="menu-item px-5">
|
||||
<a onClick={logout} className="menu-link px-5">
|
||||
Sign Out
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export {HeaderUserMenu}
|
||||
export { HeaderUserMenu };
|
||||
|
||||
@@ -5,7 +5,7 @@ import {UsersListWrapper} from './users-list/UsersList'
|
||||
const usersBreadcrumbs: Array<PageLink> = [
|
||||
{
|
||||
title: 'User Management',
|
||||
path: '/apps/user-management/users',
|
||||
path: '/tools/user-management/users',
|
||||
isSeparator: false,
|
||||
isActive: false,
|
||||
},
|
||||
@@ -31,7 +31,7 @@ const UsersPage = () => {
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
<Route index element={<Navigate to='/apps/user-management/users' />} />
|
||||
<Route index element={<Navigate to='/tools/user-management/users' />} />
|
||||
</Routes>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import {Route, Routes, Outlet, Navigate} from 'react-router-dom'
|
||||
import {PageLink, PageTitle} from '../../../../_digifi/layout/core'
|
||||
import {UsersListWrapper} from './users-list/UsersList'
|
||||
import {UsersListWrapper as SignatoryList} from './signatory-list/UsersList'
|
||||
|
||||
const usersBreadcrumbs: Array<PageLink> = [
|
||||
{
|
||||
title: 'Employer Management',
|
||||
path: '/employers/employerslist',
|
||||
isSeparator: false,
|
||||
isActive: false,
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
path: '',
|
||||
isSeparator: true,
|
||||
isActive: false,
|
||||
},
|
||||
]
|
||||
|
||||
const EmployersPage = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route element={<Outlet />}>
|
||||
<Route
|
||||
path='employerslist'
|
||||
element={
|
||||
<>
|
||||
<PageTitle breadcrumbs={usersBreadcrumbs}>Employers list</PageTitle>
|
||||
<UsersListWrapper />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='signatory'
|
||||
element={
|
||||
<>
|
||||
<PageTitle breadcrumbs={usersBreadcrumbs}>Signatory list</PageTitle>
|
||||
<SignatoryList />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
<Route index element={<Navigate to='/employers/employerslist' />} />
|
||||
</Routes>
|
||||
)
|
||||
}
|
||||
|
||||
export default EmployersPage
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
import {ListViewProvider, useListView} from './core/ListViewProvider'
|
||||
import {QueryRequestProvider} from './core/QueryRequestProvider'
|
||||
import {QueryResponseProvider} from './core/QueryResponseProvider'
|
||||
import {QueryResponseProvider, useAllResponse} from './core/QueryResponseProvider'
|
||||
import {UsersListHeader} from './components/header/UsersListHeader'
|
||||
import {UsersTable} from './table/UsersTable'
|
||||
import {UserEditModal} from './user-edit-modal/UserEditModal'
|
||||
@@ -9,6 +9,8 @@ import { ToolbarWrapper } from '../../../../../_digifi/layout/components/toolbar
|
||||
import { Content } from '../../../../../_digifi/layout/components/content'
|
||||
|
||||
const UsersList = () => {
|
||||
const response = useAllResponse()
|
||||
console.log('RESPONSE', response)
|
||||
const {itemIdForUpdate} = useListView()
|
||||
return (
|
||||
<>
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import {useEffect} from 'react'
|
||||
import {AddSignatoryModalHeader} from './AddSignatoryModalHeader'
|
||||
import {AddSignatoryModalFormWrapper} from './AddSignatoryModalFormWrapper'
|
||||
|
||||
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'>
|
||||
<AddSignatoryModalHeader />
|
||||
{/* begin::Modal body */}
|
||||
<div className='modal-body scroll-y mx-5 mx-xl-15 my-7'>
|
||||
<AddSignatoryModalFormWrapper />
|
||||
</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}
|
||||
+2
-2
@@ -26,7 +26,7 @@ const editUserSchema = Yup.object().shape({
|
||||
.required('Name is required'),
|
||||
})
|
||||
|
||||
const UserEditModalForm: FC<Props> = ({user, isUserLoading}) => {
|
||||
const AddSignatoryModalForm: FC<Props> = ({user, isUserLoading}) => {
|
||||
const {setItemIdForUpdate} = useListView()
|
||||
const {refetch} = useQueryResponse()
|
||||
|
||||
@@ -404,4 +404,4 @@ const UserEditModalForm: FC<Props> = ({user, isUserLoading}) => {
|
||||
)
|
||||
}
|
||||
|
||||
export {UserEditModalForm}
|
||||
export {AddSignatoryModalForm}
|
||||
+5
-5
@@ -1,10 +1,10 @@
|
||||
import {useQuery} from 'react-query'
|
||||
import {UserEditModalForm} from './UserEditModalForm'
|
||||
import {AddSignatoryModalForm} from './AddSignatoryModalForm'
|
||||
import {isNotEmpty, QUERIES} from '../../../../../../_digifi/helpers'
|
||||
import {useListView} from '../core/ListViewProvider'
|
||||
import {getUserById} from '../core/_requests'
|
||||
|
||||
const UserEditModalFormWrapper = () => {
|
||||
const AddSignatoryModalFormWrapper = () => {
|
||||
const {itemIdForUpdate, setItemIdForUpdate} = useListView()
|
||||
const enabledQuery: boolean = isNotEmpty(itemIdForUpdate)
|
||||
const {
|
||||
@@ -27,14 +27,14 @@ const UserEditModalFormWrapper = () => {
|
||||
)
|
||||
|
||||
if (!itemIdForUpdate) {
|
||||
return <UserEditModalForm isUserLoading={isLoading} user={{id: undefined}} />
|
||||
return <AddSignatoryModalForm isUserLoading={isLoading} user={{id: undefined}} />
|
||||
}
|
||||
|
||||
if (!isLoading && !error && user) {
|
||||
return <UserEditModalForm isUserLoading={isLoading} user={user} />
|
||||
return <AddSignatoryModalForm isUserLoading={isLoading} user={user} />
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export {UserEditModalFormWrapper}
|
||||
export {AddSignatoryModalFormWrapper}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import {KTIcon} from '../../../../../../_digifi/helpers'
|
||||
import {useListView} from '../core/ListViewProvider'
|
||||
|
||||
const UserEditModalHeader = () => {
|
||||
const AddSignatoryModalHeader = () => {
|
||||
const {setItemIdForUpdate} = useListView()
|
||||
|
||||
return (
|
||||
@@ -24,4 +24,4 @@ const UserEditModalHeader = () => {
|
||||
)
|
||||
}
|
||||
|
||||
export {UserEditModalHeader}
|
||||
export {AddSignatoryModalHeader}
|
||||
+5
-5
@@ -10,9 +10,7 @@ const UsersListToolbar = () => {
|
||||
|
||||
return (
|
||||
<div className='d-flex justify-content-end' data-kt-user-table-toolbar='base'>
|
||||
<UsersListFilter />
|
||||
|
||||
{/* begin::Export */}
|
||||
{/* begin::Export */}
|
||||
{/* <button type='button' className='btn btn-light-primary me-3'>
|
||||
<KTIcon iconName='exit-up' className='fs-2' />
|
||||
Export
|
||||
@@ -20,11 +18,13 @@ const UsersListToolbar = () => {
|
||||
{/* end::Export */}
|
||||
|
||||
{/* begin::Add user */}
|
||||
{/* <button type='button' className='btn btn-primary' onClick={openAddUserModal}>
|
||||
{/* <button type='button' className='btn btn-primary me-3' onClick={openAddUserModal}>
|
||||
<KTIcon iconName='plus' className='fs-2' />
|
||||
Add User
|
||||
Add New
|
||||
</button> */}
|
||||
{/* end::Add user */}
|
||||
|
||||
<UsersListFilter />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+12
-4
@@ -11,7 +11,7 @@ import {
|
||||
stringifyRequestQuery,
|
||||
WithChildren,
|
||||
} from '../../../../../../_digifi/helpers'
|
||||
import {getStartedUsers} from './_requests'
|
||||
import {getSignatoryList} from './_requests'
|
||||
import {User} from './_models'
|
||||
import {useQueryRequest} from './QueryRequestProvider'
|
||||
|
||||
@@ -32,9 +32,9 @@ const QueryResponseProvider: FC<WithChildren> = ({children}) => {
|
||||
refetch,
|
||||
data: response,
|
||||
} = useQuery(
|
||||
`${QUERIES.USERS_LIST}-${query}`,
|
||||
`${QUERIES.SIGNATORY_LIST}-${query}`,
|
||||
() => {
|
||||
return getStartedUsers(query)
|
||||
return getSignatoryList(query)
|
||||
},
|
||||
{cacheTime: 0, keepPreviousData: true, refetchOnWindowFocus: false}
|
||||
)
|
||||
@@ -53,10 +53,17 @@ const useQueryResponseData = () => {
|
||||
if (!response) {
|
||||
return []
|
||||
}
|
||||
|
||||
return response?.records || []
|
||||
}
|
||||
|
||||
const useAllResponse = () => {
|
||||
const {response} = useQueryResponse()
|
||||
if (!response) {
|
||||
return []
|
||||
}
|
||||
return response || []
|
||||
}
|
||||
|
||||
const useQueryResponsePagination = () => {
|
||||
const defaultPaginationState: PaginationState = {
|
||||
links: [],
|
||||
@@ -82,4 +89,5 @@ export {
|
||||
useQueryResponseData,
|
||||
useQueryResponsePagination,
|
||||
useQueryResponseLoading,
|
||||
useAllResponse
|
||||
}
|
||||
+4
-12
@@ -14,21 +14,13 @@ export type User = {
|
||||
label: string
|
||||
state: string
|
||||
}
|
||||
firstname?: string,
|
||||
lastname?: string
|
||||
uid?: string
|
||||
loan_amount?: string
|
||||
payment_month?: string
|
||||
sales_agent?: string
|
||||
gender?: string | null
|
||||
marital_status?: string
|
||||
email?: string
|
||||
address?: string
|
||||
state?: string
|
||||
country?: string
|
||||
status?: string
|
||||
added?: string
|
||||
updated?: string
|
||||
email?: string
|
||||
employer_name?: string
|
||||
title?: string
|
||||
phone?: string
|
||||
}
|
||||
|
||||
export type UsersQueryResponse = Response<Array<User>>
|
||||
+3
-3
@@ -13,9 +13,9 @@ const NEW_USER_ENDPOINT = import.meta.env.VITE_APP_USER_ENDPOINT
|
||||
// .get(`${GET_USERS_URL}?${query}`)
|
||||
// .then((d: AxiosResponse<UsersQueryResponse>) => d.data);
|
||||
// };
|
||||
const getStartedUsers = (query: string): Promise<UsersQueryResponse> => { // FUNCTION TO GET USERS THAT HAVE STARTED LOAN APPLICATION
|
||||
const getSignatoryList = (query: string): Promise<UsersQueryResponse> => { // FUNCTION TO GET EMPLOYERS LIST
|
||||
return axios
|
||||
.get(`${NEW_USER_ENDPOINT}/loan/started`)
|
||||
.get(`${NEW_USER_ENDPOINT}/employers/signatory`)
|
||||
.then((d: AxiosResponse<UsersQueryResponse>) => d.data);
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ const deleteSelectedUsers = (userIds: Array<ID>): Promise<void> => {
|
||||
};
|
||||
|
||||
export {
|
||||
getStartedUsers,
|
||||
getSignatoryList,
|
||||
deleteUser,
|
||||
deleteSelectedUsers,
|
||||
getUserById,
|
||||
@@ -0,0 +1,11 @@
|
||||
import {FC} from 'react'
|
||||
|
||||
type Props = {
|
||||
phone?: string
|
||||
}
|
||||
|
||||
const Phone: FC<Props> = ({phone}) => (
|
||||
<div className='badge badge-light fw-bolder'>{phone}</div>
|
||||
)
|
||||
|
||||
export {Phone}
|
||||
+8
@@ -54,6 +54,14 @@ const UserActionsCell: FC<Props> = ({id}) => {
|
||||
Edit
|
||||
</a>
|
||||
</div>
|
||||
{/* end::Menu item */}
|
||||
|
||||
{/* begin::Menu item */}
|
||||
<div className='menu-item px-3'>
|
||||
<a className='menu-link px-3' onClick={openEditModal}>
|
||||
Add Signatory
|
||||
</a>
|
||||
</div>
|
||||
{/* end::Menu item */}
|
||||
|
||||
{/* begin::Menu item */}
|
||||
+2
-2
@@ -25,14 +25,14 @@ const UserInfoCell: FC<Props> = ({user}) => (
|
||||
`text-${user.initials?.state}`
|
||||
)}
|
||||
>
|
||||
{user.firstname?.substring(0,1).toUpperCase()} {user.lastname?.substring(0,1).toUpperCase()}
|
||||
{user.name?.substring(0,1).toUpperCase()} {user.name?.substring(0,1).toUpperCase()}
|
||||
</div>
|
||||
)}
|
||||
</a>
|
||||
</div>
|
||||
<div className='d-flex flex-column'>
|
||||
<a href='#' className='text-gray-800 text-hover-primary mb-1'>
|
||||
{user.firstname} {user.lastname}
|
||||
{user.name}
|
||||
</a>
|
||||
<span>{user.email}</span>
|
||||
</div>
|
||||
+14
-13
@@ -8,6 +8,7 @@ import {UserCustomHeader} from './UserCustomHeader'
|
||||
import {UserSelectionHeader} from './UserSelectionHeader'
|
||||
import {User} from '../../core/_models'
|
||||
import { AddedCell } from './AddedCell'
|
||||
import { Phone } from './Phone'
|
||||
|
||||
const usersColumns: ReadonlyArray<Column<User>> = [
|
||||
{
|
||||
@@ -17,26 +18,26 @@ const usersColumns: ReadonlyArray<Column<User>> = [
|
||||
},
|
||||
{
|
||||
Header: (props) => <UserCustomHeader tableProps={props} title='Name' className='min-w-125px' />,
|
||||
id: 'firstname',
|
||||
id: 'name',
|
||||
Cell: ({...props}) => <UserInfoCell user={props.data[props.row.index]} />,
|
||||
},
|
||||
// {
|
||||
// Header: (props) => <UserCustomHeader tableProps={props} title='Max Loan' className='min-w-125px' />,
|
||||
// accessor: 'max_loan',
|
||||
// },
|
||||
{
|
||||
Header: (props) => <UserCustomHeader tableProps={props} title='Amount' className='min-w-125px' />,
|
||||
accessor: 'loan_amount',
|
||||
Header: (props) => (
|
||||
<UserCustomHeader tableProps={props} title='Phone' className='min-w-125px' />
|
||||
),
|
||||
id: 'phone',
|
||||
Cell: ({...props}) => <Phone phone={props.data[props.row.index].phone} />,
|
||||
},
|
||||
{
|
||||
Header: (props) => (
|
||||
<UserCustomHeader tableProps={props} title='Payment Terms' className='min-w-125px' />
|
||||
<UserCustomHeader tableProps={props} title='Employer Name' className='min-w-125px' />
|
||||
),
|
||||
id: 'payment_month',
|
||||
Cell: ({...props}) => <PaymentMonthCell payment_month={props.data[props.row.index].payment_month} />,
|
||||
},
|
||||
{
|
||||
Header: (props) => (
|
||||
<UserCustomHeader tableProps={props} title='Agent' className='min-w-125px' />
|
||||
),
|
||||
id: 'sales_agent',
|
||||
Cell: ({...props}) => <AgentCell agent={props.data[props.row.index].sales_agent} />,
|
||||
id: 'employer_name',
|
||||
Cell: ({...props}) => <PaymentMonthCell payment_month={props.data[props.row.index].employer_name} />,
|
||||
},
|
||||
{
|
||||
Header: (props) => (
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
import {ListViewProvider, useListView} from './core/ListViewProvider'
|
||||
import {QueryRequestProvider} from './core/QueryRequestProvider'
|
||||
import {QueryResponseProvider} from './core/QueryResponseProvider'
|
||||
import {QueryResponseProvider, useAllResponse} from './core/QueryResponseProvider'
|
||||
import {UsersListHeader} from './components/header/UsersListHeader'
|
||||
import {UsersTable} from './table/UsersTable'
|
||||
import {UserEditModal} from './user-edit-modal/UserEditModal'
|
||||
@@ -9,6 +9,8 @@ import { ToolbarWrapper } from '../../../../../_digifi/layout/components/toolbar
|
||||
import { Content } from '../../../../../_digifi/layout/components/content'
|
||||
|
||||
const UsersList = () => {
|
||||
const response = useAllResponse()
|
||||
console.log('RESPONSE', response)
|
||||
const {itemIdForUpdate} = useListView()
|
||||
return (
|
||||
<>
|
||||
+6
-6
@@ -10,9 +10,7 @@ const UsersListToolbar = () => {
|
||||
|
||||
return (
|
||||
<div className='d-flex justify-content-end' data-kt-user-table-toolbar='base'>
|
||||
<UsersListFilter />
|
||||
|
||||
{/* begin::Export */}
|
||||
{/* begin::Export */}
|
||||
{/* <button type='button' className='btn btn-light-primary me-3'>
|
||||
<KTIcon iconName='exit-up' className='fs-2' />
|
||||
Export
|
||||
@@ -20,11 +18,13 @@ const UsersListToolbar = () => {
|
||||
{/* end::Export */}
|
||||
|
||||
{/* begin::Add user */}
|
||||
{/* <button type='button' className='btn btn-primary' onClick={openAddUserModal}>
|
||||
<button type='button' className='btn btn-primary me-3' onClick={openAddUserModal}>
|
||||
<KTIcon iconName='plus' className='fs-2' />
|
||||
Add User
|
||||
</button> */}
|
||||
Add New
|
||||
</button>
|
||||
{/* end::Add user */}
|
||||
|
||||
<UsersListFilter />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+12
-4
@@ -11,7 +11,7 @@ import {
|
||||
stringifyRequestQuery,
|
||||
WithChildren,
|
||||
} from '../../../../../../_digifi/helpers'
|
||||
import {getStartedUsers} from './_requests'
|
||||
import {getEmployersList} from './_requests'
|
||||
import {User} from './_models'
|
||||
import {useQueryRequest} from './QueryRequestProvider'
|
||||
|
||||
@@ -32,9 +32,9 @@ const QueryResponseProvider: FC<WithChildren> = ({children}) => {
|
||||
refetch,
|
||||
data: response,
|
||||
} = useQuery(
|
||||
`${QUERIES.USERS_LIST}-${query}`,
|
||||
`${QUERIES.EMPLOYERS_LIST}-${query}`,
|
||||
() => {
|
||||
return getStartedUsers(query)
|
||||
return getEmployersList(query)
|
||||
},
|
||||
{cacheTime: 0, keepPreviousData: true, refetchOnWindowFocus: false}
|
||||
)
|
||||
@@ -53,10 +53,17 @@ const useQueryResponseData = () => {
|
||||
if (!response) {
|
||||
return []
|
||||
}
|
||||
|
||||
return response?.records || []
|
||||
}
|
||||
|
||||
const useAllResponse = () => {
|
||||
const {response} = useQueryResponse()
|
||||
if (!response) {
|
||||
return []
|
||||
}
|
||||
return response || []
|
||||
}
|
||||
|
||||
const useQueryResponsePagination = () => {
|
||||
const defaultPaginationState: PaginationState = {
|
||||
links: [],
|
||||
@@ -82,4 +89,5 @@ export {
|
||||
useQueryResponseData,
|
||||
useQueryResponsePagination,
|
||||
useQueryResponseLoading,
|
||||
useAllResponse
|
||||
}
|
||||
+7
-11
@@ -14,21 +14,17 @@ export type User = {
|
||||
label: string
|
||||
state: string
|
||||
}
|
||||
firstname?: string,
|
||||
lastname?: string
|
||||
uid?: string
|
||||
loan_amount?: string
|
||||
payment_month?: string
|
||||
sales_agent?: string
|
||||
gender?: string | null
|
||||
marital_status?: string
|
||||
email?: string
|
||||
address?: string
|
||||
state?: string
|
||||
country?: string
|
||||
percent_interest?: string
|
||||
max_loan?: string
|
||||
tenor?: string
|
||||
retirement_age?: string
|
||||
status?: string
|
||||
sector?: string
|
||||
salary_source?: string
|
||||
added?: string
|
||||
updated?: string
|
||||
email?: string
|
||||
}
|
||||
|
||||
export type UsersQueryResponse = Response<Array<User>>
|
||||
+3
-3
@@ -13,9 +13,9 @@ const NEW_USER_ENDPOINT = import.meta.env.VITE_APP_USER_ENDPOINT
|
||||
// .get(`${GET_USERS_URL}?${query}`)
|
||||
// .then((d: AxiosResponse<UsersQueryResponse>) => d.data);
|
||||
// };
|
||||
const getStartedUsers = (query: string): Promise<UsersQueryResponse> => { // FUNCTION TO GET USERS THAT HAVE STARTED LOAN APPLICATION
|
||||
const getEmployersList = (query: string): Promise<UsersQueryResponse> => { // FUNCTION TO GET EMPLOYERS LIST
|
||||
return axios
|
||||
.get(`${NEW_USER_ENDPOINT}/loan/started`)
|
||||
.get(`${NEW_USER_ENDPOINT}/employers`)
|
||||
.then((d: AxiosResponse<UsersQueryResponse>) => d.data);
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ const deleteSelectedUsers = (userIds: Array<ID>): Promise<void> => {
|
||||
};
|
||||
|
||||
export {
|
||||
getStartedUsers,
|
||||
getEmployersList,
|
||||
deleteUser,
|
||||
deleteSelectedUsers,
|
||||
getUserById,
|
||||
@@ -0,0 +1,12 @@
|
||||
import {FC} from 'react'
|
||||
import { formatNumbers } from '../../../../../../../_digifi/helpers/formatNumbers'
|
||||
|
||||
type Props = {
|
||||
max_loan?: string
|
||||
}
|
||||
|
||||
const MaxLoan: FC<Props> = ({max_loan}) => (
|
||||
<div className='badge badge-light fw-bolder'>{formatNumbers(max_loan)}</div>
|
||||
)
|
||||
|
||||
export {MaxLoan}
|
||||
+2
-2
@@ -25,14 +25,14 @@ const UserInfoCell: FC<Props> = ({user}) => (
|
||||
`text-${user.initials?.state}`
|
||||
)}
|
||||
>
|
||||
{user.firstname?.substring(0,1).toUpperCase()} {user.lastname?.substring(0,1).toUpperCase()}
|
||||
{user.name?.substring(0,1).toUpperCase()} {user.name?.substring(0,1).toUpperCase()}
|
||||
</div>
|
||||
)}
|
||||
</a>
|
||||
</div>
|
||||
<div className='d-flex flex-column'>
|
||||
<a href='#' className='text-gray-800 text-hover-primary mb-1'>
|
||||
{user.firstname} {user.lastname}
|
||||
{user.name}
|
||||
</a>
|
||||
<span>{user.email}</span>
|
||||
</div>
|
||||
@@ -0,0 +1,65 @@
|
||||
import {Column} from 'react-table'
|
||||
import {UserInfoCell} from './UserInfoCell'
|
||||
import { PaymentMonthCell } from './UserLastLoginCell'
|
||||
import {AgentCell} from './AgentCell'
|
||||
import {UserActionsCell} from './UserActionsCell'
|
||||
import {UserSelectionCell} from './UserSelectionCell'
|
||||
import {UserCustomHeader} from './UserCustomHeader'
|
||||
import {UserSelectionHeader} from './UserSelectionHeader'
|
||||
import {User} from '../../core/_models'
|
||||
import { AddedCell } from './AddedCell'
|
||||
import { MaxLoan } from './MaxLoan'
|
||||
|
||||
const usersColumns: ReadonlyArray<Column<User>> = [
|
||||
{
|
||||
Header: (props) => <UserSelectionHeader tableProps={props} />,
|
||||
id: 'selection',
|
||||
Cell: ({...props}) => <UserSelectionCell id={props.data[props.row.index].uid} />,
|
||||
},
|
||||
{
|
||||
Header: (props) => <UserCustomHeader tableProps={props} title='Name' className='min-w-125px' />,
|
||||
id: 'name',
|
||||
Cell: ({...props}) => <UserInfoCell user={props.data[props.row.index]} />,
|
||||
},
|
||||
// {
|
||||
// Header: (props) => <UserCustomHeader tableProps={props} title='Max Loan' className='min-w-125px' />,
|
||||
// accessor: 'max_loan',
|
||||
// },
|
||||
{
|
||||
Header: (props) => (
|
||||
<UserCustomHeader tableProps={props} title='Max Loan' className='min-w-125px' />
|
||||
),
|
||||
id: 'max_loan',
|
||||
Cell: ({...props}) => <MaxLoan max_loan={props.data[props.row.index].max_loan} />,
|
||||
},
|
||||
{
|
||||
Header: (props) => (
|
||||
<UserCustomHeader tableProps={props} title='Percent Interest' className='min-w-125px' />
|
||||
),
|
||||
id: 'percent_interest',
|
||||
Cell: ({...props}) => <PaymentMonthCell payment_month={props.data[props.row.index].percent_interest} />,
|
||||
},
|
||||
{
|
||||
Header: (props) => (
|
||||
<UserCustomHeader tableProps={props} title='Retirement Age' className='min-w-125px' />
|
||||
),
|
||||
id: 'retirement_age',
|
||||
Cell: ({...props}) => <AgentCell agent={props.data[props.row.index].retirement_age} />,
|
||||
},
|
||||
{
|
||||
Header: (props) => (
|
||||
<UserCustomHeader tableProps={props} title='Added' className='min-w-125px' />
|
||||
),
|
||||
id: 'added',
|
||||
Cell: ({...props}) => <AddedCell added={props.data[props.row.index].added} />,
|
||||
},
|
||||
{
|
||||
Header: (props) => (
|
||||
<UserCustomHeader tableProps={props} title='Actions' className='text-end min-w-100px' />
|
||||
),
|
||||
id: 'actions',
|
||||
Cell: ({...props}) => <UserActionsCell id={props.data[props.row.index].uid} />,
|
||||
},
|
||||
]
|
||||
|
||||
export {usersColumns}
|
||||
@@ -1,37 +1,5 @@
|
||||
import { KTCard } from "../../../../_digifi/helpers"
|
||||
import { Content } from "../../../../_digifi/layout/components/content"
|
||||
import { ToolbarWrapper } from "../../../../_digifi/layout/components/toolbar"
|
||||
import { UsersListHeader } from "../user-approved/users-list/components/header/UsersListHeader"
|
||||
import { ListViewProvider, useListView } from "../user-approved/users-list/core/ListViewProvider"
|
||||
import { QueryRequestProvider } from "../user-approved/users-list/core/QueryRequestProvider"
|
||||
import { QueryResponseProvider } from "../user-approved/users-list/core/QueryResponseProvider"
|
||||
import { UsersTable } from "../user-approved/users-list/table/UsersTable"
|
||||
import { UserEditModal } from "../user-approved/users-list/user-edit-modal/UserEditModal"
|
||||
import { UsersListWrapper } from "../user-approved/UsersList";
|
||||
|
||||
const UsersList = () => {
|
||||
const {itemIdForUpdate} = useListView()
|
||||
return (
|
||||
<>
|
||||
<KTCard>
|
||||
<UsersListHeader />
|
||||
<UsersTable />
|
||||
</KTCard>
|
||||
{itemIdForUpdate !== undefined && <UserEditModal />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
const UserApprovedList = () => <UsersListWrapper />;
|
||||
|
||||
const UserApprovedList = () => (
|
||||
<QueryRequestProvider>
|
||||
<QueryResponseProvider>
|
||||
<ListViewProvider>
|
||||
<ToolbarWrapper />
|
||||
<Content>
|
||||
<UsersList />
|
||||
</Content>
|
||||
</ListViewProvider>
|
||||
</QueryResponseProvider>
|
||||
</QueryRequestProvider>
|
||||
)
|
||||
|
||||
export {UserApprovedList}
|
||||
export { UserApprovedList };
|
||||
|
||||
@@ -1,37 +1,5 @@
|
||||
import { KTCard } from "../../../../_digifi/helpers"
|
||||
import { Content } from "../../../../_digifi/layout/components/content"
|
||||
import { ToolbarWrapper } from "../../../../_digifi/layout/components/toolbar"
|
||||
import { UsersListHeader } from "../user-pending/users-list/components/header/UsersListHeader"
|
||||
import { ListViewProvider, useListView } from "../user-pending/users-list/core/ListViewProvider"
|
||||
import { QueryRequestProvider } from "../user-pending/users-list/core/QueryRequestProvider"
|
||||
import { QueryResponseProvider } from "../user-pending/users-list/core/QueryResponseProvider"
|
||||
import { UsersTable } from "../user-pending/users-list/table/UsersTable"
|
||||
import { UserEditModal } from "../user-pending/users-list/user-edit-modal/UserEditModal"
|
||||
import { UsersListWrapper } from "../user-pending/UsersList";
|
||||
|
||||
const UsersList = () => {
|
||||
const {itemIdForUpdate} = useListView()
|
||||
return (
|
||||
<>
|
||||
<KTCard>
|
||||
<UsersListHeader />
|
||||
<UsersTable />
|
||||
</KTCard>
|
||||
{itemIdForUpdate !== undefined && <UserEditModal />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
const UserPendingList = () => <UsersListWrapper />;
|
||||
|
||||
const UserPendingList = () => (
|
||||
<QueryRequestProvider>
|
||||
<QueryResponseProvider>
|
||||
<ListViewProvider>
|
||||
<ToolbarWrapper />
|
||||
<Content>
|
||||
<UsersList />
|
||||
</Content>
|
||||
</ListViewProvider>
|
||||
</QueryResponseProvider>
|
||||
</QueryRequestProvider>
|
||||
)
|
||||
|
||||
export {UserPendingList}
|
||||
export { UserPendingList };
|
||||
|
||||
@@ -1,37 +1,5 @@
|
||||
import { KTCard } from "../../../../_digifi/helpers"
|
||||
import { Content } from "../../../../_digifi/layout/components/content"
|
||||
import { ToolbarWrapper } from "../../../../_digifi/layout/components/toolbar"
|
||||
import { UsersListHeader } from "../user-ready/users-list/components/header/UsersListHeader"
|
||||
import { ListViewProvider, useListView } from "../user-ready/users-list/core/ListViewProvider"
|
||||
import { QueryRequestProvider } from "../user-ready/users-list/core/QueryRequestProvider"
|
||||
import { QueryResponseProvider } from "../user-ready/users-list/core/QueryResponseProvider"
|
||||
import { UsersTable } from "../user-ready/users-list/table/UsersTable"
|
||||
import { UserEditModal } from "../user-ready/users-list/user-edit-modal/UserEditModal"
|
||||
import { UsersListWrapper } from "../user-ready/UsersList";
|
||||
|
||||
const UsersList = () => {
|
||||
const {itemIdForUpdate} = useListView()
|
||||
return (
|
||||
<>
|
||||
<KTCard>
|
||||
<UsersListHeader />
|
||||
<UsersTable />
|
||||
</KTCard>
|
||||
{itemIdForUpdate !== undefined && <UserEditModal />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
const UserReadyList = () => <UsersListWrapper />;
|
||||
|
||||
const UserReadyList = () => (
|
||||
<QueryRequestProvider>
|
||||
<QueryResponseProvider>
|
||||
<ListViewProvider>
|
||||
<ToolbarWrapper />
|
||||
<Content>
|
||||
<UsersList />
|
||||
</Content>
|
||||
</ListViewProvider>
|
||||
</QueryResponseProvider>
|
||||
</QueryRequestProvider>
|
||||
)
|
||||
|
||||
export {UserReadyList}
|
||||
export { UserReadyList };
|
||||
|
||||
@@ -1,37 +1,5 @@
|
||||
import { KTCard } from "../../../../_digifi/helpers"
|
||||
import { Content } from "../../../../_digifi/layout/components/content"
|
||||
import { ToolbarWrapper } from "../../../../_digifi/layout/components/toolbar"
|
||||
import { UsersListHeader } from "../user-rejected/users-list/components/header/UsersListHeader"
|
||||
import { ListViewProvider, useListView } from "../user-rejected/users-list/core/ListViewProvider"
|
||||
import { QueryRequestProvider } from "../user-rejected/users-list/core/QueryRequestProvider"
|
||||
import { QueryResponseProvider } from "../user-rejected/users-list/core/QueryResponseProvider"
|
||||
import { UsersTable } from "../user-rejected/users-list/table/UsersTable"
|
||||
import { UserEditModal } from "../user-rejected/users-list/user-edit-modal/UserEditModal"
|
||||
import { UsersListWrapper } from "../user-rejected/UsersList";
|
||||
|
||||
const UsersList = () => {
|
||||
const {itemIdForUpdate} = useListView()
|
||||
return (
|
||||
<>
|
||||
<KTCard>
|
||||
<UsersListHeader />
|
||||
<UsersTable />
|
||||
</KTCard>
|
||||
{itemIdForUpdate !== undefined && <UserEditModal />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
const UserRejectedList = () => <UsersListWrapper />;
|
||||
|
||||
const UserRejectedList = () => (
|
||||
<QueryRequestProvider>
|
||||
<QueryResponseProvider>
|
||||
<ListViewProvider>
|
||||
<ToolbarWrapper />
|
||||
<Content>
|
||||
<UsersList />
|
||||
</Content>
|
||||
</ListViewProvider>
|
||||
</QueryResponseProvider>
|
||||
</QueryRequestProvider>
|
||||
)
|
||||
|
||||
export {UserRejectedList}
|
||||
export { UserRejectedList };
|
||||
|
||||
@@ -1,37 +1,5 @@
|
||||
import { KTCard } from "../../../../_digifi/helpers"
|
||||
import { Content } from "../../../../_digifi/layout/components/content"
|
||||
import { ToolbarWrapper } from "../../../../_digifi/layout/components/toolbar"
|
||||
import { UsersListHeader } from "../user-started/users-list/components/header/UsersListHeader"
|
||||
import { ListViewProvider, useListView } from "../user-started/users-list/core/ListViewProvider"
|
||||
import { QueryRequestProvider } from "../user-started/users-list/core/QueryRequestProvider"
|
||||
import { QueryResponseProvider } from "../user-started/users-list/core/QueryResponseProvider"
|
||||
import { UsersTable } from "../user-started/users-list/table/UsersTable"
|
||||
import { UserEditModal } from "../user-started/users-list/user-edit-modal/UserEditModal"
|
||||
import { UsersListWrapper } from "../user-started/UsersList";
|
||||
|
||||
const UsersList = () => {
|
||||
const {itemIdForUpdate} = useListView()
|
||||
return (
|
||||
<>
|
||||
<KTCard>
|
||||
<UsersListHeader />
|
||||
<UsersTable />
|
||||
</KTCard>
|
||||
{itemIdForUpdate !== undefined && <UserEditModal />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
const UserStartedList = () => <UsersListWrapper />;
|
||||
|
||||
const UserStartedList = () => (
|
||||
<QueryRequestProvider>
|
||||
<QueryResponseProvider>
|
||||
<ListViewProvider>
|
||||
<ToolbarWrapper />
|
||||
<Content>
|
||||
<UsersList />
|
||||
</Content>
|
||||
</ListViewProvider>
|
||||
</QueryResponseProvider>
|
||||
</QueryRequestProvider>
|
||||
)
|
||||
|
||||
export {UserStartedList}
|
||||
export { UserStartedList };
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
import {ID, Response} from '../../../../../../_digifi/helpers'
|
||||
import { ID, Response } from "../../../../_digifi/helpers"
|
||||
|
||||
export type User = {
|
||||
id?: ID
|
||||
name?: string
|
||||
+29
-1
@@ -1,5 +1,5 @@
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
import { ID, Response } from "../../../../../../_digifi/helpers";
|
||||
import { ID, Response } from "../../../../_digifi/helpers"
|
||||
import { User, UsersQueryResponse } from "./_models";
|
||||
|
||||
const API_URL = import.meta.env.VITE_APP_THEME_API_URL;
|
||||
@@ -19,6 +19,30 @@ const getStartedUsers = (query: string): Promise<UsersQueryResponse> => { // FUN
|
||||
.then((d: AxiosResponse<UsersQueryResponse>) => d.data);
|
||||
};
|
||||
|
||||
const getRejectedUsers = (query: string): Promise<UsersQueryResponse> => { // FUNCTION TO GET USERS THAT HAVE REJECTED LOAN APPLICATION
|
||||
return axios
|
||||
.get(`${NEW_USER_ENDPOINT}/loan/rejected`)
|
||||
.then((d: AxiosResponse<UsersQueryResponse>) => d.data);
|
||||
};
|
||||
|
||||
const getPendingUsers = (query: string): Promise<UsersQueryResponse> => { // FUNCTION TO GET USERS THAT HAVE PENDING LOAN APPLICATION
|
||||
return axios
|
||||
.get(`${NEW_USER_ENDPOINT}/loan/pending`)
|
||||
.then((d: AxiosResponse<UsersQueryResponse>) => d.data);
|
||||
};
|
||||
|
||||
const getReadyUsers = (query: string): Promise<UsersQueryResponse> => { // FUNCTION TO GET USERS THAT HAVE READY LOAN APPLICATION
|
||||
return axios
|
||||
.get(`${NEW_USER_ENDPOINT}/loan/ready`)
|
||||
.then((d: AxiosResponse<UsersQueryResponse>) => d.data);
|
||||
};
|
||||
|
||||
const getApprovedUsers = (query: string): Promise<UsersQueryResponse> => { // FUNCTION TO GET USERS THAT HAVE APPROVED LOAN APPLICATION
|
||||
return axios
|
||||
.get(`${NEW_USER_ENDPOINT}/loan/approved`)
|
||||
.then((d: AxiosResponse<UsersQueryResponse>) => d.data);
|
||||
};
|
||||
|
||||
const getUserById = (id: ID): Promise<User | undefined> => {
|
||||
return axios
|
||||
.get(`${USER_URL}/${id}`)
|
||||
@@ -51,6 +75,10 @@ const deleteSelectedUsers = (userIds: Array<ID>): Promise<void> => {
|
||||
|
||||
export {
|
||||
getStartedUsers,
|
||||
getRejectedUsers,
|
||||
getPendingUsers,
|
||||
getReadyUsers,
|
||||
getApprovedUsers,
|
||||
deleteUser,
|
||||
deleteSelectedUsers,
|
||||
getUserById,
|
||||
@@ -0,0 +1,37 @@
|
||||
import { ListViewProvider, useListView } from "./core/ListViewProvider";
|
||||
import { QueryRequestProvider } from "./core/QueryRequestProvider";
|
||||
import { QueryResponseProvider } from "./core/QueryResponseProvider";
|
||||
import { UsersListHeader } from "./components/header/UsersListHeader";
|
||||
import { UsersTable } from "./table/UsersTable";
|
||||
import { UserEditModal } from "./user-edit-modal/UserEditModal";
|
||||
import { KTCard } from "../../../../_digifi/helpers";
|
||||
import { ToolbarWrapper } from "../../../../_digifi/layout/components/toolbar";
|
||||
import { Content } from "../../../../_digifi/layout/components/content";
|
||||
|
||||
const UsersList = () => {
|
||||
const { itemIdForUpdate } = useListView();
|
||||
return (
|
||||
<>
|
||||
<KTCard>
|
||||
<UsersListHeader />
|
||||
<UsersTable />
|
||||
</KTCard>
|
||||
{itemIdForUpdate !== undefined && <UserEditModal />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const UsersListWrapper = () => (
|
||||
<QueryRequestProvider>
|
||||
<QueryResponseProvider>
|
||||
<ListViewProvider>
|
||||
<ToolbarWrapper />
|
||||
<Content>
|
||||
<UsersList />
|
||||
</Content>
|
||||
</ListViewProvider>
|
||||
</QueryResponseProvider>
|
||||
</QueryRequestProvider>
|
||||
);
|
||||
|
||||
export { UsersListWrapper };
|
||||
@@ -1,39 +0,0 @@
|
||||
import {Route, Routes, Outlet, Navigate} from 'react-router-dom'
|
||||
import {PageLink, PageTitle} from '../../../../_digifi/layout/core'
|
||||
import {UsersListWrapper} from './users-list/UsersList'
|
||||
|
||||
const usersBreadcrumbs: Array<PageLink> = [
|
||||
{
|
||||
title: 'User Management',
|
||||
path: '/apps/user-management/users',
|
||||
isSeparator: false,
|
||||
isActive: false,
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
path: '',
|
||||
isSeparator: true,
|
||||
isActive: false,
|
||||
},
|
||||
]
|
||||
|
||||
const UsersPage = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route element={<Outlet />}>
|
||||
<Route
|
||||
path='users'
|
||||
element={
|
||||
<>
|
||||
<PageTitle breadcrumbs={usersBreadcrumbs}>Users list</PageTitle>
|
||||
<UsersListWrapper />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
<Route index element={<Navigate to='/apps/user-management/users' />} />
|
||||
</Routes>
|
||||
)
|
||||
}
|
||||
|
||||
export default UsersPage
|
||||
+13
-10
@@ -1,15 +1,18 @@
|
||||
import {KTIcon} from '../../../../../../../_digifi/helpers'
|
||||
import {useListView} from '../../core/ListViewProvider'
|
||||
import {UsersListFilter} from './UsersListFilter'
|
||||
import { KTIcon } from "../../../../../../_digifi/helpers";
|
||||
import { useListView } from "../../core/ListViewProvider";
|
||||
import { UsersListFilter } from "./UsersListFilter";
|
||||
|
||||
const UsersListToolbar = () => {
|
||||
const {setItemIdForUpdate} = useListView()
|
||||
const { setItemIdForUpdate } = useListView();
|
||||
const openAddUserModal = () => {
|
||||
setItemIdForUpdate(null)
|
||||
}
|
||||
setItemIdForUpdate(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='d-flex justify-content-end' data-kt-user-table-toolbar='base'>
|
||||
<div
|
||||
className="d-flex justify-content-end"
|
||||
data-kt-user-table-toolbar="base"
|
||||
>
|
||||
<UsersListFilter />
|
||||
|
||||
{/* begin::Export */}
|
||||
@@ -26,7 +29,7 @@ const UsersListToolbar = () => {
|
||||
</button> */}
|
||||
{/* end::Add user */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export {UsersListToolbar}
|
||||
export { UsersListToolbar };
|
||||
@@ -0,0 +1,136 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { MenuComponent } from "../../../../../../_digifi/assets/ts/components";
|
||||
import { initialQueryState, KTIcon } from "../../../../../../_digifi/helpers";
|
||||
import { useQueryRequest } from "../../core/QueryRequestProvider";
|
||||
import { useQueryResponse } from "../../core/QueryResponseProvider";
|
||||
|
||||
const UsersListFilter = () => {
|
||||
const { updateState } = useQueryRequest();
|
||||
const { isLoading } = useQueryResponse();
|
||||
const [role, setRole] = useState<string | undefined>();
|
||||
const [lastLogin, setLastLogin] = useState<string | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
MenuComponent.reinitialization();
|
||||
}, []);
|
||||
|
||||
const resetData = () => {
|
||||
updateState({ filter: undefined, ...initialQueryState });
|
||||
};
|
||||
|
||||
const filterData = () => {
|
||||
updateState({
|
||||
filter: { role, last_login: lastLogin },
|
||||
...initialQueryState,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* begin::Filter Button */}
|
||||
<button
|
||||
disabled={isLoading}
|
||||
type="button"
|
||||
className="btn btn-light-primary me-3"
|
||||
data-kt-menu-trigger="click"
|
||||
data-kt-menu-placement="bottom-end"
|
||||
>
|
||||
<KTIcon iconName="filter" className="fs-2" />
|
||||
Filter
|
||||
</button>
|
||||
{/* end::Filter Button */}
|
||||
{/* begin::SubMenu */}
|
||||
<div
|
||||
className="menu menu-sub menu-sub-dropdown w-300px w-md-325px"
|
||||
data-kt-menu="true"
|
||||
>
|
||||
{/* begin::Header */}
|
||||
<div className="px-7 py-5">
|
||||
<div className="fs-5 text-gray-900 fw-bolder">Filter Options</div>
|
||||
</div>
|
||||
{/* end::Header */}
|
||||
|
||||
{/* begin::Separator */}
|
||||
<div className="separator border-gray-200"></div>
|
||||
{/* end::Separator */}
|
||||
|
||||
{/* begin::Content */}
|
||||
<div className="px-7 py-5" data-kt-user-table-filter="form">
|
||||
{/* begin::Input group */}
|
||||
<div className="mb-10">
|
||||
<label className="form-label fs-6 fw-bold">Role:</label>
|
||||
<select
|
||||
className="form-select form-select-solid fw-bolder"
|
||||
data-kt-select2="true"
|
||||
data-placeholder="Select option"
|
||||
data-allow-clear="true"
|
||||
data-kt-user-table-filter="role"
|
||||
data-hide-search="true"
|
||||
onChange={(e) => setRole(e.target.value)}
|
||||
value={role}
|
||||
>
|
||||
<option value=""></option>
|
||||
<option value="Administrator">Administrator</option>
|
||||
<option value="Analyst">Analyst</option>
|
||||
<option value="Developer">Developer</option>
|
||||
<option value="Support">Support</option>
|
||||
<option value="Trial">Trial</option>
|
||||
</select>
|
||||
</div>
|
||||
{/* end::Input group */}
|
||||
|
||||
{/* begin::Input group */}
|
||||
<div className="mb-10">
|
||||
<label className="form-label fs-6 fw-bold">Last login:</label>
|
||||
<select
|
||||
className="form-select form-select-solid fw-bolder"
|
||||
data-kt-select2="true"
|
||||
data-placeholder="Select option"
|
||||
data-allow-clear="true"
|
||||
data-kt-user-table-filter="two-step"
|
||||
data-hide-search="true"
|
||||
onChange={(e) => setLastLogin(e.target.value)}
|
||||
value={lastLogin}
|
||||
>
|
||||
<option value=""></option>
|
||||
<option value="Yesterday">Yesterday</option>
|
||||
<option value="20 mins ago">20 mins ago</option>
|
||||
<option value="5 hours ago">5 hours ago</option>
|
||||
<option value="2 days ago">2 days ago</option>
|
||||
</select>
|
||||
</div>
|
||||
{/* end::Input group */}
|
||||
|
||||
{/* begin::Actions */}
|
||||
<div className="d-flex justify-content-end">
|
||||
<button
|
||||
type="button"
|
||||
disabled={isLoading}
|
||||
onClick={filterData}
|
||||
className="btn btn-light btn-active-light-primary fw-bold me-2 px-6"
|
||||
data-kt-menu-dismiss="true"
|
||||
data-kt-user-table-filter="reset"
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
<button
|
||||
disabled={isLoading}
|
||||
type="button"
|
||||
onClick={resetData}
|
||||
className="btn btn-primary fw-bold px-6"
|
||||
data-kt-menu-dismiss="true"
|
||||
data-kt-user-table-filter="filter"
|
||||
>
|
||||
Apply
|
||||
</button>
|
||||
</div>
|
||||
{/* end::Actions */}
|
||||
</div>
|
||||
{/* end::Content */}
|
||||
</div>
|
||||
{/* end::SubMenu */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export { UsersListFilter };
|
||||
@@ -0,0 +1,38 @@
|
||||
import { useQueryClient, useMutation } from "react-query";
|
||||
import { QUERIES } from "../../../../../../_digifi/helpers";
|
||||
import { useListView } from "../../core/ListViewProvider";
|
||||
import { useQueryResponse } from "../../core/QueryResponseProvider";
|
||||
import { deleteSelectedUsers } from "../../../core/_requests";
|
||||
|
||||
const UsersListGrouping = () => {
|
||||
const { selected, clearSelected } = useListView();
|
||||
const queryClient = useQueryClient();
|
||||
const { query } = useQueryResponse();
|
||||
|
||||
const deleteSelectedItems = useMutation(() => deleteSelectedUsers(selected), {
|
||||
// 💡 response of the mutation is passed to onSuccess
|
||||
onSuccess: () => {
|
||||
// ✅ update detail view directly
|
||||
queryClient.invalidateQueries([`${QUERIES.USERS_LIST}-${query}`]);
|
||||
clearSelected();
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="d-flex justify-content-end align-items-center">
|
||||
<div className="fw-bolder me-5">
|
||||
<span className="me-2">{selected.length}</span> Selected
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-danger"
|
||||
onClick={async () => await deleteSelectedItems.mutateAsync()}
|
||||
>
|
||||
Delete Selected
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { UsersListGrouping };
|
||||
@@ -0,0 +1,49 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
initialQueryState,
|
||||
KTIcon,
|
||||
useDebounce,
|
||||
} from "../../../../../../_digifi/helpers";
|
||||
import { useQueryRequest } from "../../core/QueryRequestProvider";
|
||||
|
||||
const UsersListSearchComponent = () => {
|
||||
const { updateState } = useQueryRequest();
|
||||
const [searchTerm, setSearchTerm] = useState<string>("");
|
||||
// Debounce search term so that it only gives us latest value ...
|
||||
// ... if searchTerm has not been updated within last 500ms.
|
||||
// The goal is to only have the API call fire when user stops typing ...
|
||||
// ... so that we aren't hitting our API rapidly.
|
||||
const debouncedSearchTerm = useDebounce(searchTerm, 150);
|
||||
// Effect for API call
|
||||
useEffect(
|
||||
() => {
|
||||
if (debouncedSearchTerm !== undefined && searchTerm !== undefined) {
|
||||
updateState({ search: debouncedSearchTerm, ...initialQueryState });
|
||||
}
|
||||
},
|
||||
[debouncedSearchTerm] // Only call effect if debounced search term changes
|
||||
// More details about useDebounce: https://usehooks.com/useDebounce/
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="card-title">
|
||||
{/* begin::Search */}
|
||||
<div className="d-flex align-items-center position-relative my-1">
|
||||
<KTIcon iconName="magnifier" className="fs-1 position-absolute ms-6" />
|
||||
<input
|
||||
type="text"
|
||||
data-kt-user-table-filter="search"
|
||||
className="form-control form-control-solid w-250px ps-14"
|
||||
placeholder="Search user"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
{/* end::Search */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { UsersListSearchComponent };
|
||||
@@ -0,0 +1,179 @@
|
||||
import clsx from "clsx";
|
||||
import {
|
||||
useQueryResponseLoading,
|
||||
useQueryResponsePagination,
|
||||
} from "../../core/QueryResponseProvider";
|
||||
import { useQueryRequest } from "../../core/QueryRequestProvider";
|
||||
import { PaginationState } from "../../../../../../_digifi/helpers";
|
||||
import { useMemo } from "react";
|
||||
|
||||
const mappedLabel = (label: string): string => {
|
||||
if (label === "« Previous") {
|
||||
return "Previous";
|
||||
}
|
||||
|
||||
if (label === "Next »") {
|
||||
return "Next";
|
||||
}
|
||||
|
||||
return label;
|
||||
};
|
||||
|
||||
const UsersListPagination = () => {
|
||||
const pagination = useQueryResponsePagination();
|
||||
const isLoading = useQueryResponseLoading();
|
||||
const { updateState } = useQueryRequest();
|
||||
const updatePage = (page: number | undefined | null) => {
|
||||
if (!page || isLoading || pagination.page === page) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateState({ page, items_per_page: pagination.items_per_page || 10 });
|
||||
};
|
||||
|
||||
const PAGINATION_PAGES_COUNT = 5;
|
||||
const sliceLinks = (pagination?: PaginationState) => {
|
||||
if (!pagination?.links?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const scopedLinks = [...pagination.links];
|
||||
|
||||
let pageLinks: Array<{
|
||||
label: string;
|
||||
active: boolean;
|
||||
url: string | null;
|
||||
page: number | null;
|
||||
}> = [];
|
||||
const previousLink: {
|
||||
label: string;
|
||||
active: boolean;
|
||||
url: string | null;
|
||||
page: number | null;
|
||||
} = scopedLinks.shift()!;
|
||||
const nextLink: {
|
||||
label: string;
|
||||
active: boolean;
|
||||
url: string | null;
|
||||
page: number | null;
|
||||
} = scopedLinks.pop()!;
|
||||
|
||||
const halfOfPagesCount = Math.floor(PAGINATION_PAGES_COUNT / 2);
|
||||
|
||||
pageLinks.push(previousLink);
|
||||
|
||||
if (
|
||||
pagination.page <= Math.round(PAGINATION_PAGES_COUNT / 2) ||
|
||||
scopedLinks.length <= PAGINATION_PAGES_COUNT
|
||||
) {
|
||||
pageLinks = [
|
||||
...pageLinks,
|
||||
...scopedLinks.slice(0, PAGINATION_PAGES_COUNT),
|
||||
];
|
||||
}
|
||||
|
||||
if (
|
||||
pagination.page > scopedLinks.length - halfOfPagesCount &&
|
||||
scopedLinks.length > PAGINATION_PAGES_COUNT
|
||||
) {
|
||||
pageLinks = [
|
||||
...pageLinks,
|
||||
...scopedLinks.slice(
|
||||
scopedLinks.length - PAGINATION_PAGES_COUNT,
|
||||
scopedLinks.length
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
if (
|
||||
!(
|
||||
pagination.page <= Math.round(PAGINATION_PAGES_COUNT / 2) ||
|
||||
scopedLinks.length <= PAGINATION_PAGES_COUNT
|
||||
) &&
|
||||
!(pagination.page > scopedLinks.length - halfOfPagesCount)
|
||||
) {
|
||||
pageLinks = [
|
||||
...pageLinks,
|
||||
...scopedLinks.slice(
|
||||
pagination.page - 1 - halfOfPagesCount,
|
||||
pagination.page + halfOfPagesCount
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
pageLinks.push(nextLink);
|
||||
|
||||
return pageLinks;
|
||||
};
|
||||
|
||||
const paginationLinks = useMemo(() => sliceLinks(pagination), [pagination]);
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-sm-12 col-md-5 d-flex align-items-center justify-content-center justify-content-md-start"></div>
|
||||
<div className="col-sm-12 col-md-7 d-flex align-items-center justify-content-center justify-content-md-end">
|
||||
<div id="kt_table_users_paginate">
|
||||
<ul className="pagination">
|
||||
<li
|
||||
className={clsx("page-item", {
|
||||
disabled: isLoading || pagination.page === 1,
|
||||
})}
|
||||
>
|
||||
<a
|
||||
onClick={() => updatePage(1)}
|
||||
style={{ cursor: "pointer" }}
|
||||
className="page-link"
|
||||
>
|
||||
First
|
||||
</a>
|
||||
</li>
|
||||
{paginationLinks
|
||||
?.map((link) => {
|
||||
return { ...link, label: mappedLabel(link.label) };
|
||||
})
|
||||
.map((link) => (
|
||||
<li
|
||||
key={link.label}
|
||||
className={clsx("page-item", {
|
||||
active: pagination.page === link.page,
|
||||
disabled: isLoading,
|
||||
previous: link.label === "Previous",
|
||||
next: link.label === "Next",
|
||||
})}
|
||||
>
|
||||
<a
|
||||
className={clsx("page-link", {
|
||||
"page-text":
|
||||
link.label === "Previous" || link.label === "Next",
|
||||
"me-5": link.label === "Previous",
|
||||
})}
|
||||
onClick={() => updatePage(link.page)}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
{mappedLabel(link.label)}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
<li
|
||||
className={clsx("page-item", {
|
||||
disabled:
|
||||
isLoading ||
|
||||
pagination.page === (pagination.links?.length || 3) - 2,
|
||||
})}
|
||||
>
|
||||
<a
|
||||
onClick={() => updatePage((pagination.links?.length || 3) - 2)}
|
||||
style={{ cursor: "pointer" }}
|
||||
className="page-link"
|
||||
>
|
||||
Last
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { UsersListPagination };
|
||||
@@ -0,0 +1,62 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import { FC, useState, createContext, useContext, useMemo } from "react";
|
||||
import {
|
||||
ID,
|
||||
calculatedGroupingIsDisabled,
|
||||
calculateIsAllDataSelected,
|
||||
groupingOnSelect,
|
||||
initialListView,
|
||||
ListViewContextProps,
|
||||
groupingOnSelectAll,
|
||||
WithChildren,
|
||||
} from "../../../../../_digifi/helpers";
|
||||
import {
|
||||
useQueryResponse,
|
||||
useQueryResponseData,
|
||||
} from "./QueryResponseProvider";
|
||||
|
||||
const ListViewContext = createContext<ListViewContextProps>(initialListView);
|
||||
|
||||
const ListViewProvider: FC<WithChildren> = ({ children }) => {
|
||||
const [selected, setSelected] = useState<Array<ID>>(initialListView.selected);
|
||||
const [itemIdForUpdate, setItemIdForUpdate] = useState<ID>(
|
||||
initialListView.itemIdForUpdate
|
||||
);
|
||||
const { isLoading } = useQueryResponse();
|
||||
const data = useQueryResponseData();
|
||||
const disabled = useMemo(
|
||||
() => calculatedGroupingIsDisabled(isLoading, data),
|
||||
[isLoading, data]
|
||||
);
|
||||
const isAllSelected = useMemo(
|
||||
() => calculateIsAllDataSelected(data, selected),
|
||||
[data, selected]
|
||||
);
|
||||
|
||||
return (
|
||||
<ListViewContext.Provider
|
||||
value={{
|
||||
selected,
|
||||
itemIdForUpdate,
|
||||
setItemIdForUpdate,
|
||||
disabled,
|
||||
isAllSelected,
|
||||
onSelect: (id: ID) => {
|
||||
groupingOnSelect(id, selected, setSelected);
|
||||
},
|
||||
onSelectAll: () => {
|
||||
groupingOnSelectAll(isAllSelected, setSelected, data);
|
||||
},
|
||||
clearSelected: () => {
|
||||
setSelected([]);
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ListViewContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useListView = () => useContext(ListViewContext);
|
||||
|
||||
export { ListViewProvider, useListView };
|
||||
@@ -0,0 +1,29 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import { FC, useState, createContext, useContext } from "react";
|
||||
import {
|
||||
QueryState,
|
||||
QueryRequestContextProps,
|
||||
initialQueryRequest,
|
||||
WithChildren,
|
||||
} from "../../../../../_digifi/helpers";
|
||||
|
||||
const QueryRequestContext =
|
||||
createContext<QueryRequestContextProps>(initialQueryRequest);
|
||||
|
||||
const QueryRequestProvider: FC<WithChildren> = ({ children }) => {
|
||||
const [state, setState] = useState<QueryState>(initialQueryRequest.state);
|
||||
|
||||
const updateState = (updates: Partial<QueryState>) => {
|
||||
const updatedState = { ...state, ...updates } as QueryState;
|
||||
setState(updatedState);
|
||||
};
|
||||
|
||||
return (
|
||||
<QueryRequestContext.Provider value={{ state, updateState }}>
|
||||
{children}
|
||||
</QueryRequestContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useQueryRequest = () => useContext(QueryRequestContext);
|
||||
export { QueryRequestProvider, useQueryRequest };
|
||||
@@ -0,0 +1,87 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { FC, useContext, useState, useEffect, useMemo } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import {
|
||||
createResponseContext,
|
||||
initialQueryResponse,
|
||||
initialQueryState,
|
||||
PaginationState,
|
||||
QUERIES,
|
||||
stringifyRequestQuery,
|
||||
WithChildren,
|
||||
} from "../../../../../_digifi/helpers";
|
||||
import { getApprovedUsers } from "../../core/_requests";
|
||||
import { User } from "../../core/_models";
|
||||
import { useQueryRequest } from "./QueryRequestProvider";
|
||||
|
||||
const QueryResponseContext = createResponseContext<User>(initialQueryResponse);
|
||||
const QueryResponseProvider: FC<WithChildren> = ({ children }) => {
|
||||
const { state } = useQueryRequest();
|
||||
const [query, setQuery] = useState<string>(stringifyRequestQuery(state));
|
||||
const updatedQuery = useMemo(() => stringifyRequestQuery(state), [state]);
|
||||
|
||||
useEffect(() => {
|
||||
if (query !== updatedQuery) {
|
||||
setQuery(updatedQuery);
|
||||
}
|
||||
}, [updatedQuery]);
|
||||
|
||||
const {
|
||||
isFetching,
|
||||
refetch,
|
||||
data: response,
|
||||
} = useQuery(
|
||||
`${QUERIES.USERS_LIST}-${query}`,
|
||||
() => {
|
||||
return getApprovedUsers(query);
|
||||
},
|
||||
{ cacheTime: 0, keepPreviousData: true, refetchOnWindowFocus: false }
|
||||
);
|
||||
|
||||
return (
|
||||
<QueryResponseContext.Provider
|
||||
value={{ isLoading: isFetching, refetch, response, query }}
|
||||
>
|
||||
{children}
|
||||
</QueryResponseContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useQueryResponse = () => useContext(QueryResponseContext);
|
||||
|
||||
const useQueryResponseData = () => {
|
||||
const { response } = useQueryResponse();
|
||||
if (!response) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return response?.records || [];
|
||||
};
|
||||
|
||||
const useQueryResponsePagination = () => {
|
||||
const defaultPaginationState: PaginationState = {
|
||||
links: [],
|
||||
...initialQueryState,
|
||||
};
|
||||
|
||||
const { response } = useQueryResponse();
|
||||
if (!response || !response.payload || !response.payload.pagination) {
|
||||
return defaultPaginationState;
|
||||
}
|
||||
|
||||
return response.payload.pagination;
|
||||
};
|
||||
|
||||
const useQueryResponseLoading = (): boolean => {
|
||||
const { isLoading } = useQueryResponse();
|
||||
return isLoading;
|
||||
};
|
||||
|
||||
export {
|
||||
QueryResponseProvider,
|
||||
useQueryResponse,
|
||||
useQueryResponseData,
|
||||
useQueryResponsePagination,
|
||||
useQueryResponseLoading,
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
import { useMemo } from "react";
|
||||
import { useTable, ColumnInstance, Row } from "react-table";
|
||||
import { CustomHeaderColumn } from "./columns/CustomHeaderColumn";
|
||||
import { CustomRow } from "./columns/CustomRow";
|
||||
import {
|
||||
useQueryResponseData,
|
||||
useQueryResponseLoading,
|
||||
} from "../core/QueryResponseProvider";
|
||||
import { usersColumns } from "./columns/_columns";
|
||||
import { User } from "../../core/_models";
|
||||
import { UsersListLoading } from "../components/loading/UsersListLoading";
|
||||
import { UsersListPagination } from "../components/pagination/UsersListPagination";
|
||||
import { KTCardBody } from "../../../../../_digifi/helpers";
|
||||
|
||||
const UsersTable = () => {
|
||||
const users = useQueryResponseData();
|
||||
// console.log('users', users)
|
||||
const isLoading = useQueryResponseLoading();
|
||||
const data = useMemo(() => users, [users]);
|
||||
const columns = useMemo(() => usersColumns, []);
|
||||
const { getTableProps, getTableBodyProps, headers, rows, prepareRow } =
|
||||
useTable({
|
||||
columns,
|
||||
data,
|
||||
});
|
||||
|
||||
return (
|
||||
<KTCardBody className="py-4">
|
||||
<div className="table-responsive">
|
||||
<table
|
||||
id="kt_table_users"
|
||||
className="table align-middle table-row-dashed fs-6 gy-5 dataTable no-footer"
|
||||
{...getTableProps()}
|
||||
>
|
||||
<thead>
|
||||
<tr className="text-start text-muted fw-bolder fs-7 text-uppercase gs-0">
|
||||
{headers.map((column: ColumnInstance<User>) => (
|
||||
<CustomHeaderColumn key={column.id} column={column} />
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-gray-600 fw-bold" {...getTableBodyProps()}>
|
||||
{rows.length > 0 ? (
|
||||
rows.map((row: Row<User>, i) => {
|
||||
prepareRow(row);
|
||||
return <CustomRow row={row} key={`row-${i}-${row.id}`} />;
|
||||
})
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={7}>
|
||||
<div className="d-flex text-center w-100 align-content-center justify-content-center">
|
||||
No matching records found
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<UsersListPagination />
|
||||
{isLoading && <UsersListLoading />}
|
||||
</KTCardBody>
|
||||
);
|
||||
};
|
||||
|
||||
export { UsersTable };
|
||||
@@ -0,0 +1,14 @@
|
||||
import { FC } from "react";
|
||||
import { NewDateTimeFormatter } from "../../../../../../_digifi/lib/NewDateTimeFormatter";
|
||||
|
||||
type Props = {
|
||||
added?: string;
|
||||
};
|
||||
|
||||
const AddedCell: FC<Props> = ({ added }) => (
|
||||
<div className="badge badge-light fw-bolder">
|
||||
{NewDateTimeFormatter(added)}
|
||||
</div>
|
||||
);
|
||||
|
||||
export { AddedCell };
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import {FC} from 'react'
|
||||
import {ColumnInstance} from 'react-table'
|
||||
import {User} from '../../core/_models'
|
||||
import {User} from '../../../core/_models'
|
||||
|
||||
type Props = {
|
||||
column: ColumnInstance<User>
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import clsx from 'clsx'
|
||||
import {FC} from 'react'
|
||||
import {Row} from 'react-table'
|
||||
import {User} from '../../core/_models'
|
||||
import {User} from '../../../core/_models'
|
||||
|
||||
type Props = {
|
||||
row: Row<User>
|
||||
@@ -0,0 +1,75 @@
|
||||
import { FC, useEffect } from "react";
|
||||
import { useMutation, useQueryClient } from "react-query";
|
||||
import { MenuComponent } from "../../../../../../_digifi/assets/ts/components";
|
||||
import { ID, KTIcon, QUERIES } from "../../../../../../_digifi/helpers";
|
||||
import { useListView } from "../../core/ListViewProvider";
|
||||
import { useQueryResponse } from "../../core/QueryResponseProvider";
|
||||
import { deleteUser } from "../../../core/_requests";
|
||||
|
||||
type Props = {
|
||||
id: ID;
|
||||
};
|
||||
|
||||
const UserActionsCell: FC<Props> = ({ id }) => {
|
||||
const { setItemIdForUpdate } = useListView();
|
||||
const { query } = useQueryResponse();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
useEffect(() => {
|
||||
MenuComponent.reinitialization();
|
||||
}, []);
|
||||
|
||||
const openEditModal = () => {
|
||||
setItemIdForUpdate(id);
|
||||
};
|
||||
|
||||
const deleteItem = useMutation(() => deleteUser(id), {
|
||||
// 💡 response of the mutation is passed to onSuccess
|
||||
onSuccess: () => {
|
||||
// ✅ update detail view directly
|
||||
queryClient.invalidateQueries([`${QUERIES.USERS_LIST}-${query}`]);
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<a
|
||||
href="#"
|
||||
className="btn btn-light btn-active-light-primary btn-sm"
|
||||
data-kt-menu-trigger="click"
|
||||
data-kt-menu-placement="bottom-end"
|
||||
>
|
||||
Actions
|
||||
<KTIcon iconName="down" className="fs-5 m-0" />
|
||||
</a>
|
||||
{/* begin::Menu */}
|
||||
<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"
|
||||
data-kt-menu="true"
|
||||
>
|
||||
{/* begin::Menu item */}
|
||||
<div className="menu-item px-3">
|
||||
<a className="menu-link px-3" onClick={openEditModal}>
|
||||
Edit
|
||||
</a>
|
||||
</div>
|
||||
{/* end::Menu item */}
|
||||
|
||||
{/* begin::Menu item */}
|
||||
<div className="menu-item px-3">
|
||||
<a
|
||||
className="menu-link px-3"
|
||||
data-kt-users-table-filter="delete_row"
|
||||
onClick={async () => await deleteItem.mutateAsync()}
|
||||
>
|
||||
Delete
|
||||
</a>
|
||||
</div>
|
||||
{/* end::Menu item */}
|
||||
</div>
|
||||
{/* end::Menu */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export { UserActionsCell };
|
||||
@@ -0,0 +1,61 @@
|
||||
import clsx from "clsx";
|
||||
import { FC, PropsWithChildren, useMemo } from "react";
|
||||
import { HeaderProps } from "react-table";
|
||||
import { initialQueryState } from "../../../../../../_digifi/helpers";
|
||||
import { useQueryRequest } from "../../core/QueryRequestProvider";
|
||||
import { User } from "../../../core/_models";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
title?: string;
|
||||
tableProps: PropsWithChildren<HeaderProps<User>>;
|
||||
};
|
||||
const UserCustomHeader: FC<Props> = ({ className, title, tableProps }) => {
|
||||
const id = tableProps.column.id;
|
||||
const { state, updateState } = useQueryRequest();
|
||||
|
||||
const isSelectedForSorting = useMemo(() => {
|
||||
return state.sort && state.sort === id;
|
||||
}, [state, id]);
|
||||
const order: "asc" | "desc" | undefined = useMemo(() => state.order, [state]);
|
||||
|
||||
const sortColumn = () => {
|
||||
// avoid sorting for these columns
|
||||
if (id === "actions" || id === "selection") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isSelectedForSorting) {
|
||||
// enable sort asc
|
||||
updateState({ sort: id, order: "asc", ...initialQueryState });
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSelectedForSorting && order !== undefined) {
|
||||
if (order === "asc") {
|
||||
// enable sort desc
|
||||
updateState({ sort: id, order: "desc", ...initialQueryState });
|
||||
return;
|
||||
}
|
||||
|
||||
// disable sort
|
||||
updateState({ sort: undefined, order: undefined, ...initialQueryState });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<th
|
||||
{...tableProps.column.getHeaderProps()}
|
||||
className={clsx(
|
||||
className,
|
||||
isSelectedForSorting && order !== undefined && `table-sort-${order}`
|
||||
)}
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={sortColumn}
|
||||
>
|
||||
{title}
|
||||
</th>
|
||||
);
|
||||
};
|
||||
|
||||
export { UserCustomHeader };
|
||||
@@ -0,0 +1,46 @@
|
||||
import clsx from "clsx";
|
||||
import { FC } from "react";
|
||||
import { toAbsoluteUrl } from "../../../../../../_digifi/helpers";
|
||||
import { User } from "../../../core/_models";
|
||||
|
||||
type Props = {
|
||||
user: User;
|
||||
};
|
||||
|
||||
const UserInfoCell: FC<Props> = ({ user }) => (
|
||||
<div className="d-flex align-items-center">
|
||||
{/* begin:: Avatar */}
|
||||
<div className="symbol symbol-circle symbol-50px overflow-hidden me-3">
|
||||
<a href="#">
|
||||
{user.avatar ? (
|
||||
<div className="symbol-label">
|
||||
<img
|
||||
src={toAbsoluteUrl(`media/${user.avatar}`)}
|
||||
alt={user.name}
|
||||
className="w-100"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={clsx(
|
||||
"symbol-label fs-3",
|
||||
`bg-light-${user.initials?.state}`,
|
||||
`text-${user.initials?.state}`
|
||||
)}
|
||||
>
|
||||
{user.firstname?.substring(0, 1).toUpperCase()}{" "}
|
||||
{user.lastname?.substring(0, 1).toUpperCase()}
|
||||
</div>
|
||||
)}
|
||||
</a>
|
||||
</div>
|
||||
<div className="d-flex flex-column">
|
||||
<a href="#" className="text-gray-800 text-hover-primary mb-1">
|
||||
{user.firstname} {user.lastname}
|
||||
</a>
|
||||
<span>{user.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export { UserInfoCell };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user