feat: admin staff management

This commit is contained in:
deepvasoya 2025-05-21 16:02:43 +05:30
parent 60d187b300
commit 3061b88343
12 changed files with 1647 additions and 339 deletions

10
src/hooks/useQuery.jsx Normal file
View File

@ -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;

55
src/hooks/useScript.js Normal file
View File

@ -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;

View File

@ -10,10 +10,11 @@ import MockPayment from "../views/MockPayment";
import Users from "../views/User"; import Users from "../views/User";
import ClinicSetup from "../views/ClinicSetup"; import ClinicSetup from "../views/ClinicSetup";
import ClinicTranscripts from "../views/ClinicTranscripts"; import ClinicTranscripts from "../views/ClinicTranscripts";
import ContractManagement from "../views/ContractManagement";
import MasterDataManagement from "../views/MasterData"; import MasterDataManagement from "../views/MasterData";
import PaymentManagement from "../views/PaymentManagement"; import PaymentManagement from "../views/PaymentManagement";
import ClinicDocUpdater from "../views/ClinicDocUpdate"; import ClinicDocUpdater from "../views/ClinicDocUpdate";
import ForgotPassword from "../views/ForgotPassword";
import ResetPassword from "../views/ResetPassword";
export const routesData = [ export const routesData = [
{ {
@ -43,6 +44,14 @@ export const routesData = [
path: "signup/your-details", path: "signup/your-details",
component: YourDetailsForm, component: YourDetailsForm,
}, },
{
path: "forgotpassword",
component: ForgotPassword,
},
{
path: 'reset-password',
component: ResetPassword,
},
], ],
}, },
]; ];

View File

@ -1,38 +1,83 @@
import { axiosInstance } from "../config/api"; import { axiosInstance } from "../config/api";
export const signup = (data) => { export const signup = (data) => {
const url = '/auth/register'; const url = "/auth/register";
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axiosInstance axiosInstance
.post(url, data) .post(url, data)
.then((response) => resolve(response)) .then((response) => resolve(response))
.catch((err) => reject(err)); .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) => { export const getEmailOtp = (data) => {
const url = '/auth/send-otp'; const url = "/auth/send-otp";
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axiosInstance axiosInstance
.post(url, null, { params: data }) .post(url, null, { params: data })
.then((response) => resolve(response)) .then((response) => resolve(response))
.catch((err) => reject(err)); .catch((err) => reject(err));
}); });
}; };
export const verifyOtp = (data) => { export const verifyOtp = (data) => {
const url = '/auth/verify-otp'; const url = "/auth/verify-otp";
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axiosInstance axiosInstance
.post(url, data) .post(url, data)
.catch((err) => { .catch((err) => {
if(err.status==200){ if (err.status == 200) {
resolve(err) resolve(err);
}else{ } else {
reject(err) reject(err);
} }
}) })
.then((response) => resolve(response)) .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));
});
};

View File

@ -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 (
<Box className={classes.right}>
<form onSubmit={handleSubmitClick} className={classes.form}>
<Box className={classes.formBody}>
<Box className={classes.formHeader}>
<Typography className={classes.subHeading}>
Please provide the email address that you used when you signed up
for your account.
</Typography>
</Box>
<InputLabel
shrink={false}
htmlFor={'email'}
className={classes.labelStyle}
>
<Typography
className={classes.inputTitle}
style={{
fontSize: '0.8rem',
}}
>
Email Address*
</Typography>
</InputLabel>
<TextField
style={{ marginTop: '2px' }}
color="secondary"
placeholder="Enter Email ID"
variant="outlined"
margin="normal"
fullWidth
name="email"
value={formik.values.email}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
inputRef={emailRef}
error={Boolean(formik.errors.email && formik.touched.email)}
helperText={
formik.errors.email && formik.touched.email
? formik.errors.email
: ''
}
/>
</Box>
{/* )} */}
<Typography className={classes.sendEmailText}>
We will send you an email that will allow you to reset your password.
</Typography>
<LoadingButton
size="large"
type="submit"
variant="contained"
className={classes.submitButton}
loading={formik.isSubmitting}
>
Reset Password
</LoadingButton>
{/* <Box className={classes.formFooter}>
<Typography className={classes.emailText}>
If you no longer have access to this email account, please contact
our support team at
</Typography>
<Typography className={classes.emailText}>
<a href={`mailto:${CONTACT_EMAIL}`}>{CONTACT_EMAIL}</a>
</Typography>
</Box> */}
</form>
</Box>
);
}
export default ForgotPasswordForm;

