@campxdev/shared 1.11.6 → 1.11.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ {
2
+ "cSpell.words": ["campxdev", "mentee", "mentees"]
3
+ }
package/exports.ts CHANGED
@@ -9,6 +9,10 @@ export {
9
9
  default as DialogProvider,
10
10
  useModal,
11
11
  } from './src/components/DrawerWrapper/DrawerWrapper'
12
+ export {
13
+ default as ErrorModalProvider,
14
+ useErrorModal,
15
+ } from './src/components/ErrorModalWrapper/ErrorModalWrapper'
12
16
  export { default as ConfirmContextProvider } from './src/components/PopupConfirm/ConfirmContextProvider'
13
17
 
14
18
  export { default as Providers } from './src/contexts/Providers'
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@campxdev/shared",
3
- "version": "1.11.6",
3
+ "version": "1.11.8",
4
4
  "main": "./exports.ts",
5
5
  "scripts": {
6
6
  "start": "react-scripts start",
7
7
  "dev": "react-scripts start",
8
8
  "build": "CI=false && react-scripts build",
9
+ "prepublish": "npm run build",
9
10
  "test": "react-scripts test",
10
11
  "eject": "react-scripts eject",
11
12
  "lint": "eslint ./src/**/*.{js,jsx,ts,tsx,json}",
@@ -73,8 +74,8 @@
73
74
  "@types/react": "^18.0.25",
74
75
  "@types/react-flatpickr": "^3.8.8",
75
76
  "@types/react-helmet": "^6.1.6",
76
- "@typescript-eslint/eslint-plugin": "^5.35.1",
77
- "@typescript-eslint/parser": "^5.35.1",
77
+ "@typescript-eslint/eslint-plugin": "^6.7.4",
78
+ "@typescript-eslint/parser": "^6.7.4",
78
79
  "eslint": "^8.23.0",
79
80
  "eslint-config-prettier": "^8.5.0",
80
81
  "eslint-import-resolver-typescript": "^3.5.0",
@@ -85,6 +86,6 @@
85
86
  "prettier": "^2.5.0",
86
87
  "react-scripts": "^5.0.1",
87
88
  "storybook-addon-react-router-v6": "^0.2.1",
88
- "typescript": "^4.8.4"
89
+ "typescript": "^5.2.2"
89
90
  }
90
91
  }
Binary file
@@ -5,13 +5,14 @@ const internalserver = require('./500.png')
5
5
  const unauth = require('./401.png')
6
6
  const nointernet = require('./nointernet.png')
7
7
  const campxLogoPrimary = require('./campx_logo__full_primary.png')
8
-
8
+ const animatedImage = require('./animation.gif')
9
9
  export {
10
+ animatedImage,
10
11
  avatarImage,
11
12
  campxLogoPrimary,
13
+ internalserver,
14
+ nointernet,
12
15
  pagenotfound,
13
16
  permissiondenied,
14
- internalserver,
15
17
  unauth,
16
- nointernet,
17
18
  }
