Compare commits

..

5 Commits

Author SHA1 Message Date
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
9 changed files with 955 additions and 296 deletions
@@ -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 */}
@@ -1,6 +1,6 @@
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'
@@ -9,8 +9,6 @@ import { ToolbarWrapper } from '../../../../../_digifi/layout/components/toolbar
import { Content } from '../../../../../_digifi/layout/components/content' import { Content } from '../../../../../_digifi/layout/components/content'
const UsersList = () => { const UsersList = () => {
const response = useAllResponse()
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
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: '',
} }
@@ -19,6 +19,33 @@ 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 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 +53,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 +71,9 @@ const deleteSelectedUsers = (userIds: Array<ID>): Promise<void> => {
export { export {
getEmployersList, getEmployersList,
createUser,
deleteUser, deleteUser,
deleteSelectedUsers, deleteSelectedUsers,
getUserById, getUserById,
createUser,
updateUser, updateUser,
}; };
@@ -56,6 +56,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={openEditModal}>
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("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 */}