View File

@ -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),
},
}));

View File

@ -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 (
<Box className={classes.root}>
<Card className={classes.card}>
<Grid container display="flex" justifyContent="center">
{!resetPasswordLinkSend ? (
<Grid item xs={12}>
<Box
display="flex"
alignItems="center"
className={classes.forgotPasswordHeader}
>
<Grid item xs={2}>
<ArrowBackOutlined
className={classes.icon}
onClick={handleGoBackClick}
/>
</Grid>
<Grid item xs={10}>
<Typography variant="Inter-bold" className={classes.heading}>
Reset your Password
</Typography>
</Grid>
</Box>
<ForgotPasswordForm
onSubmit={handleFormSubmit}
setEmail={setEmail}
/>
</Grid>
) : (
<Box className={classes.emailSentMainDiv}>
{/* <Box width="90%"> */}
<Typography
variant="Gilroy-SemiBold"
className={classes.heading}
marginBottom="20px"
>
Email Sent
</Typography>
<Typography
variant="Gilroy-normal"
className={classes.subheading}
// marginBottom="30px"
>
{/* {` 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 dont see the email in
your inbox.`} */}
{`An email with instructions on how to reset your password has
been sent to `}
<span style={{ fontWeight: 'bold' }}>{email}</span>
{`. It is valid for the next 24 hrs. Check your spam or junk folder if you dont see the email in
your inbox.`}
</Typography>
{/* </Box> */}
</Box>
)}
</Grid>
</Card>
</Box>
);
}
export default ForgotPassword;

View File

@ -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 (
<Box className={classes.right}>
<form onSubmit={handleSubmitClick} className={classes.form}>
<Box className={classes.formBody}>
<Box className={classes.formHeader}>
<Typography className={classes.subHeading}>
Note: Password must be at least 8 characters, contain 1 letter and
1 number, with no spaces at the beginning/end.
</Typography>
</Box>
<InputLabel
shrink={false}
htmlFor={'password'}
className={classes.labelStyle}
>
<Typography className={classes.inputTitle}>
Enter New Password
</Typography>
</InputLabel>
<TextField
color="secondary"
placeholder="Enter Password"
variant="outlined"
margin="normal"
fullWidth
type={showPassword ? 'text' : 'password'}
name="password"
value={formik.values.password}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
inputRef={fieldRefs.password}
error={Boolean(formik.errors.password && formik.touched.password)}
helperText={
formik.errors.password && formik.touched.password
? formik.errors.password
: ''
}
InputProps={{
endAdornment: (
<IconButton
onClick={() => handleTogglePasswordVisibility('password')}
edge="end"
>
{!showPassword ? (
<VisibilityOffOutlined />
) : (
<VisibilityOutlined />
)}
</IconButton>
),
}}
/>
{/* Password validation display */}
{!formik.isValid && (
<>
<Typography className={classes.passwordCheckListTitle}>
Note: Password must be
</Typography>
<Box className={classes.passwordCheckList}>
<PasswordValidation
isValid={passwordLengthRegex.test(formik.values.password)}
text="at least 6 characters"
/>
<PasswordValidation
isValid={passwordLetterRegex.test(formik.values.password)}
text="at least 1 letter"
/>
<PasswordValidation
isValid={passwordNumberRegex.test(formik.values.password)}
text="at least 1 number"
/>
<PasswordValidation
isValid={passwordSpacesRegex.test(formik.values.password)}
text="with no spaces at the beginning/end"
/>
</Box>
</>
)}
</Box>
<Box className={classes.confirmResetBox}>
<InputLabel
shrink={false}
htmlFor={'confirmPassword'}
className={classes.labelStyle}
>
<Typography className={classes.inputTitle}>
Confirm New Password
</Typography>
</InputLabel>
<TextField
className={classes.resetPasswordTextField}
color="secondary"
placeholder="Confirm Password"
variant="outlined"
margin="normal"
fullWidth
type={showConfirmPassword ? 'text' : 'password'}
name="confirmPassword"
value={formik.values.confirmPassword}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
inputRef={fieldRefs.confirmPassword}
error={Boolean(
formik.errors.confirmPassword && formik.touched.confirmPassword
)}
helperText={
formik.errors.confirmPassword && formik.touched.confirmPassword
? formik.errors.confirmPassword
: ''
}
InputProps={{
endAdornment: (
<IconButton
onClick={() =>
handleTogglePasswordVisibility('confirmPassword')
}
edge="end"
>
{!showConfirmPassword ? (
<VisibilityOffOutlined />
) : (
<VisibilityOutlined />
)}
</IconButton>
),
}}
/>
</Box>
<LoadingButton
size="large"
type="submit"
variant="contained"
className={classes.submitButton}
loading={formik.isSubmitting}
>
Save New Password
</LoadingButton>
</form>
</Box>
);
}
export default ResetPasswordForm;