@@ -0,0 +1,28 @@
1
+ import { Stack } from '@mui/material'
2
+ import { useQuery } from 'react-query'
3
+ import PageHeader from '../PageHeader'
4
+ import Spinner from '../Spinner'
5
+ import { AddConnectionDrawerButton } from './components/AddConnectionDrawerButton'
6
+ import { ConnectionCard } from './components/ConnectionCard'
7
+ import { getPunchLogsDatabaseConfigByTenant } from './service'
8
+
9
+ const DatabaseConfiguration = ({ type }) => {
10
+ const { data: punchLogsDatabaseConfig, isLoading } = useQuery(
11
+ 'punch-logs-database-config',
12
+ getPunchLogsDatabaseConfigByTenant,
13
+ )
14
+
15
+ if (isLoading) return <Spinner />
16
+ return (
17
+ <Stack>
18
+ <PageHeader title="Punch Logs Database Configuration" />
19
+ {!punchLogsDatabaseConfig ? (
20
+ <AddConnectionDrawerButton type={type} />
21
+ ) : (
22
+ <ConnectionCard data={punchLogsDatabaseConfig} />
23
+ )}
24
+ </Stack>
25
+ )
26
+ }
27
+
28
+ export default DatabaseConfiguration
@@ -0,0 +1,87 @@
1
+ import * as yup from 'yup'
2
+ import { generateLabelValueOptions } from '../../constants'
3
+ import Form from '../Form/Form'
4
+ import { Field } from '../Form/RenderForm'
5
+
6
+ const fields: Field[] = [
7
+ {
8
+ name: 'host',
9
+ render: 'FormTextField',
10
+ required: true,
11
+ label: 'Host',
12
+ },
13
+ {
14
+ name: 'username',
15
+ render: 'FormTextField',
16
+ required: true,
17
+ label: 'Username',
18
+ },
19
+ {
20
+ name: 'password',
21
+ render: 'FormTextField',
22
+ required: true,
23
+ label: 'Password',
24
+ },
25
+ {
26
+ name: 'port',
27
+ render: 'FormTextField',
28
+ required: true,
29
+ label: 'Port',
30
+ },
31
+ {
32
+ name: 'database',
33
+ render: 'FormTextField',
34
+ required: true,
35
+ label: 'Database',
36
+ },
37
+ {
38
+ name: 'timezone',
39
+ render: 'FormMultiSelect',
40
+ elementProps: {
41
+ multiple: false,
42
+ },
43
+ validation: yup.object().required('Select a Timezone'),
44
+ required: true,
45
+ label: 'Timezone',
46
+ },
47
+ ]
48
+
49
+ export const DatabaseConfigurationForm = ({
50
+ data,
51
+ close,
52
+ successToastMessage,
53
+ type,
54
+ }) => {
55
+ const timezones = Intl.supportedValuesOf('timeZone')
56
+ return (
57
+ <Form
58
+ onCancel={close}
59
+ endPoint="/hrms/punch-logs-database-config"
60
+ fields={fields}
61
+ cols={1}
62
+ refetchKey="punch-logs-database-config"
63
+ dropdowns={{ timezone: generateLabelValueOptions(timezones) }}
64
+ submitBtn={{
65
+ label: 'Save',
66
+ }}
67
+ onSubmit={(originalFormData) => {
68
+ return {
69
+ ...originalFormData,
70
+ timezone: originalFormData?.timezone?.value,
71
+ type,
72
+ }
73
+ }}
74
+ defaultValues={{
75
+ ...data,
76
+ timezone: {
77
+ label: data?.timezone,
78
+ value: data?.timezone,
79
+ },
80
+ }}
81
+ successToastMessage={successToastMessage}
82
+ onSuccess={() => {
83
+ close()
84
+ }}
85
+ />
86
+ )
87
+ }
@@ -0,0 +1,30 @@
1
+ import { Add } from '@mui/icons-material'
2
+ import { Typography } from '@mui/material'
3
+ import { DrawerButton } from '../../ModalButtons'
4
+ import { DatabaseConfigurationForm } from '../DatabaseConfigurationForm'
5
+ import { StyledSelectAttachmentsContainer } from '../styles'
6
+
7
+ export const AddConnectionDrawerButton = ({ type }) => {
8
+ return (
9
+ <DrawerButton
10
+ key={'1'}
11
+ anchor={({ open }) => (
12
+ <StyledSelectAttachmentsContainer onClick={open}>
13
+ <Add />
14
+ <Typography sx={{ margin: '0px 10px', opacity: '0.6' }}>
15
+ Add Database Configuration
16
+ </Typography>
17
+ </StyledSelectAttachmentsContainer>
18
+ )}
19
+ content={({ close }) => (
20
+ <DatabaseConfigurationForm
21
+ type={type}
22
+ data={null}
23
+ close={close}
24
+ successToastMessage="Created Database Configuration"
25
+ />
26
+ )}
27
+ title="Add Database Configuration"
28
+ />
29
+ )
30
+ }
@@ -0,0 +1,79 @@
1
+ import { Cached } from '@mui/icons-material'
2
+ import { Box, CardActions, IconButton, Typography } from '@mui/material'
3
+ import { toast } from 'react-toastify'
4
+ import axios from '../../../config/axios'
5
+ import { EditButton } from '../../IconButtons'
6
+ import { DrawerButton } from '../../ModalButtons'
7
+ import { DatabaseConfigurationForm } from '../DatabaseConfigurationForm'
8
+ import { StyledCard, StyledCardContent } from '../styles'
9
+
10
+ export const ConnectionCard = ({ data }) => {
11
+ const handleReconnect = async () => {
12
+ const response: string = await axios.post(
13
+ 'hrms/punch-logs-database-config/connect',
14
+ {},
15
+ )
16
+ if (response) {
17
+ window.location.reload()
18
+ }
19
+ toast.success(response)
20
+ }
21
+ return (
22
+ <>
23
+ <StyledCard>
24
+ <StyledCardContent>
25
+ <Column label={'Host'} value={data?.host} />
26
+ <Column label={'Username'} value={data?.username} />
27
+ <Column label={'Password'} value={data?.password} />
28
+ <Column label={'Port'} value={data?.port} />
29
+ <Column label={'Database'} value={data?.database} />
30
+ <Column label={'Timezone'} value={data?.timezone} />
31
+ <Column
32
+ label={'Status'}
33
+ value={data?.connectionStatus}
34
+ color={data?.connectionStatus == 'Connected' ? 'green' : 'red'}
35
+ />
36
+ </StyledCardContent>
37
+ <CardActions>
38
+ <DrawerButton
39
+ key={'1'}
40
+ anchor={({ open }) => <EditButton onClick={open} />}
41
+ content={({ close }) => (
42
+ <DatabaseConfigurationForm
43
+ data={data}
44
+ close={close}
45
+ successToastMessage="Updated Database Configuration"
46
+ type={data.type}
47
+ />
48
+ )}
49
+ title="Add Database Configuration"
50
+ />
51
+ <IconButton onClick={handleReconnect}>
52
+ <Cached sx={{ '&:hover': { color: 'green' } }} />
53
+ </IconButton>
54
+ </CardActions>
55
+ </StyledCard>
56
+ </>
57
+ )
58
+ }
59
+
60
+ export const Column = ({
61
+ label,
62
+ value,
63
+ color = '#000000',
64
+ }: {
65
+ label: string
66
+ value: string
67
+ color?: string
68
+ }) => {
69
+ return (
70
+ <Box>
71
+ <Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
72
+ {label}
73
+ </Typography>
74
+ <Typography variant="h5" component="div" color={color}>
75
+ {value}
76
+ </Typography>
77
+ </Box>
78
+ )
79
+ }
@@ -0,0 +1,5 @@
1
+ import { lazy } from 'react'
2
+
3
+ const DatabaseConfiguration = lazy(() => import('./DatabaseConfiguration'))
4
+
5
+ export { DatabaseConfiguration }
@@ -0,0 +1,6 @@
1
+ import axios from '../../config/axios'
2
+ const ENDPOINT = '/hrms/punch-logs-database-config'
3
+
4
+ export const getPunchLogsDatabaseConfigByTenant = async () => {
5
+ return axios.get(`${ENDPOINT}/tenant-config`).then((res) => res.data)
6
+ }
@@ -0,0 +1,30 @@
1
+ import { Card, CardContent, FormLabel, styled } from '@mui/material'
2
+
3
+ export const StyledSelectAttachmentsContainer = styled(FormLabel)({
4
+ border: '1px dashed #0000001A',
5
+ margin: '20px',
6
+ display: 'flex',
7
+ justifyContent: 'center',
8
+ alignItems: 'center',
9
+ borderRadius: '10px',
10
+ cursor: 'pointer',
11
+ height: '148px',
12
+ gap: '10px',
13
+ '&:hover': {
14
+ backgroundColor: '#EFEFEF',
15
+ },
16
+ })
17
+
18
+ export const StyledCard = styled(Card)({
19
+ display: 'flex',
20
+ margin: '20px',
21
+ justifyContent: 'center',
22
+ gap: '60px',
23
+ })
24
+
25
+ export const StyledCardContent = styled(CardContent)({
26
+ display: 'flex',
27
+ margin: '20px',
28
+ alignItems: 'center',
29
+ gap: '30px',
30
+ })
@@ -0,0 +1,58 @@
1
+ import { Close } from '@mui/icons-material'
2
+ import {
3
+ alpha,
4
+ Box,
5
+ Dialog,
6
+ DialogContent,
7
+ IconButton,
8
+ styled,
9
+ Typography,
10
+ } from '@mui/material'
11
+
12
+ const StyledDialogHeader = styled(Box)(({ theme }) => ({
13
+ height: '64px',
14
+ backgroundColor: alpha(theme.palette.text.secondary, 0.1),
15
+ display: 'flex',
16
+ justifyContent: 'space-between',
17
+ alignItems: 'center',
18
+ padding: '0.6rem 1rem',
19
+ }))
20
+
21
+ export default function DialogTemplate({
22
+ handleClose,
23
+ modal,
24
+ setModal,
25
+ ...props
26
+ }) {
27
+ return (
28
+ <Dialog
29
+ sx={{
30
+ zIndex: 500,
31
+ minHeight: '70vh',
32
+ '& .MuiDialog-container': {
33
+ '& .MuiPaper-root': {
34
+ maxWidth: '1420px',
35
+ },
36
+ },
37
+ }}
38
+ onClose={handleClose}
39
+ open={modal.open}
40
+ {...props}
41
+ PaperProps={{
42
+ sx: {
43
+ borderRadius: '10px',
44
+ },
45
+ }}
46
+ >
47
+ <StyledDialogHeader>
48
+ <Typography fontWeight={600}>{modal.title}</Typography>
49
+ <IconButton onClick={() => setModal({ ...modal, open: false })}>
50
+ <Close />
51
+ </IconButton>
52
+ </StyledDialogHeader>
53
+ <DialogContent sx={{ padding: '0' }}>
54
+ <>{modal.content({ close: handleClose })}</>
55
+ </DialogContent>
56
+ </Dialog>
57
+ )
58
+ }
@@ -1,9 +1,11 @@
1
1
  import { createContext, useContext, useState } from 'react'
