diff --git a/src/hooks/useQuery.jsx b/src/hooks/useQuery.jsx
new file mode 100644
index 0000000..63dc613
--- /dev/null
+++ b/src/hooks/useQuery.jsx
@@ -0,0 +1,10 @@
+import { useMemo } from 'react';
+import { useLocation } from 'react-router-dom';
+
+const useQuery = () => {
+ const { search } = useLocation();
+
+ return useMemo(() => new URLSearchParams(search), [search]);
+};
+
+export default useQuery;
diff --git a/src/hooks/useScript.js b/src/hooks/useScript.js
new file mode 100644
index 0000000..5e9fdb2
--- /dev/null
+++ b/src/hooks/useScript.js
@@ -0,0 +1,55 @@
+import { useState, useEffect } from 'react';
+
+const useScript = ({
+ src,
+ async = true,
+ type = 'text/javascript',
+ noModule = false,
+}) => {
+ const [status, setStatus] = useState(src ? 'loading' : 'idle');
+
+ useEffect(() => {
+ if (!src) {
+ setStatus('idle');
+ return;
+ }
+
+ // Check if the script is already present
+ let script = document.querySelector(`script[src="${src}"]`);
+
+ if (!script) {
+ // Create script element and add it to the document head
+ script = document.createElement('script');
+ script.src = src;
+ script.async = async;
+ script.type = type;
+ script.noModule = noModule;
+
+ // Set initial status
+ setStatus('loading');
+
+ // Script event listeners
+ const onScriptLoad = () => setStatus('ready');
+ const onScriptError = () => setStatus('error');
+
+ script.addEventListener('load', onScriptLoad);
+ script.addEventListener('error', onScriptError);
+
+ document.head.appendChild(script);
+
+ // Cleanup function
+ return () => {
+ script.removeEventListener('load', onScriptLoad);
+ script.removeEventListener('error', onScriptError);
+ document.head.removeChild(script);
+ };
+ } else {
+ // If script is already present, update status accordingly
+ setStatus('ready');
+ }
+ }, [src, async, type, noModule]);
+
+ return status;
+};
+
+export default useScript;
diff --git a/src/routes/index.js b/src/routes/index.js
index 6d7efa8..084fa0f 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -10,10 +10,11 @@ import MockPayment from "../views/MockPayment";
import Users from "../views/User";
import ClinicSetup from "../views/ClinicSetup";
import ClinicTranscripts from "../views/ClinicTranscripts";
-import ContractManagement from "../views/ContractManagement";
import MasterDataManagement from "../views/MasterData";
import PaymentManagement from "../views/PaymentManagement";
import ClinicDocUpdater from "../views/ClinicDocUpdate";
+import ForgotPassword from "../views/ForgotPassword";
+import ResetPassword from "../views/ResetPassword";
export const routesData = [
{
@@ -43,6 +44,14 @@ export const routesData = [
path: "signup/your-details",
component: YourDetailsForm,
},
+ {
+ path: "forgotpassword",
+ component: ForgotPassword,
+ },
+ {
+ path: 'reset-password',
+ component: ResetPassword,
+ },
],
},
];
diff --git a/src/services/auth.services.js b/src/services/auth.services.js
index da183d3..833785a 100644
--- a/src/services/auth.services.js
+++ b/src/services/auth.services.js
@@ -1,38 +1,83 @@
import { axiosInstance } from "../config/api";
export const signup = (data) => {
- const url = '/auth/register';
- return new Promise((resolve, reject) => {
- axiosInstance
- .post(url, data)
- .then((response) => resolve(response))
- .catch((err) => reject(err));
- });
- };
+ const url = "/auth/register";
+ return new Promise((resolve, reject) => {
+ axiosInstance
+ .post(url, data)
+ .then((response) => resolve(response))
+ .catch((err) => reject(err));
+ });
+};
+export const createAdmin = (data) => {
+ const url = "/admin/user";
+ return new Promise((resolve, reject) => {
+ axiosInstance
+ .post(url, data)
+ .then((response) => resolve(response))
+ .catch((err) => reject(err));
+ });
+};
+
+export const getAdmins = (params) => {
+
+ let searchParams = new URLSearchParams();
+ searchParams.append("size", params?.pagination?.pageSize ?? 10);
+ searchParams.append("page", params?.pagination.pageIndex ?? 0);
+ searchParams.append("search", params?.globalFilter ?? "");
+
+ const url = `/admin/?${searchParams.toString()}`;
+ return new Promise((resolve, reject) => {
+ axiosInstance
+ .get(url)
+ .then((response) => resolve(response))
+ .catch((err) => reject(err));
+ });
+};
export const getEmailOtp = (data) => {
- const url = '/auth/send-otp';
- return new Promise((resolve, reject) => {
- axiosInstance
- .post(url, null, { params: data })
- .then((response) => resolve(response))
- .catch((err) => reject(err));
- });
- };
+ const url = "/auth/send-otp";
+ return new Promise((resolve, reject) => {
+ axiosInstance
+ .post(url, null, { params: data })
+ .then((response) => resolve(response))
+ .catch((err) => reject(err));
+ });
+};
- export const verifyOtp = (data) => {
- const url = '/auth/verify-otp';
- return new Promise((resolve, reject) => {
- axiosInstance
- .post(url, data)
- .catch((err) => {
- if(err.status==200){
- resolve(err)
- }else{
- reject(err)
- }
- })
- .then((response) => resolve(response))
- });
- };
\ No newline at end of file
+export const verifyOtp = (data) => {
+ const url = "/auth/verify-otp";
+ return new Promise((resolve, reject) => {
+ axiosInstance
+ .post(url, data)
+ .catch((err) => {
+ if (err.status == 200) {
+ resolve(err);
+ } else {
+ reject(err);
+ }
+ })
+ .then((response) => resolve(response));
+ });
+};
+
+export const forgotPassword = (data) => {
+ const url = "/auth/admin/forget-password";
+ return new Promise((resolve, reject) => {
+ axiosInstance
+ .post(url, null, { params: data })
+ .then((response) => resolve(response))
+ .catch((err) => reject(err));
+ });
+};
+
+export const resetPassword = (data) => {
+ const url = "/auth/admin/reset-password";
+ return new Promise((resolve, reject) => {
+ axiosInstance
+ .post(url, data)
+ .then((response) => resolve(response))
+ .catch((err) => reject(err));
+ });
+};
diff --git a/src/views/ForgotPassword/ForgotPasswordForm.jsx b/src/views/ForgotPassword/ForgotPasswordForm.jsx
new file mode 100644
index 0000000..90cd8c9
--- /dev/null
+++ b/src/views/ForgotPassword/ForgotPasswordForm.jsx
@@ -0,0 +1,150 @@
+import { LoadingButton } from '@mui/lab';
+import { Box, InputLabel, TextField, Typography } from '@mui/material';
+import { useFormik } from 'formik';
+import { useRef } from 'react';
+import * as Yup from 'yup';
+import { NOTIFICATION } from '../../constants';
+import { forgotPassword } from '../../services/auth.services';
+import { pushNotification } from '../../utils/notification';
+import { useStyles } from './forgotPasswordStyles';
+
+const validationSchema = Yup.object().shape({
+ email: Yup.string()
+ .email('Invalid Email Address')
+ .required('Email is required'),
+});
+
+function ForgotPasswordForm({ onSubmit, setEmail }) {
+ const classes = useStyles();
+
+ const emailRef = useRef(null);
+
+ const formik = useFormik({
+ initialValues: { email: '' },
+ validationSchema,
+ onSubmit: async (values) => {
+ try {
+ const response = await forgotPassword(values);
+ if (response.status === 200) {
+ // setTimer(15);
+ setEmail(values.email);
+ // setShowButton(false);
+ pushNotification(response?.data?.message, NOTIFICATION.SUCCESS);
+ onSubmit();
+ }
+ } catch (error) {
+ if (error.response?.data?.message) {
+ formik.setErrors({
+ email: error?.response?.data?.message,
+ });
+ emailRef.current.focus();
+ }
+ } finally {
+ formik.setSubmitting(false);
+ }
+ },
+ });
+
+ const handleSubmitClick = async (e) => {
+ e.preventDefault();
+ const formikError = await formik.validateForm();
+ const errors = Object.keys(formikError);
+
+ if (errors.length) {
+ // Find the first invalid field and focus it
+ const firstErrorField = errors[0];
+ const firstErrorRef = emailRef?.current;
+
+ if (firstErrorRef) {
+ // Scroll to the first invalid field smoothly
+ if (typeof firstErrorRef?.scrollIntoView === 'function') {
+ firstErrorRef?.scrollIntoView({
+ behavior: 'smooth',
+ block: 'center',
+ });
+ }
+
+ // Focus the field after a slight delay (to ensure scrolling completes first)
+ setTimeout(() => firstErrorRef.focus(), 300);
+ }
+
+ // Show error notification
+ if (formik?.touched[firstErrorField])
+ pushNotification(formikError[firstErrorField], NOTIFICATION.ERROR);
+ }
+
+ formik.handleSubmit();
+ };
+
+ return (
+
+
+
+ );
+}
+
+export default ForgotPasswordForm;
diff --git a/src/views/ForgotPassword/forgotPasswordStyles.js b/src/views/ForgotPassword/forgotPasswordStyles.js
new file mode 100644
index 0000000..9de1bb3
--- /dev/null
+++ b/src/views/ForgotPassword/forgotPasswordStyles.js
@@ -0,0 +1,189 @@
+import makeStyles from '@mui/styles/makeStyles';
+import backgroundImage from '../../assets/images/background/background.jpg';
+import { pxToRem } from '../../theme/typography';
+
+export const useStyles = makeStyles((theme) => ({
+ root: {
+ height: '100vh',
+ backgroundImage: `url(${backgroundImage})`,
+ backgroundSize: 'cover',
+ backgroundRepeat: 'no-repeat',
+ backgroundPosition: 'center',
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ card: {
+ width: '370px',
+ maxHeight: '835px',
+ [theme.breakpoints.up('sm')]: {
+ width: '500px',
+ },
+ [theme.breakpoints.up('md')]: {
+ width: '500px',
+ },
+ [theme.breakpoints.up('lg')]: {
+ width: '500px',
+ },
+ [theme.breakpoints.up('xl')]: {
+ width: '500px',
+ maxHeight: '735px',
+ },
+ boxShadow: 'none',
+ borderRadius: theme.spacing(3.8),
+ opacity: '0.95',
+ },
+ forgotPasswordHeader: {
+ marginTop: theme.spacing(4.5),
+ },
+ right: {
+ textAlign: 'center',
+ alignItems: 'center',
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ form: {
+ width: '65%',
+ },
+ formBody: {
+ justifyContent: 'start',
+ display: 'flex',
+ flexDirection: 'column',
+ [theme.breakpoints.up('md')]: {
+ // height: '162.5px',
+ },
+ [theme.breakpoints.up('lg')]: {
+ // height: '234px',
+ },
+ [theme.breakpoints.up('xl')]: {
+ // height: '325px',
+ },
+
+ '& .MuiTextField-root': {
+ marginTop: theme.spacing(0.8),
+ [theme.breakpoints.up('md')]: {
+ marginTop: theme.spacing(0.8),
+ },
+ [theme.breakpoints.up('lg')]: {
+ marginTop: theme.spacing(0.8),
+ },
+ [theme.breakpoints.up('xl')]: {
+ marginTop: theme.spacing(4),
+ },
+ },
+ },
+ formHeader: {
+ marginTop: theme.spacing(1.2),
+ marginBottom: theme.spacing(3),
+ },
+ formFooter: {
+ marginBottom: theme.spacing(4.0),
+ },
+ heading: {
+ fontFamily: 'Inter-Bold',
+ // width: '70%',
+ textAlign: 'center',
+ display: 'flex',
+ justifyContent: 'flex-start',
+ lineHeight: 1.2,
+ fontSize: pxToRem(27),
+ [theme.breakpoints.up('md')]: {
+ fontSize: pxToRem(27),
+ },
+ [theme.breakpoints.up('lg')]: {
+ fontSize: pxToRem(27),
+ },
+ [theme.breakpoints.up('xl')]: {
+ fontSize: pxToRem(27),
+ },
+ },
+ subHeading: {
+ lineHeight: 1.1,
+ textAlign: 'left',
+ fontSize: pxToRem(12),
+ marginTop: theme.spacing(0),
+ [theme.breakpoints.up('md')]: {
+ fontSize: pxToRem(13),
+ marginTop: theme.spacing(0),
+ },
+ [theme.breakpoints.up('lg')]: {
+ fontSize: pxToRem(13),
+ marginTop: theme.spacing(0),
+ },
+ [theme.breakpoints.up('xl')]: {
+ fontSize: pxToRem(13),
+ marginTop: theme.spacing(0),
+ },
+ },
+ labelStyle: {
+ marginBottom: '0px',
+ fontSize: pxToRem(3.2),
+ color: theme.palette.grey[10],
+ // paddingBottom: '20px',
+ },
+ inputTitle: {
+ '&.MuiTypography-body1': {
+ fontSize: pxToRem(12),
+ color: theme.palette.grey[10],
+ },
+ fontSize: pxToRem(12),
+ textAlign: 'left',
+ // opacity: '0.54',
+ color: theme.palette.grey[10],
+ marginBottom: theme.spacing(0.6),
+ marginLeft: theme.spacing(0.6),
+ },
+ icon: {
+ cursor: 'pointer',
+ paddingLeft: '30px',
+ display: 'flex',
+ alignItems: 'center',
+ width: '50px',
+ [theme.breakpoints.up('md')]: {
+ width: '50px',
+ },
+ [theme.breakpoints.up('lg')]: {
+ width: '50px',
+ },
+ [theme.breakpoints.up('xl')]: {
+ width: '50px',
+ },
+ },
+ submitButton: {
+ fontFamily: 'Inter-Regular',
+ fontWeight: 500,
+ marginTop: theme.spacing(1.0),
+ marginBottom: theme.spacing(3.0),
+ fontSize: pxToRem(12.8),
+ justifyContent: 'center',
+ borderRadius: '6px',
+ padding: '20px',
+ [theme.breakpoints.up('md')]: {
+ padding: '20px',
+ fontSize: pxToRem(12.8),
+ },
+ [theme.breakpoints.up('lg')]: {
+ padding: '20px',
+ fontSize: pxToRem(12.8),
+ },
+ [theme.breakpoints.up('xl')]: {
+ height: '10px',
+ fontSize: pxToRem(12.8),
+ },
+ },
+ sendEmailText: {
+ marginTop: theme.spacing(2.0),
+ textAlign: 'left',
+ fontSize: pxToRem(10.4),
+ },
+ emailText: {
+ lineHeight: 1.1,
+ fontSize: pxToRem(12.48),
+ },
+ emailSentMainDiv: {
+ width: '70%',
+ paddingTop: theme.spacing(3.4),
+ paddingBottom: theme.spacing(7.0),
+ fontSize: pxToRem(12.8),
+ },
+}));
diff --git a/src/views/ForgotPassword/index.jsx b/src/views/ForgotPassword/index.jsx
new file mode 100644
index 0000000..777a3d6
--- /dev/null
+++ b/src/views/ForgotPassword/index.jsx
@@ -0,0 +1,82 @@
+import { ArrowBackOutlined } from '@mui/icons-material';
+import { Box, Card, Grid, Typography } from '@mui/material';
+import { useNavigate } from 'react-router-dom';
+import ForgotPasswordForm from './ForgotPasswordForm';
+import { useStyles } from './forgotPasswordStyles';
+import { useState } from 'react';
+
+function ForgotPassword() {
+ const navigate = useNavigate();
+ const classes = useStyles();
+ const [resetPasswordLinkSend, setResetPasswordLinkSend] = useState(false);
+ const [email, setEmail] = useState('');
+
+ const handleFormSubmit = () => {
+ setResetPasswordLinkSend(true);
+ };
+ const handleGoBackClick = () => {
+ navigate('/auth/login');
+ };
+ return (
+
+
+
+ {!resetPasswordLinkSend ? (
+
+
+
+
+
+
+
+ Reset your Password
+
+
+
+
+
+ ) : (
+
+ {/* */}
+
+ Email Sent
+
+
+ {/* {` An email with instructions on how to reset your password has
+ been sent to ${email}. It is valid for next 24 hrs.
+ Check your spam or junk folder if you don’t see the email in
+ your inbox.`} */}
+ {`An email with instructions on how to reset your password has
+ been sent to `}
+ {email}
+ {`. It is valid for the next 24 hrs. Check your spam or junk folder if you don’t see the email in
+ your inbox.`}
+
+ {/* */}
+
+ )}
+
+
+
+ );
+}
+
+export default ForgotPassword;
diff --git a/src/views/ResetPassword/ResetPasswordForm.jsx b/src/views/ResetPassword/ResetPasswordForm.jsx
new file mode 100644
index 0000000..5e91938
--- /dev/null
+++ b/src/views/ResetPassword/ResetPasswordForm.jsx
@@ -0,0 +1,259 @@
+import { LoadingButton } from '@mui/lab';
+import {
+ Box,
+ IconButton,
+ InputLabel,
+ TextField,
+ Typography,
+} from '@mui/material';
+import { useFormik } from 'formik';
+import * as Yup from 'yup';
+import { useStyles } from './resetPasswordStyles';
+import { useRef, useState } from 'react';
+import PasswordValidation from '../Login/component/PasswordValidation';
+import { VisibilityOffOutlined, VisibilityOutlined } from '@mui/icons-material';
+import useQuery from '../../hooks/useQuery';
+import {
+ passwordLengthRegex,
+ passwordLetterRegex,
+ passwordNumberRegex,
+ passwordSpacesRegex,
+} from '../../utils/regex';
+import { pushNotification } from '../../utils/notification';
+import { NOTIFICATION } from '../../constants';
+import { resetPassword } from '../../services/auth.services';
+
+const validationSchema = Yup.object().shape({
+ password: Yup.string()
+ .matches(passwordLengthRegex, 'Password must be at least 8 characters')
+ .matches(passwordLetterRegex, 'Password must contain at least one letter')
+ .matches(passwordNumberRegex, 'Password must contain at least one number')
+ .matches(passwordSpacesRegex, 'Password must not start or end with a space')
+ .required('Password is required'),
+ confirmPassword: Yup.string()
+ .oneOf([Yup.ref('password'), null], 'Passwords must match')
+ .required('Passwords must match'),
+});
+
+function ResetPasswordForm({ onSubmit }) {
+ const query = useQuery();
+ const classes = useStyles();
+ const [showPassword, setShowPassword] = useState(false);
+ const [showConfirmPassword, setShowConfirmPassword] = useState(false);
+
+ const fieldRefs = {
+ password: useRef(null),
+ confirmPassword: useRef(null),
+ };
+
+ const formik = useFormik({
+ initialValues: { password: '', confirmPassword: '' },
+ validationSchema,
+ onSubmit: (values) => handleSubmit(values, formik),
+ });
+
+ const handleSubmit = async (values, formik) => {
+ try {
+ values.token = query.get('token');
+
+ const requestBody = {
+ token: values.token,
+ password: values.password,
+ };
+
+ const response = await resetPassword(requestBody);
+ if (response.status === 200) {
+ pushNotification(response?.data?.message, NOTIFICATION.SUCCESS);
+ }
+ onSubmit();
+ } catch (error) {
+ return;
+ } finally {
+ formik.setSubmitting(false);
+ formik.resetForm();
+ }
+ };
+ const handleTogglePasswordVisibility = (field) => {
+ if (field === 'password') {
+ setShowPassword((prevShowPassword) => !prevShowPassword);
+ } else if (field === 'confirmPassword') {
+ setShowConfirmPassword(
+ (prevShowConfirmPassword) => !prevShowConfirmPassword
+ );
+ }
+ };
+
+ const handleSubmitClick = async (e) => {
+ e.preventDefault();
+ const formikErrors = await formik.validateForm();
+ const errors = Object.keys(formikErrors);
+
+ if (errors.length) {
+ // Find the first invalid field and focus it
+ const firstErrorField = errors[0];
+ const firstErrorRef = fieldRefs[firstErrorField]?.current;
+
+ if (firstErrorRef) {
+ // Scroll to the first invalid field smoothly
+ if (typeof firstErrorRef?.scrollIntoView === 'function') {
+ firstErrorRef?.scrollIntoView({
+ behavior: 'smooth',
+ block: 'center',
+ });
+ }
+
+ // Focus the field after a slight delay (to ensure scrolling completes first)
+ setTimeout(() => firstErrorRef.focus(), 300);
+ }
+
+ // Show error notification
+ if (formik?.touched[firstErrorField])
+ pushNotification(formikErrors[firstErrorField], NOTIFICATION.ERROR);
+ }
+
+ formik.handleSubmit();
+ };
+
+ return (
+
+
+
+ );
+}
+
+export default ResetPasswordForm;
diff --git a/src/views/ResetPassword/index.jsx b/src/views/ResetPassword/index.jsx
new file mode 100644
index 0000000..1f63b5a
--- /dev/null
+++ b/src/views/ResetPassword/index.jsx
@@ -0,0 +1,94 @@
+import { Box, Card, Grid, Typography } from '@mui/material';
+import { useLocation, useNavigate } from 'react-router-dom';
+import ResetPasswordForm from './ResetPasswordForm';
+import { useStyles } from './resetPasswordStyles';
+import { useState } from 'react';
+import ResetPasswordSuccessIcon from '../../assets/images/icon/ResetPasswordSuccessIcon.png';
+import { LoadingButton } from '@mui/lab';
+
+function ResetPassword() {
+ const navigate = useNavigate();
+ const location = useLocation();
+ const classes = useStyles();
+ const [resetPasswordLinkSend, setResetPasswordLinkSend] = useState(false);
+ const handleFormSubmit = () => {
+ setResetPasswordLinkSend(true);
+ };
+
+ // Function to get URL parameters
+ const getQueryParams = () => new URLSearchParams(location.search);
+
+ // Determine if we are resetting or creating a password based on URL params
+ const queryParams = getQueryParams();
+ const isCreatePassword = queryParams.get('createPassword') === 'true';
+
+ const headingText = !isCreatePassword ? 'Reset' : 'Create';
+ const successMessage = !isCreatePassword
+ ? 'Password Reset Successful'
+ : 'Create Password Successful';
+
+ return (
+
+
+
+ {!resetPasswordLinkSend ? (
+
+
+
+
+
+ {headingText} your Password
+
+
+
+
+
+ ) : (
+
+
+
+
+
+
+ {successMessage}
+
+
+
+ Awesome, you have successfully{' '}
+ {!isCreatePassword ? 'updated' : 'created'} your password.
+
+
+ navigate('/auth/login')}
+ >
+ Back to Login
+
+
+
+ )}
+
+
+
+ );
+}
+
+export default ResetPassword;
diff --git a/src/views/ResetPassword/resetPasswordStyles.js b/src/views/ResetPassword/resetPasswordStyles.js
new file mode 100644
index 0000000..f2a8852
--- /dev/null
+++ b/src/views/ResetPassword/resetPasswordStyles.js
@@ -0,0 +1,211 @@
+import makeStyles from '@mui/styles/makeStyles';
+import backgroundImage from '../../assets/images/background/background.jpg';
+import { pxToRem } from '../../theme/typography';
+
+export const useStyles = makeStyles((theme) => ({
+ root: {
+ height: '100vh',
+ backgroundImage: `url(${backgroundImage})`,
+ backgroundSize: 'cover',
+ backgroundRepeat: 'no-repeat',
+ backgroundPosition: 'center',
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ card: {
+ width: '370px',
+ maxHeight: '835px',
+ [theme.breakpoints.up('sm')]: {
+ width: '500px',
+ },
+ [theme.breakpoints.up('md')]: {
+ width: '500px',
+ },
+ [theme.breakpoints.up('lg')]: {
+ width: '500px',
+ },
+ [theme.breakpoints.up('xl')]: {
+ width: '500px',
+ maxHeight: '735px',
+ },
+ boxShadow: 'none',
+ borderRadius: theme.spacing(3.8),
+ opacity: '0.95',
+ },
+ forgotPasswordHeader: {
+ marginTop: theme.spacing(4.5),
+ },
+ right: {
+ textAlign: 'center',
+ alignItems: 'center',
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ form: {
+ width: '65%',
+ },
+ formBody: {
+ justifyContent: 'center',
+ display: 'flex',
+ flexDirection: 'column',
+ [theme.breakpoints.up('md')]: {
+ // height: '162.5px',
+ },
+ [theme.breakpoints.up('lg')]: {
+ // height: '234px',
+ },
+ [theme.breakpoints.up('xl')]: {
+ // height: '325px',
+ },
+
+ '& .MuiTextField-root': {
+ marginTop: theme.spacing(0.4),
+ [theme.breakpoints.up('md')]: {
+ marginTop: theme.spacing(0.4),
+ },
+ [theme.breakpoints.up('lg')]: {
+ marginTop: theme.spacing(0.4),
+ },
+ [theme.breakpoints.up('xl')]: {
+ marginTop: theme.spacing(0.4),
+ },
+ },
+ },
+ formHeader: {
+ marginTop: theme.spacing(1.2),
+ marginBottom: theme.spacing(3),
+ },
+ heading: {
+ fontFamily: 'Inter-Bold',
+ // width: '70%',
+ textAlign: 'center',
+ display: 'flex',
+ justifyContent: 'flex-start',
+ lineHeight: 1.2,
+ fontSize: pxToRem(27),
+ [theme.breakpoints.up('md')]: {
+ fontSize: pxToRem(27),
+ },
+ [theme.breakpoints.up('lg')]: {
+ fontSize: pxToRem(27),
+ },
+ [theme.breakpoints.up('xl')]: {
+ fontSize: pxToRem(27),
+ },
+ },
+ successIcon: {
+ display: 'flex',
+ justifyContent: 'center',
+ marginTop: theme.spacing(1.1),
+ },
+ resetPasswordheading: {
+ // width: '70%',
+ display: 'flex',
+ fontFamily: 'Inter-Bold',
+ justifyContent: 'center',
+ marginTop: theme.spacing(3.8),
+ lineHeight: 1.2,
+ fontSize: pxToRem(27),
+ [theme.breakpoints.up('md')]: {
+ fontSize: pxToRem(27),
+ },
+ [theme.breakpoints.up('lg')]: {
+ fontSize: pxToRem(27),
+ },
+ [theme.breakpoints.up('xl')]: {
+ fontSize: pxToRem(27),
+ },
+ },
+ subHeading: {
+ width: '90%',
+ lineHeight: 1.1,
+ textAlign: 'left',
+ fontSize: pxToRem(12),
+ marginTop: theme.spacing(0),
+ marginBottom: theme.spacing(1.0),
+ [theme.breakpoints.up('md')]: {
+ fontSize: pxToRem(13),
+ marginTop: theme.spacing(0),
+ },
+ [theme.breakpoints.up('lg')]: {
+ fontSize: pxToRem(13),
+ marginTop: theme.spacing(0),
+ },
+ [theme.breakpoints.up('xl')]: {
+ fontSize: pxToRem(13),
+ marginTop: theme.spacing(0),
+ },
+ },
+ resetPasswordTextField: {
+ marginTop: '0px',
+ },
+ confirmResetBox: {
+ marginTop: theme.spacing(1.2),
+ },
+ passwordCheckListTitle: {
+ color: 'grey',
+ fontSize: pxToRem(14.4),
+ fontStyle: 'italic',
+ textAlign: 'left',
+ marginBottom: theme.spacing(0.4),
+ },
+ passwordCheckList: {
+ color: 'grey',
+ fontSize: pxToRem(14.4),
+ paddingTop: '2px',
+ fontStyle: 'italic',
+ },
+ labelStyle: {
+ marginBottom: '0px',
+ fontSize: pxToRem(3.2),
+ textAlign: 'left',
+ color: theme.palette.grey[10],
+ // paddingBottom: '20px',
+ },
+ inputTitle: {
+ '&.MuiTypography-body1': {
+ fontSize: pxToRem(12),
+ color: theme.palette.grey[10],
+ },
+ fontSize: pxToRem(12),
+ // opacity: '0.54',
+ color: theme.palette.grey[10],
+ marginBottom: theme.spacing(0.6),
+ marginLeft: theme.spacing(0.6),
+ },
+ submitButton: {
+ fontFamily: 'Inter-Regular',
+ fontWeight: 500,
+ marginTop: theme.spacing(2.2),
+ marginBottom: theme.spacing(4.0),
+ fontSize: pxToRem(12.8),
+ justifyContent: 'center',
+ borderRadius: '6px',
+ padding: '20px',
+ [theme.breakpoints.up('md')]: {
+ padding: '20px',
+ fontSize: pxToRem(12.8),
+ },
+ [theme.breakpoints.up('lg')]: {
+ padding: '20px',
+ fontSize: pxToRem(12.8),
+ },
+ [theme.breakpoints.up('xl')]: {
+ height: '10px',
+ fontSize: pxToRem(12.8),
+ },
+ },
+ resetPasswordSuccessIcon: {
+ width: '17%',
+ },
+ sendEmailText: {
+ marginTop: theme.spacing(2.0),
+ textAlign: 'center',
+ fontSize: pxToRem(10.4),
+ },
+ emailText: {
+ lineHeight: 1.1,
+ fontSize: pxToRem(12.48),
+ },
+}));
diff --git a/src/views/StaffManagement/index.jsx b/src/views/StaffManagement/index.jsx
index b2b699e..369a4dd 100644
--- a/src/views/StaffManagement/index.jsx
+++ b/src/views/StaffManagement/index.jsx
@@ -1,9 +1,9 @@
-import AddIcon from '@mui/icons-material/Add';
-import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
-import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
-import CloseIcon from '@mui/icons-material/Close';
-import PersonAddIcon from '@mui/icons-material/PersonAdd';
-import SearchIcon from '@mui/icons-material/Search';
+import AddIcon from "@mui/icons-material/Add";
+import ArrowBackIosNewIcon from "@mui/icons-material/ArrowBackIosNew";
+import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
+import CloseIcon from "@mui/icons-material/Close";
+import PersonAddIcon from "@mui/icons-material/PersonAdd";
+import SearchIcon from "@mui/icons-material/Search";
import {
Alert,
Box,
@@ -18,7 +18,6 @@ import {
InputAdornment,
Paper,
Snackbar,
- Table,
TableBody,
TableCell,
TableContainer,
@@ -26,18 +25,26 @@ import {
TableRow,
TextField,
Typography,
-} from '@mui/material';
-import React, { useState } from 'react';
-import CustomBreadcrumbs from '../../components/CustomBreadcrumbs';
-import PageHeader from '../../components/PageHeader';
+} from "@mui/material";
+import React, { useMemo, useRef, useState } from "react";
+import CustomBreadcrumbs from "../../components/CustomBreadcrumbs";
+import PageHeader from "../../components/PageHeader";
+import { createAdmin, getAdmins } from "../../services/auth.services";
+import { pushNotification } from "../../utils/notification";
+import { NOTIFICATION } from "../../constants";
+import { useTheme } from "@emotion/react";
+import { useStyles } from "./staffStyles";
+import Table from "../../components/Table";
const StaffManagement = () => {
+ const theme = useTheme();
+ const classes = useStyles();
+ const ref = useRef(null);
// State for form fields
- const [firstName, setFirstName] = useState('');
- const [lastName, setLastName] = useState('');
- const [email, setEmail] = useState('');
- const [emailError, setEmailError] = useState('');
- const queryParams = new URLSearchParams(location.search);
+ const [firstName, setFirstName] = useState("");
+ const [lastName, setLastName] = useState("");
+ const [email, setEmail] = useState("");
+ const [emailError, setEmailError] = useState("");
// State for staff list
const [staffList, setStaffList] = useState([]);
@@ -46,7 +53,7 @@ const StaffManagement = () => {
const [openDialog, setOpenDialog] = useState(false);
// State for search
- const [searchQuery, setSearchQuery] = useState('');
+ const [searchQuery, setSearchQuery] = useState("");
// State for pagination
const [page, setPage] = useState(1);
@@ -55,8 +62,8 @@ const StaffManagement = () => {
// State for notification
const [notification, setNotification] = useState({
open: false,
- message: '',
- severity: 'success',
+ message: "",
+ severity: "success",
});
// Email validation function
@@ -73,41 +80,37 @@ const StaffManagement = () => {
const handleCloseDialog = () => {
setOpenDialog(false);
// Clear form
- setFirstName('');
- setLastName('');
- setEmail('');
- setEmailError('');
+ setFirstName("");
+ setLastName("");
+ setEmail("");
+ setEmailError("");
};
// Handle form submission
- const handleSubmit = (e) => {
+ const handleSubmit = async (e) => {
e.preventDefault();
// Validate email
if (!validateEmail(email)) {
- setEmailError('Please enter a valid email address');
+ setEmailError("Please enter a valid email address");
return;
}
- // Add new staff member
- const newStaff = {
- id: staffList.length + 1,
- firstName,
- lastName,
+ const payload = {
+ username: `${firstName} ${lastName}`,
email,
};
- setStaffList([...staffList, newStaff]);
+ const response = await createAdmin(payload);
+
+ if (response?.data?.data) {
+ pushNotification("Admin created successfully!", NOTIFICATION.SUCCESS);
+ } else {
+ pushNotification(response?.data?.message, NOTIFICATION.ERROR);
+ }
// Close dialog
handleCloseDialog();
-
- // Show success notification
- setNotification({
- open: true,
- message: 'Staff member added successfully!',
- severity: 'success',
- });
};
// Handle notification close
@@ -118,310 +121,392 @@ const StaffManagement = () => {
});
};
+ const getData = async (filters) => {
+ try {
+ // Remove the type parameter since it's not defined
+ let params = {
+ ...filters
+ };
+
+ const resp = await getAdmins(params);
+ console.log('API Response:', resp);
+
+ return {
+ data: resp?.data?.data?.data,
+ rowCount: resp?.data?.total || 0,
+ };
+ } catch (error) {
+ console.error('Error fetching admins:', error);
+ return {
+ data: [],
+ rowCount: 0,
+ };
+ }
+ };
+
// ...................breadcrumbs array........................
const breadcrumbs = [
{
- label: 'Dashboard',
- path: '/',
+ label: "Dashboard",
+ path: "/",
},
{
- label: 'Staff Management',
- path: '/staff',
+ label: "Staff Management",
+ path: "/staff",
},
];
- return (
-
- {/*
- Staff Management
- */}
+ const columns = useMemo(() => [
+ {
+ size: 100,
+ header: "Sr. No.",
+ Cell: (props) => {
+ const tableState = props?.table?.getState();
+ const serialNumber = (
+ props?.row?.index +
+ 1 +
+ tableState?.pagination?.pageIndex * tableState?.pagination?.pageSize
+ )
+ ?.toString()
+ ?.padStart(2, "0");
+ return {serialNumber};
+ },
+ enableSorting: false,
+ },
+ {
+ size: 280,
+ accessorKey: "username",
+ header: "User Name",
+ enableColumnFilter: false,
+ },
+ {
+ size: 220,
+ accessorKey: "email",
+ header: "User Email Address",
+ enableColumnFilter: false,
+ },
+ ]);
+ return (
+
}
- // addButtonDisabled={false}
- // permissionName={'CREATE_USERS'}
- // infiniteDropdown
+ // hideAddButton
+ addButtonIcon={}
+ onAddButtonClick={handleOpenDialog}
/>
-
-
-
- {/* Staff List Header with Add Button */}
-
-
- Staff List
-
-
- }
- onClick={handleOpenDialog}
- sx={{
- borderRadius: 50,
- textTransform: 'none',
- backgroundColor: '#ff3366',
- '&:hover': {
- backgroundColor: '#e61653',
- },
- }}
- >
- Add Admin
-
-
-
- {/* Search Box */}
-
- setSearchQuery(e.target.value)}
- InputProps={{
- startAdornment: (
-
-
-
- ),
- }}
- sx={{
- backgroundColor: '#fff',
- '& .MuiOutlinedInput-root': {
- borderRadius: 2,
- },
+
+
+
-
- {/* Staff List Table */}
-
-
-
-
- Sr. No.
- User Name
-
- User Email Address
-
-
-
-
- {staffList.length > 0 ? (
- staffList
- .filter(
- (staff) =>
- `${staff.firstName} ${staff.lastName}`
- .toLowerCase()
- .includes(searchQuery.toLowerCase()) ||
- staff.email
- .toLowerCase()
- .includes(searchQuery.toLowerCase())
- )
- .slice((page - 1) * rowsPerPage, page * rowsPerPage)
- .map((staff, index) => (
-
-
- {(page - 1) * rowsPerPage + index + 1}
-
- {`${staff.firstName} ${staff.lastName}`}
- {staff.email}
-
- ))
- ) : (
-
-
- No staff members added yet
-
-
- )}
-
-
-
-
- {/* Pagination */}
- {staffList.length > 0 && (
-
+
+
+
+
+ //
+ // {/*
+ // Staff Management
+ // */}
-
-
- )}
-
+ // }
+ // // addButtonDisabled={false}
+ // // permissionName={'CREATE_USERS'}
+ // // infiniteDropdown
+ // />
- {/* Add Staff Dialog */}
-
+ // {/* Pagination */}
+ // {staffList.length > 0 && (
+ //
+ //
- {/* Notification */}
-
-
- {notification.message}
-
-
-
+ //
+
+ //
+ //
+ // )}
+ //
+
+ // {/* Add Staff Dialog */}
+
+ // {/* Notification */}
+ //
+ //
+ // {notification.message}
+ //
+ //
+ //
);
};
diff --git a/src/views/StaffManagement/staffStyles.js b/src/views/StaffManagement/staffStyles.js
new file mode 100644
index 0000000..a2a130e
--- /dev/null
+++ b/src/views/StaffManagement/staffStyles.js
@@ -0,0 +1,119 @@
+import makeStyles from '@mui/styles/makeStyles';
+import { pxToRem } from '../../theme/typography';
+
+export const useStyles = makeStyles((theme) => ({
+ chipClass: {
+ height: 'fit-content',
+ minHeight: '30px',
+ padding: '2px',
+ alignItems: 'center',
+ },
+ statusColor: {
+ color: theme.palette.primary.main,
+ fontSize: pxToRem(10),
+ },
+ tabsBox: {
+ display: 'flex',
+ justifyContent: ' space-around',
+ // width: '55%',
+ marginTop: theme.spacing(0.5),
+ marginRight: theme.spacing(5.0),
+ alignItems: 'center',
+ },
+ secondaryButton: {
+ width: '200px',
+ height: '46px',
+ borderRadius: '8px',
+ justifyContent: 'space-evenly',
+ fontSize: pxToRem(16),
+ },
+ tableActionIcons: {
+ marginRight: theme.spacing(1.4),
+ width: '15px',
+ },
+ companyNameTableColumn: {
+ display: 'flex',
+ alignItems: 'center',
+ },
+ companyName: {
+ marginLeft: theme.spacing(1),
+ fontSize: pxToRem(14),
+ objectFit: 'contain',
+ width: '260px',
+ },
+ companyNameLink: {
+ textDecoration: 'none',
+ color: theme.palette.grey[10],
+ '&:hover': {
+ color: theme.palette.info.main,
+ textDecoration: 'underline',
+ },
+ },
+ companyWebsiteLabel: {
+ fontSize: pxToRem(12),
+ },
+ companyNameLogo: {
+ height: '40px',
+ width: '40px',
+ borderRadius: theme.shape.borderRadiusComponent,
+ objectFit: 'contain',
+ },
+ sendEmailStatus: {
+ fontSize: pxToRem(14),
+ color: theme.palette.primary.main,
+ },
+ sendEmailLastSentMailDate: {
+ fontSize: pxToRem(12),
+ },
+ addDiscountCodeLink: {
+ fontSize: pxToRem(12),
+ color: theme.palette.primary.main,
+ },
+ addDiscountCodeLabel: {
+ fontSize: pxToRem(14),
+ backgroundColor: theme.palette.common.white,
+ color: theme.palette.common.black,
+ },
+ customModel: {
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ height: '100%',
+ },
+ customModelBox: {
+ paddingLeft: theme.spacing(5),
+ paddingRight: theme.spacing(5),
+ textAlign: 'center',
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ // height: '100%',
+ border: 'none',
+ },
+ newPlanTitleText: {
+ fontSize: pxToRem(28),
+ fontFamily: theme.fontFamily.bold,
+ },
+ newPlanSubTitleText: {
+ fontSize: pxToRem(18),
+ padding: theme.spacing(1),
+ },
+ addPlanSuccessIcon: {
+ padding: theme.spacing(2),
+ paddingTop: theme.spacing(1),
+ },
+ planAddedText: {
+ fontSize: pxToRem(12),
+ color: theme.palette.grey[54],
+ display: 'flex',
+ alignItems: 'center',
+ alignSelf: 'center',
+ },
+ verifyIcon: {
+ marginLeft: theme.spacing(0.3),
+ fontSize: pxToRem(12),
+ color: theme.palette.grey[54],
+ },
+}));