View File

@ -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 (
<Box className={classes.root}>
<Card className={classes.card}>
<Grid container display="flex" justifyContent="center">
{!resetPasswordLinkSend ? (
<Grid item xs={12}>
<Box
display="flex"
alignItems="center"
className={classes.forgotPasswordHeader}
>
<Grid item xs={2}></Grid>
<Grid item xs={10}>
<Typography variant="Gilroy-bold" className={classes.heading}>
{headingText} your Password
</Typography>
</Grid>
</Box>
<ResetPasswordForm onSubmit={handleFormSubmit} />
</Grid>
) : (
<Box paddingTop="35px" paddingBottom="5px" fontSize="0.8rem">
<Box className={classes.successIcon}>
<img
className={classes.resetPasswordSuccessIcon}
src={ResetPasswordSuccessIcon}
/>
</Box>
<Box textAlign="center">
<Typography
variant="Gilroy-SemiBold"
className={classes.resetPasswordheading}
marginBottom="20px"
>
{successMessage}
</Typography>
<Grid item width="80%" margin="auto">
<Typography
variant="Gilroy-normal"
className={classes.subheading}
>
Awesome, you have successfully{' '}
{!isCreatePassword ? 'updated' : 'created'} your password.
</Typography>
</Grid>
<LoadingButton
className={classes.submitButton}
size="small"
width="60%"
type="submit"
variant="contained"
onClick={() => navigate('/auth/login')}
>
Back to Login
</LoadingButton>
</Box>
</Box>
)}
</Grid>
</Card>
</Box>
);
}
export default ResetPassword;

View File

@ -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),
},
}));

View File