2
+ import DialogTemplate from './DialogTemplate'
2
3
  import { DrawerTemplate } from './DrawerTemplate'
4
+ import ErrorTemplate from './ErrorTemplate'
3
5
 
4
6
  interface IProps {
5
7
  title: string
6
- modalType?: 'dialog' | 'drawer'
8
+ modalType?: 'dialog' | 'drawer' | 'error'
7
9
  content: ({ close }: { close: () => void }) => any
8
10
  }
9
11
 
@@ -32,7 +34,7 @@ export default function DialogProvider({ children }) {
32
34
  open: true,
33
35
  title: props.title,
34
36
  content: props.content,
35
- // modalType: props.modalType ?? 'drawer',
37
+ modalType: props.modalType ?? 'drawer',
36
38
  })
37
39
  }
38
40
 
@@ -40,11 +42,25 @@ export default function DialogProvider({ children }) {
40
42
  <DialogContext.Provider value={showModal}>
41
43
  {children}
42
44
  <>
43
- <DrawerTemplate
44
- handleClose={handleClose}
45
- modal={modal}
46
- setModal={setModal}
47
- />
45
+ {modal.modalType == 'drawer' ? (
46
+ <DrawerTemplate
47
+ handleClose={handleClose}
48
+ modal={modal}
49
+ setModal={setModal}
50
+ />
51
+ ) : modal.modalType == 'dialog' ? (
52
+ <DialogTemplate
53
+ handleClose={handleClose}
54
+ modal={modal}
55
+ setModal={setModal}
56
+ />
57
+ ) : (
58
+ <ErrorTemplate
59
+ handleClose={handleClose}
60
+ modal={modal}
61
+ setModal={setModal}
62
+ />
63
+ )}
48
64
  </>
49
65
  </DialogContext.Provider>
50
66
  )
