@campxdev/shared 1.11.5 → 1.11.7
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.
- package/exports.ts +4 -0
- package/package.json +4 -4
- package/src/assets/images/animation.gif +0 -0
- package/src/assets/images/index.ts +4 -3
- package/src/components/DatabaseConfiguration/DatabaseConfiguration.tsx +28 -0
- package/src/components/DatabaseConfiguration/DatabaseConfigurationForm.tsx +87 -0
- package/src/components/DatabaseConfiguration/components/AddConnectionDrawerButton.tsx +30 -0
- package/src/components/DatabaseConfiguration/components/ConnectionCard.tsx +79 -0
- package/src/components/DatabaseConfiguration/index.ts +5 -0
- package/src/components/DatabaseConfiguration/service.ts +6 -0
- package/src/components/DatabaseConfiguration/styles.ts +30 -0
- package/src/components/DrawerWrapper/DialogTemplate.tsx +58 -0
- package/src/components/DrawerWrapper/DrawerWrapper.tsx +23 -7
- package/src/components/DrawerWrapper/ErrorTemplate.tsx +77 -0
- package/src/components/ErrorModal.tsx +88 -0
- package/src/components/ErrorModalWrapper/ErrorModalTemplate.tsx +73 -0
- package/src/components/ErrorModalWrapper/ErrorModalWrapper.tsx +57 -0
- package/src/components/ReportHeader.tsx +11 -7
- package/src/components/ToastContainer/ToastContainer.tsx +2 -3
- package/src/constants/UIConstants.ts +49 -0
- package/src/contexts/Providers.tsx +3 -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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@campxdev/shared",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.7",
|
|
4
4
|
"main": "./exports.ts",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"start": "react-scripts start",
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
"@types/react": "^18.0.25",
|
|
74
74
|
"@types/react-flatpickr": "^3.8.8",
|
|
75
75
|
"@types/react-helmet": "^6.1.6",
|
|
76
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
77
|
-
"@typescript-eslint/parser": "^
|
|
76
|
+
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
|
77
|
+
"@typescript-eslint/parser": "^6.7.4",
|
|
78
78
|
"eslint": "^8.23.0",
|
|
79
79
|
"eslint-config-prettier": "^8.5.0",
|
|
80
80
|
"eslint-import-resolver-typescript": "^3.5.0",
|
|
@@ -85,6 +85,6 @@
|
|
|
85
85
|
"prettier": "^2.5.0",
|
|
86
86
|
"react-scripts": "^5.0.1",
|
|
87
87
|
"storybook-addon-react-router-v6": "^0.2.1",
|
|
88
|
-
"typescript": "^
|
|
88
|
+
"typescript": "^5.2.2"
|
|
89
89
|
}
|
|
90
90
|
}
|
|
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,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
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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)
|
|
@@ -118,7 +118,7 @@ export default function ReportHeader({
|
|
|
118
118
|
src={
|
|
119
119
|
reportHeaderImageUrl ||
|
|
120
120
|
current?.reportHeader?.url ||
|
|
121
|
-
masterInstitution
|
|
121
|
+
masterInstitution?.reportHeader?.url
|
|
122
122
|
}
|
|
123
123
|
/>
|
|
124
124
|
</>
|
|
@@ -142,19 +142,23 @@ export default function ReportHeader({
|
|
|
142
142
|
sx={recognitionDetailsSx}
|
|
143
143
|
>
|
|
144
144
|
{recognitionDetailsText ||
|
|
145
|
-
current
|
|
146
|
-
masterInstitution
|
|
145
|
+
current?.recognitionDetails ||
|
|
146
|
+
masterInstitution?.recognitionDetails ||
|
|
147
|
+
''}
|
|
147
148
|
</Typography>
|
|
148
149
|
)}
|
|
149
150
|
{!hideAddress && (
|
|
150
151
|
<Typography variant={addressVariant} sx={addressSx}>
|
|
151
|
-
{addressText ||
|
|
152
|
+
{addressText ||
|
|
153
|
+
current?.address ||
|
|
154
|
+
masterInstitution?.address ||
|
|
155
|
+
''}
|
|
152
156
|
</Typography>
|
|
153
157
|
)}
|
|
154
158
|
{!hidePhone && (
|
|
155
159
|
<Typography variant={phoneVariant} sx={phoneSx}>
|
|
156
160
|
{'Phone: ' +
|
|
157
|
-
(phoneText || current
|
|
161
|
+
(phoneText || current?.phone || masterInstitution?.phone)}
|
|
158
162
|
</Typography>
|
|
159
163
|
)}
|
|
160
164
|
</>
|
|
@@ -168,8 +172,8 @@ export default function ReportHeader({
|
|
|
168
172
|
/>
|
|
169
173
|
)}
|
|
170
174
|
{typographyList &&
|
|
171
|
-
typographyList.map((s) => (
|
|
172
|
-
<Typography variant={s.variant || 'body1'} sx={s.sx}>
|
|
175
|
+
typographyList.map((s, index) => (
|
|
176
|
+
<Typography key={index} variant={s.variant || 'body1'} sx={s.sx}>
|
|
173
177
|
{s.text}
|
|
174
178
|
</Typography>
|
|
175
179
|
))}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Close, Done, WarningAmberRounded } from '@mui/icons-material'
|
|
2
2
|
import { Box } from '@mui/material'
|
|
3
|
-
import {
|
|
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
|
|
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 />
|