@ -1,9 +1,9 @@
import AddIcon from '@mui/icons-material/Add'; import AddIcon from "@mui/icons-material/Add";
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew'; import ArrowBackIosNewIcon from "@mui/icons-material/ArrowBackIosNew";
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
import CloseIcon from '@mui/icons-material/Close'; import CloseIcon from "@mui/icons-material/Close";
import PersonAddIcon from '@mui/icons-material/PersonAdd'; import PersonAddIcon from "@mui/icons-material/PersonAdd";
import SearchIcon from '@mui/icons-material/Search'; import SearchIcon from "@mui/icons-material/Search";
import { import {
Alert, Alert,
Box, Box,
@ -18,7 +18,6 @@ import {
InputAdornment, InputAdornment,
Paper, Paper,
Snackbar, Snackbar,
Table,
TableBody, TableBody,
TableCell, TableCell,
TableContainer, TableContainer,
@ -26,18 +25,26 @@ import {
TableRow, TableRow,
TextField, TextField,
Typography, Typography,
} from '@mui/material'; } from "@mui/material";
import React, { useState } from 'react'; import React, { useMemo, useRef, useState } from "react";
import CustomBreadcrumbs from '../../components/CustomBreadcrumbs'; import CustomBreadcrumbs from "../../components/CustomBreadcrumbs";
import PageHeader from '../../components/PageHeader'; 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 StaffManagement = () => {
const theme = useTheme();
const classes = useStyles();
const ref = useRef(null);
// State for form fields // State for form fields
const [firstName, setFirstName] = useState(''); const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState(''); const [lastName, setLastName] = useState("");
const [email, setEmail] = useState(''); const [email, setEmail] = useState("");
const [emailError, setEmailError] = useState(''); const [emailError, setEmailError] = useState("");
const queryParams = new URLSearchParams(location.search);
// State for staff list // State for staff list
const [staffList, setStaffList] = useState([]); const [staffList, setStaffList] = useState([]);
@ -46,7 +53,7 @@ const StaffManagement = () => {
const [openDialog, setOpenDialog] = useState(false); const [openDialog, setOpenDialog] = useState(false);
// State for search // State for search
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState("");
// State for pagination // State for pagination
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
@ -55,8 +62,8 @@ const StaffManagement = () => {
// State for notification // State for notification
const [notification, setNotification] = useState({ const [notification, setNotification] = useState({
open: false, open: false,
message: '', message: "",
severity: 'success', severity: "success",
}); });
// Email validation function // Email validation function
@ -73,41 +80,37 @@ const StaffManagement = () => {
const handleCloseDialog = () => { const handleCloseDialog = () => {
setOpenDialog(false); setOpenDialog(false);
// Clear form // Clear form
setFirstName(''); setFirstName("");
setLastName(''); setLastName("");
setEmail(''); setEmail("");
setEmailError(''); setEmailError("");
}; };
// Handle form submission // Handle form submission
const handleSubmit = (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
// Validate email // Validate email
if (!validateEmail(email)) { if (!validateEmail(email)) {
setEmailError('Please enter a valid email address'); setEmailError("Please enter a valid email address");
return; return;
} }
// Add new staff member const payload = {
const newStaff = { username: `${firstName} ${lastName}`,
id: staffList.length + 1,
firstName,
lastName,
email, 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 // Close dialog
handleCloseDialog(); handleCloseDialog();
// Show success notification
setNotification({
open: true,
message: 'Staff member added successfully!',
severity: 'success',
});
}; };
// Handle notification close // 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........................ // ...................breadcrumbs array........................
const breadcrumbs = [ const breadcrumbs = [
{ {
label: 'Dashboard', label: "Dashboard",
path: '/', path: "/",
}, },
{ {
label: 'Staff Management', label: "Staff Management",
path: '/staff', path: "/staff",
}, },
]; ];
return ( const columns = useMemo(() => [
<Container maxWidth="lg" sx={{ py: 4 }}> {
{/* <Typography size: 100,
variant="h4" header: "Sr. No.",
component="h1" Cell: (props) => {
gutterBottom const tableState = props?.table?.getState();
sx={{ fontWeight: 'bold', mb: 4, color: '#0a2d6b' }} const serialNumber = (
> props?.row?.index +
Staff Management 1 +
</Typography> */} tableState?.pagination?.pageIndex * tableState?.pagination?.pageSize
)
?.toString()
?.padStart(2, "0");
return <span>{serialNumber}</span>;
},
enableSorting: false,
},
{
size: 280,
accessorKey: "username",
header: "User Name",
enableColumnFilter: false,
},
{
size: 220,
accessorKey: "email",
header: "User Email Address",
enableColumnFilter: false,
},
]);
return (
<Box>
<PageHeader <PageHeader
pageTitle="Staff Management" pageTitle="Staff Management"
hideAddButton // hideAddButton
// addButtonTitle="Add Staff" addButtonIcon={<AddIcon />}
// onAddButtonClick={toggle} onAddButtonClick={handleOpenDialog}
// addButtonIcon={<img src={AddIcon} />}
// addButtonDisabled={false}
// permissionName={'CREATE_USERS'}
// infiniteDropdown
/> />
<CustomBreadcrumbs breadcrumbs={breadcrumbs} /> <CustomBreadcrumbs breadcrumbs={breadcrumbs} />
<Box className={classes.tableMainDiv}>
<Paper elevation={3} sx={{ p: 0, mb: 4, overflow: 'hidden' }}> <Box>
{/* Staff List Header with Add Button */} <Table
<Box hideTopToolbar
sx={{ columns={columns}
display: 'flex', getData={getData}
justifyContent: 'space-between', options={{
alignItems: 'center', enableRowSelection: true,
p: 3, showTopBar: false,
}} showFilters: true,
>
<Typography variant="h6" component="h2" sx={{ fontWeight: 'bold' }}>
Staff List
</Typography>
<Button
variant="contained"
color="error"
startIcon={<AddIcon />}
onClick={handleOpenDialog}
sx={{
borderRadius: 50,
textTransform: 'none',
backgroundColor: '#ff3366',
'&:hover': {
backgroundColor: '#e61653',
},
}}
>
Add Admin
</Button>
</Box>
{/* Search Box */}
<Box sx={{ px: 3, pb: 2 }}>
<TextField
placeholder="Search user here"
fullWidth
size="small"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon color="action" />
</InputAdornment>
),
}}
sx={{
backgroundColor: '#fff',
'& .MuiOutlinedInput-root': {
borderRadius: 2,
},
}} }}
searchText="Staff"
showSearchBox={true}
ref={ref}
/> />
</Box> </Box>
</Box>
{/* Staff List Table */} <Box className={classes.tableMainDiv}>
<TableContainer> <Dialog
<Table> open={openDialog}
<TableHead> onClose={handleCloseDialog}
<TableRow sx={{ backgroundColor: '#f5f5f5' }}> maxWidth="sm"
<TableCell sx={{ fontWeight: 'bold' }}>Sr. No.</TableCell> fullWidth
<TableCell sx={{ fontWeight: 'bold' }}>User Name</TableCell> >
<TableCell sx={{ fontWeight: 'bold' }}> <DialogTitle
User Email Address
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{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) => (
<TableRow key={staff.id}>
<TableCell>
{(page - 1) * rowsPerPage + index + 1}
</TableCell>
<TableCell>{`${staff.firstName} ${staff.lastName}`}</TableCell>
<TableCell>{staff.email}</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={3}
align="center"
sx={{ py: 5, color: '#666' }}
>
No staff members added yet
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
{/* Pagination */}
{staffList.length > 0 && (
<Box
sx={{ sx={{
display: 'flex', display: "flex",
justifyContent: 'center', alignItems: "center",
p: 2, justifyContent: "space-between",
borderTop: '1px solid #eee', pb: 1,
}} }}
> >
<Button <Box sx={{ display: "flex", alignItems: "center" }}>
onClick={() => setPage((prev) => Math.max(prev - 1, 1))} <PersonAddIcon sx={{ mr: 1, color: "#0a2d6b" }} />
disabled={page === 1} <Typography
startIcon={<ArrowBackIosNewIcon fontSize="small" />} variant="h6"
sx={{ mx: 1, color: '#666' }} component="span"
> sx={{ fontWeight: "bold", color: "#0a2d6b" }}
Previous >
</Button> Add New Staff
</Typography>
</Box>
<IconButton onClick={handleCloseDialog} size="small">
<CloseIcon />
</IconButton>
</DialogTitle>
<Divider />
<DialogContent>
<Box component="form" onSubmit={handleSubmit} sx={{ mt: 1 }}>
<TextField
label="First Name"
fullWidth
margin="normal"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
placeholder="First Name"
required
InputLabelProps={{
shrink: true,
}}
/>
<TextField
label="Last Name"
fullWidth
margin="normal"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
placeholder="Last Name"
required
InputLabelProps={{
shrink: true,
}}
/>
<TextField
label="Email Address"
fullWidth
margin="normal"
type="email"
value={email}
onChange={(e) => {
setEmail(e.target.value);
setEmailError("");
}}
placeholder="Email Address"
error={!!emailError}
helperText={emailError}
required
InputLabelProps={{
shrink: true,
}}
/>
</Box>
</DialogContent>
<DialogActions sx={{ px: 3, pb: 3 }}>
<Button <Button
variant="contained" variant="contained"
disableElevation color="error"
fullWidth
onClick={handleSubmit}
sx={{ sx={{
mx: 1, py: 1.5,
minWidth: '36px', backgroundColor: "#ff3366",
backgroundColor: '#f0f0f0', "&:hover": {
color: '#333', backgroundColor: "#e61653",
'&:hover': {
backgroundColor: '#e0e0e0',
}, },
}} }}
> >
{page} Add Admin
</Button> </Button>
</DialogActions>
</Dialog>
</Box>
</Box>
// <Container maxWidth="lg" sx={{ py: 4 }}>
// {/* <Typography
// variant="h4"
// component="h1"
// gutterBottom
// sx={{ fontWeight: 'bold', mb: 4, color: '#0a2d6b' }}
// >
// Staff Management
// </Typography> */}
<Button // <PageHeader
onClick={() => setPage((prev) => prev + 1)} // pageTitle="Staff Management"
disabled={page * rowsPerPage >= staffList.length} // hideAddButton
endIcon={<ArrowForwardIosIcon fontSize="small" />} // // addButtonTitle="Add Staff"
sx={{ mx: 1, color: '#666' }} // // onAddButtonClick={toggle}
> // // addButtonIcon={<img src={AddIcon} />}
Next // // addButtonDisabled={false}
</Button> // // permissionName={'CREATE_USERS'}
</Box> // // infiniteDropdown
)} // />
</Paper>
{/* Add Staff Dialog */} // <CustomBreadcrumbs breadcrumbs={breadcrumbs} />
<Dialog
open={openDialog}
onClose={handleCloseDialog}
maxWidth="sm"
fullWidth
>
<DialogTitle
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
pb: 1,
}}
>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<PersonAddIcon sx={{ mr: 1, color: '#0a2d6b' }} />
<Typography
variant="h6"
component="span"
sx={{ fontWeight: 'bold', color: '#0a2d6b' }}
>
Add New Staff
</Typography>
</Box>
<IconButton onClick={handleCloseDialog} size="small">
<CloseIcon />
</IconButton>
</DialogTitle>
<Divider /> // <Paper elevation={3} sx={{ p: 0, mb: 4, overflow: 'hidden' }}>
// {/* Staff List Header with Add Button */}
// <Box
// sx={{
// display: 'flex',
// justifyContent: 'space-between',
// alignItems: 'center',
// p: 3,
// }}
// >
// <Typography variant="h6" component="h2" sx={{ fontWeight: 'bold' }}>
// Staff List
// </Typography>
<DialogContent> // <Button
<Box component="form" onSubmit={handleSubmit} sx={{ mt: 1 }}> // variant="contained"
<TextField // color="error"
label="First Name *" // startIcon={<AddIcon />}
fullWidth // onClick={handleOpenDialog}
margin="normal" // sx={{
value={firstName} // borderRadius: 50,
onChange={(e) => setFirstName(e.target.value)} // textTransform: 'none',
placeholder="First Name" // backgroundColor: '#ff3366',
required // '&:hover': {
InputLabelProps={{ // backgroundColor: '#e61653',
shrink: true, // },
}} // }}
/> // >
// Add Admin
// </Button>
// </Box>
<TextField // {/* Search Box */}
label="Last Name *" // <Box sx={{ px: 3, pb: 2 }}>
fullWidth // <TextField
margin="normal" // placeholder="Search user here"
value={lastName} // fullWidth
onChange={(e) => setLastName(e.target.value)} // size="small"
placeholder="Last Name" // value={searchQuery}
required // onChange={(e) => setSearchQuery(e.target.value)}
InputLabelProps={{ // InputProps={{
shrink: true, // startAdornment: (
}} // <InputAdornment position="start">
/> // <SearchIcon color="action" />
// </InputAdornment>
// ),
// }}
// sx={{
// backgroundColor: '#fff',
// '& .MuiOutlinedInput-root': {
// borderRadius: 2,
// },
// }}
// />
// </Box>
<TextField // {/* Staff List Table */}
label="Email Address *" // <TableContainer>
fullWidth // <Table>
margin="normal" // <TableHead>
type="email" // <TableRow sx={{ backgroundColor: '#f5f5f5' }}>
value={email} // <TableCell sx={{ fontWeight: 'bold' }}>Sr. No.</TableCell>
onChange={(e) => { // <TableCell sx={{ fontWeight: 'bold' }}>User Name</TableCell>
setEmail(e.target.value); // <TableCell sx={{ fontWeight: 'bold' }}>
setEmailError(''); // User Email Address
}} // </TableCell>
placeholder="Email Address" // </TableRow>
error={!!emailError} // </TableHead>
helperText={emailError} // <TableBody>
required // {staffList.length > 0 ? (
InputLabelProps={{ // staffList
shrink: true, // .filter(
}} // (staff) =>
/> // `${staff.firstName} ${staff.lastName}`
</Box> // .toLowerCase()
</DialogContent> // .includes(searchQuery.toLowerCase()) ||
// staff.email
// .toLowerCase()
// .includes(searchQuery.toLowerCase())
// )
// .slice((page - 1) * rowsPerPage, page * rowsPerPage)
// .map((staff, index) => (
// <TableRow key={staff.id}>
// <TableCell>
// {(page - 1) * rowsPerPage + index + 1}
// </TableCell>
// <TableCell>{`${staff.firstName} ${staff.lastName}`}</TableCell>
// <TableCell>{staff.email}</TableCell>
// </TableRow>
// ))
// ) : (
// <TableRow>
// <TableCell
// colSpan={3}
// align="center"
// sx={{ py: 5, color: '#666' }}
// >
// No staff members added yet
// </TableCell>
// </TableRow>
// )}
// </TableBody>
// </Table>
// </TableContainer>
<DialogActions sx={{ px: 3, pb: 3 }}> // {/* Pagination */}
<Button // {staffList.length > 0 && (
variant="contained" // <Box
color="error" // sx={{
fullWidth // display: 'flex',
onClick={handleSubmit} // justifyContent: 'center',
sx={{ // p: 2,
py: 1.5, // borderTop: '1px solid #eee',
backgroundColor: '#ff3366', // }}
'&:hover': { // >
backgroundColor: '#e61653', // <Button
}, // onClick={() => setPage((prev) => Math.max(prev - 1, 1))}
}} // disabled={page === 1}
> // startIcon={<ArrowBackIosNewIcon fontSize="small" />}
Add Admin // sx={{ mx: 1, color: '#666' }}
</Button> // >
</DialogActions> // Previous
</Dialog> // </Button>
{/* Notification */} // <Button
<Snackbar // variant="contained"
open={notification.open} // disableElevation
autoHideDuration={4000} // sx={{
onClose={handleCloseNotification} // mx: 1,
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} // minWidth: '36px',
> // backgroundColor: '#f0f0f0',
<Alert // color: '#333',
onClose={handleCloseNotification} // '&:hover': {
severity={notification.severity} // backgroundColor: '#e0e0e0',
sx={{ width: '100%' }} // },
> // }}
{notification.message} // >
</Alert> // {page}
</Snackbar> // </Button>
</Container>
// <Button
// onClick={() => setPage((prev) => prev + 1)}
// disabled={page * rowsPerPage >= staffList.length}
// endIcon={<ArrowForwardIosIcon fontSize="small" />}
// sx={{ mx: 1, color: '#666' }}
// >
// Next
// </Button>
// </Box>
// )}
// </Paper>
// {/* Add Staff Dialog */}
// {/* Notification */}
// <Snackbar
// open={notification.open}
// autoHideDuration={4000}
// onClose={handleCloseNotification}
// anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
// >
// <Alert
// onClose={handleCloseNotification}
// severity={notification.severity}
// sx={{ width: '100%' }}
// >
// {notification.message}
// </Alert>
// </Snackbar>
// </Container>
); );
}; };

View File

@ -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],
},
}));