@@ -0,0 +1,77 @@
1
+ import { Box, Button, Modal, Typography, styled } from '@mui/material'
2
+ import { animatedImage } from '../../assets/images'
3
+
4
+ const style = {
5
+ position: 'absolute' as 'absolute',
6
+ top: '50%',
7
+ left: '50%',
8
+ transform: 'translate(-50%, -50%)',
9
+ bgcolor: 'background.paper',
10
+ boxShadow: 24,
11
+ width: '700px',
12
+ borderRadius: '10px',
13
+ display: 'flex',
14
+ gap: '15px',
15
+ flexDirection: 'column',
16
+ padding: '20px',
17
+ }
18
+
19
+ const StyledImage = styled(Box)(({ theme }) => ({
20
+ width: '154px',
21
+ margin: 'auto',
22
+ '> img': {
23
+ width: '100%',
24
+ height: '120px',
25
+ objectFit: 'contain',
26
+ },
27
+ }))
28
+
29
+ const MessageContainer = styled(Box)(({ theme }) => ({
30
+ maxHeight: '60vh',
31
+ padding: '15px',
32
+ overflowY: 'auto',
33
+ '&::-webkit-scrollbar': {
34
+ width: '0.5em',
35
+ height: '0.5em',
36
+ },
37
+ '&::-webkit-scrollbar-thumb': {
38
+ backgroundColor: 'rgba(0, 0, 0, 0.15)',
39
+ borderRadius: '3px',
40
+ '&:hover': {
41
+ background: 'rgba(0, 0, 0, 0.2)',
42
+ },
43
+ },
44
+ }))
45
+
46
+ export default function ErrorTemplate({
47
+ handleClose,
48
+ modal,
49
+ setModal,
50
+ ...props
51
+ }) {
52
+ return (
53
+ <Modal open={modal.open} onClose={handleClose}>
54
+ <Box sx={style}>
55
+ <StyledImage>
56
+ <img src={animatedImage} alt="Error Image" />
57
+ </StyledImage>
58
+
59
+ <MessageContainer>
60
+ <Typography
61
+ variant="h6"
62
+ sx={{ textAlign: 'center', lineHeight: 1.7 }}
63
+ >
64
+ {modal.content({ close: handleClose })}
65
+ </Typography>
66
+ </MessageContainer>
67
+ <Button
68
+ variant="outlined"
69
+ onClick={handleClose}
70
+ sx={{ height: '40px', margin: 'auto', width: '200px' }}
71
+ >
72
+ OK
73
+ </Button>
74
+ </Box>
75
+ </Modal>
76
+ )
77
+ }
@@ -0,0 +1,88 @@
1
+ import { Box, Button, Modal, Typography, styled } from '@mui/material'
2
+ import { useEffect, useState } from 'react'
3
+ import { animatedImage } from '../assets/images'
4
+
5
+ const style = {
6
+ position: 'absolute' as 'absolute',
7
+ top: '50%',
8
+ left: '50%',
9
+ transform: 'translate(-50%, -50%)',
10
+ bgcolor: 'background.paper',
11
+ boxShadow: 24,
12
+ width: '700px',
13
+ borderRadius: '10px',
14
+ display: 'flex',
15
+ gap: '15px',
16
+ flexDirection: 'column',
17
+ padding: '20px',
18
+ }
19
+
20
+ const StyledImage = styled(Box)(({ theme }) => ({
21
+ width: '154px',
22
+ margin: 'auto',
23
+ '> img': {
24
+ width: '100%',
25
+ height: '120px',
26
+ objectFit: 'contain',
27
+ },
28
+ }))
29
+
30
+ const MessageContainer = styled(Box)(({ theme }) => ({
31
+ maxHeight: '60vh',
32
+ padding: '15px',
33
+ overflowY: 'auto',
34
+ '&::-webkit-scrollbar': {
35
+ width: '0.5em',
36
+ height: '0.5em',
37
+ },
38
+ '&::-webkit-scrollbar-thumb': {
39
+ backgroundColor: 'rgba(0, 0, 0, 0.15)',
40
+ borderRadius: '3px',
41
+ '&:hover': {
42
+ background: 'rgba(0, 0, 0, 0.2)',
43
+ },
44
+ },
45
+ }))
46
+
47
+ export default function ErrorModal({
48
+ errorMessage,
49
+ ...props
50
+ }: {
51
+ errorMessage: string
52
+ }) {
53
+ const [open, setOpen] = useState(false)
54
+
55
+ const handleClose = () => {
56
+ setOpen((prev) => !prev)
57
+ }
58
+
59
+ useEffect(() => {
60
+ setOpen(true)
61
+ }, [])
62
+
63
+ return (
64
+ <Modal open={open} onClose={handleClose}>
65
+ <Box sx={style}>
66
+ <StyledImage>
67
+ <img src={animatedImage} alt="Error Image" />
68
+ </StyledImage>
69
+
70
+ <MessageContainer>
71
+ <Typography
72
+ variant="h6"
73
+ sx={{ textAlign: 'center', lineHeight: 1.7 }}
74
+ >
75
+ {errorMessage}
76
+ </Typography>
77
+ </MessageContainer>
78
+ <Button
79
+ variant="outlined"
80
+ onClick={handleClose}
81
+ sx={{ height: '40px', margin: 'auto', width: '200px' }}
82
+ >
83
+ OK
84
+ </Button>
85
+ </Box>
86
+ </Modal>
87
+ )
88
+ }
@@ -0,0 +1,73 @@
1
+ import { Box, Button, Modal, styled, Typography } from '@mui/material'
2
+ import { animatedImage } from '../../assets/images'
3
+
4
+ const style = {
5
+ position: 'absolute' as 'absolute',
6
+ top: '50%',
7
+ left: '50%',
8
+ transform: 'translate(-50%, -50%)',
9
+ bgcolor: 'background.paper',
10
+ boxShadow: 24,
11
+ width: '700px',
12
+ borderRadius: '10px',
13
+ display: 'flex',
14
+ gap: '15px',
15
+ flexDirection: 'column',
16
+ padding: '20px',
17
+ }
18
+
19
+ const StyledImage = styled(Box)(({ theme }) => ({
20
+ width: '154px',
21
+ margin: 'auto',
22
+ '> img': {
23
+ width: '100%',
24
+ height: '120px',
25
+ objectFit: 'contain',
26
+ },
27
+ }))
28
+
29
+ const MessageContainer = styled(Box)(({ theme }) => ({
30
+ maxHeight: '60vh',
31
+ padding: '15px',
32
+ overflowY: 'auto',
33
+ '&::-webkit-scrollbar': {
34
+ width: '0.5em',
35
+ height: '0.5em',
36
+ },
37
+ '&::-webkit-scrollbar-thumb': {
38
+ backgroundColor: 'rgba(0, 0, 0, 0.15)',
39
+ borderRadius: '3px',
40
+ '&:hover': {
41
+ background: 'rgba(0, 0, 0, 0.2)',
42
+ },
43
+ },
44
+ }))
45
+
46
+ export const ErrorModalTemplate = ({ handleClose, modal, setModal }) => {
47
+ return (
48
+ <Modal open={modal.open} onClose={handleClose}>
49
+ <Box sx={style}>
50
+ <StyledImage>
51
+ <img src={animatedImage} alt="Error Image" />
52
+ </StyledImage>
53
+
54
+ <MessageContainer>
55
+ <Typography
56
+ variant="h6"
57
+ sx={{ textAlign: 'center', lineHeight: 1.7 }}
58
+ >
59
+ {modal?.errorMessage}
60
+ </Typography>
61
+ </MessageContainer>
62
+ {modal.content && modal.content({ close: handleClose })}
63
+ <Button
64
+ variant="outlined"
65
+ onClick={() => setModal({ ...modal, open: false })}
66
+ sx={{ height: '40px', margin: 'auto', width: '200px' }}
67
+ >
68
+ Close
69
+ </Button>
70
+ </Box>
71
+ </Modal>
72
+ )
73
+ }
@@ -0,0 +1,57 @@
1
+ import { createContext, useContext, useState } from 'react'
2
+ import { ErrorModalTemplate } from './ErrorModalTemplate'
3
+
4
+ interface IProps {
5
+ content?: ({ close }: { close: () => void }) => any
6
+ error?: any
7
+ fallBack?: string
8
+ }
9
+
10
+ type ContextType = (props: IProps) => void
11
+
12
+ const ErrorModalContext = createContext<ContextType>(null)
13
+
14
+ export default function ErrorModalProvider({ children }) {
15
+ const [modal, setModal] = useState({
16
+ open: false,
17
+ content: ({ close }) => null,
18
+ errorMessage: null,
19
+ })
20
+
21
+ const handleClose = () => {
22
+ setModal({
23
+ ...modal,
24
+ open: false,
25
+ })
26
+ }
27
+
28
+ const showModal = (props) => {
29
+ const fallbackMessage = props.fallBack ?? 'Something went wrong.'
30
+ const errorMessage =
31
+ typeof props.error?.response?.data?.message !== 'string'
32
+ ? props.error?.response?.data?.message?.join('\n') ?? fallbackMessage
33
+ : props.error?.response?.data?.message
34
+
35
+ setModal({
36
+ ...modal,
37
+ open: true,
38
+ content: props.content,
39
+ errorMessage: errorMessage,
40
+ })
41
+ }
42
+
43
+ return (
44
+ <ErrorModalContext.Provider value={showModal}>
45
+ {children}
46
+ <>
47
+ <ErrorModalTemplate
48
+ handleClose={handleClose}
49
+ modal={modal}
50
+ setModal={setModal}
51
+ />
52
+ </>
53
+ </ErrorModalContext.Provider>
54
+ )
55
+ }
56
+
57
+ export const useErrorModal = () => useContext(ErrorModalContext)
@@ -6,7 +6,7 @@ import { SingleSelect } from '../Input'
6
6
  type QuotaSelectorProps = {
7
7
  name?: string
8
8
  label: string
9
- filters?: { courseId: number }
9
+ filters?: { courseId: number | string }
10
10
  required?: boolean
11
11
  onChange?: (value: any) => void
12
12
  allowAll?: boolean
@@ -1,8 +1,7 @@
1
- import { Done, Close, WarningAmberRounded } from '@mui/icons-material'
1
+ import { Close, Done, WarningAmberRounded } from '@mui/icons-material'
2
2
  import { Box } from '@mui/material'
3
- import { Slide, ToastContainer as ReactToastContainer } from 'react-toastify'
3
+ import { ToastContainer as ReactToastContainer, Slide } from 'react-toastify'
4
4
  import 'react-toastify/dist/ReactToastify.css'
5
- import WarningAmberRoundedIcon from '@mui/icons-material/WarningAmberRounded'
6
5
 
7
6
  export default function ToastContainer() {
8
7
  return (
@@ -1,3 +1,5 @@
1
+ import { ReactNode } from "react"
2
+
1
3
  export const batchOptions = Array.from({ length: 12 }, (_, i) => {
2
4
  return `${2012 + i} - ${2012 + i + 1}`
3
5
  }).reverse()
@@ -126,3 +128,50 @@ export const ProfileApplicationType = [
126
128
  value: 'hrms',
127
129
  },
128
130
  ]
131
+
132
+ export interface Option {
133
+ label: string | ReactNode
134
+ value: any
135
+ }
136
+
137
+ export interface GenerateLabelValueProps {
138
+ labelKey?: string
139
+ valueKey?: string
140
+ useIndexAsValue?: boolean
141
+ startFromOne?: boolean
142
+ showAll?: boolean
143
+ }
144
+
145
+ export const generateLabelValueOptions = (
146
+ options: any[],
147
+ {
148
+ labelKey,
149
+ valueKey,
150
+ useIndexAsValue,
151
+ startFromOne,
152
+ showAll,
153
+ }: GenerateLabelValueProps = {
154
+ labelKey: null,
155
+ valueKey: null,
156
+ useIndexAsValue: false,
157
+ startFromOne: false,
158
+ showAll: false,
159
+ },
160
+ ): Option[] => {
161
+ let result
162
+ if (labelKey == null) {
163
+ result = options.map((option, index) => ({
164
+ label: option[0].toUpperCase() + option.slice(1),
165
+ value: useIndexAsValue ? (startFromOne ? index + 1 : index) : option,
166
+ }))
167
+ } else {
168
+ result = options.map((option) => ({
169
+ label: option[labelKey],
170
+ value: option[valueKey],
171
+ }))
172
+ }
173
+ if (showAll) {
174
+ result.unshift({ label: 'All', value: 'all' })
175
+ }
176
+ return result
177
+ }
@@ -8,9 +8,9 @@ import { ReactNode, useEffect } from 'react'
8
8
  import { ToastContainer } from '../components'
9
9
  import DialogProvider from '../components/DrawerWrapper/DrawerWrapper'
10
10
  import GlobalNetworkLoadingIndicator from '../components/ErrorBoundary/GlobalNetworkLoadingIndicator'
11
- import FreshChatButton from '../components/Layout/Header/HeaderActions/FreshChatButton'
12
- import RootModal from './RootModal'
11
+ import ErrorModalProvider from '../components/ErrorModalWrapper/ErrorModalWrapper'
13
12
  import { isDevelopment } from '../constants'
13
+ import RootModal from './RootModal'
14
14
 
15
15
  export const campxTenantKey = Cookies.get('campx_tenant')
16
16
  export const urlTenantKey = window.location.pathname.split('/')[1]
@@ -60,7 +60,7 @@ export default function Providers({ children }: { children: ReactNode }) {
60
60
  <MuiThemeProvider>
61
61
  <ConfirmContextProvider>
62
62
  <DialogProvider>
63
- {children}
63
+ <ErrorModalProvider>{children}</ErrorModalProvider>
64
64
  <GlobalNetworkLoadingIndicator />
65
65
  <RootModal />
66
66
  <ToastContainer />
@@ -195,10 +195,20 @@ export enum SquarePermissions {
195
195
  COURSE_REGISTRATION_DELETE = 'can_course_registration_delete',
196
196
 
197
197
  // Mentors
198
- CAN_MENTORS_VIEW = 'can_mentors_view',
199
- CAN_ALL_MENTORS_VIEW = 'can_all_mentors_view',
200
- CAN_MY_MENTEES_VIEW = 'can_my_mentees_view',
201
- CAN_SESSION_CREATE = 'can_session_create',
198
+ CAN_VIEW_MENTORS = 'can_view_mentors',
199
+ CAN_ADD_MENTORS = 'can_add_mentors',
200
+ CAN_ASSIGN_MENTEES = 'can_assign_mentees',
201
+ CAN_REMOVE_MENTEES = 'can_remove_mentees',
202
+ CAN_VIEW_MENTOR_REPORT = 'can_view_mentor_report',
203
+ CAN_COMMENT_ON_MENTOR_REPORT = 'can_comment_on_mentor_report',
204
+ CAN_VIEW_MENTEES = 'can_view_mentees',
205
+ CAN_CREATE_SESSION = 'can_create_session',
206
+ CAN_VIEW_MENTEE_REPORT = 'can_view_mentee_report',
207
+ CAN_VIEW_MENTOR_SESSIONS = 'can_view_mentor_sessions',
208
+ CAN_VIEW_MENTOR_SESSIONS_COMMENTS = 'can_view_mentor_sessions_comments',
209
+ CAN_ADD_MENTOR_SESSIONS_COMMENTS = 'can_add_mentor_sessions_comments',
210
+ CAN_EDIT_MENTOR_SESSIONS_COMMENTS = 'can_edit_mentor_sessions_comments',
211
+ CAN_DELETE_MENTOR_SESSIONS_COMMENTS = 'can_delete_mentor_sessions_comments',
202
212
 
203
213
  // Extra Cirricular activities
204
214
  EXTRA_CURRICULAR_ACTIVITIES_NEWS_FEED = 'can_extra_curricular_activities_news_feed',