@campxdev/shared 1.11.6 → 1.11.7-0.alpha-22

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.
Files changed (112) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/exports.ts +4 -0
  3. package/package.json +17 -9
  4. package/src/assets/images/X.png +0 -0
  5. package/src/assets/images/active_devices.svg +3 -0
  6. package/src/assets/images/animation.gif +0 -0
  7. package/src/assets/images/change_password.svg +6 -0
  8. package/src/assets/images/clog_wheel.svg +6 -0
  9. package/src/assets/images/index.ts +28 -3
  10. package/src/assets/images/location.svg +6 -0
  11. package/src/assets/images/logout_icon.svg +6 -0
  12. package/src/assets/images/lottery.svg +22 -0
  13. package/src/assets/images/mobile.svg +7 -0
  14. package/src/assets/images/no_devices.svg +734 -0
  15. package/src/assets/images/notifications.svg +3 -0
  16. package/src/assets/images/profile.svg +6 -0
  17. package/src/assets/images/web.svg +13 -0
  18. package/src/components/ActiveDevices/ActiveDevices.tsx +60 -0
  19. package/src/components/ActiveDevices/DeviceInformationCard.tsx +97 -0
  20. package/src/components/ActiveDevices/index.ts +1 -0
  21. package/src/components/ActivityLog/ActivityLog.tsx +268 -0
  22. package/src/components/ActivityLog/Styles.tsx +35 -0
  23. package/src/components/ActivityLog/index.ts +1 -0
  24. package/src/components/ApplicationProfile/ApplicationProfile.tsx +1 -0
  25. package/src/components/ApplicationProfile/UserProfileRelation.tsx +4 -1
  26. package/src/components/DatabaseConfiguration/DatabaseConfiguration.tsx +28 -0
  27. package/src/components/DatabaseConfiguration/DatabaseConfigurationForm.tsx +87 -0
  28. package/src/components/DatabaseConfiguration/components/AddConnectionDrawerButton.tsx +30 -0
  29. package/src/components/DatabaseConfiguration/components/ConnectionCard.tsx +79 -0
  30. package/src/components/DatabaseConfiguration/index.ts +5 -0
  31. package/src/components/DatabaseConfiguration/service.ts +6 -0
  32. package/src/components/DatabaseConfiguration/styles.ts +30 -0
  33. package/src/components/DrawerWrapper/DialogTemplate.tsx +58 -0
  34. package/src/components/DrawerWrapper/DrawerWrapper.tsx +23 -7
  35. package/src/components/DrawerWrapper/ErrorTemplate.tsx +77 -0
  36. package/src/components/ErrorModal.tsx +88 -0
  37. package/src/components/ErrorModalWrapper/ErrorModalTemplate.tsx +118 -0
  38. package/src/components/ErrorModalWrapper/ErrorModalWrapper.tsx +76 -0
  39. package/src/components/FilterComponents/SearchBar.tsx +5 -2
  40. package/src/components/Form/Form.tsx +4 -1
  41. package/src/components/HookForm/AutoCompleteSearch.tsx +3 -0
  42. package/src/components/HookForm/MultiSelect.tsx +1 -0
  43. package/src/components/HookForm/SingleSelect.tsx +2 -2
  44. package/src/components/ImageUpload.tsx +4 -1
  45. package/src/components/Input/MultiSelect.tsx +1 -0
  46. package/src/components/Input/SearchSingleSelect.tsx +1 -1
  47. package/src/components/Input/SingleSelect.tsx +7 -7
  48. package/src/components/Institutions/InsititutionsDialog.tsx +2 -2
  49. package/src/components/Layout/Header/AppHeader.tsx +23 -6
  50. package/src/components/Layout/Header/HeaderActions/CogWheelMenu.tsx +2 -2
  51. package/src/components/Layout/Header/HeaderActions/HeaderActions.tsx +41 -16
  52. package/src/components/Layout/Header/HeaderActions/UserBox.tsx +48 -6
  53. package/src/components/Layout/Header/applications.ts +43 -30
  54. package/src/components/Layout/LayoutWrapper.tsx +82 -4
  55. package/src/components/Layout/SideNav.tsx +42 -9
  56. package/src/components/LoginForm.tsx +53 -1
  57. package/src/components/MongoCharts/MongoDashboard.tsx +146 -0
  58. package/src/components/MongoCharts/index.tsx +1 -0
  59. package/src/components/MyProfile/MyProfile.tsx +1 -1
  60. package/src/components/ReportHeader.tsx +2 -2
  61. package/src/components/Selectors/ClassRoomSelector.tsx +2 -2
  62. package/src/components/Selectors/CourseSelector.tsx +2 -2
  63. package/src/components/Selectors/DepartmentSelector.tsx +2 -2
  64. package/src/components/Selectors/ExamGroupSelector.tsx +19 -10
  65. package/src/components/Selectors/FacultySelector.tsx +2 -2
  66. package/src/components/Selectors/FeeTypeSelector.tsx +2 -2
  67. package/src/components/Selectors/FormSelectors/FormClassRoomSelector.tsx +2 -2
  68. package/src/components/Selectors/FormSelectors/FormCourseSelector.tsx +2 -2
  69. package/src/components/Selectors/FormSelectors/FormDepartmentSelector.tsx +2 -2
  70. package/src/components/Selectors/FormSelectors/FormExamGroupSelector.tsx +2 -2
  71. package/src/components/Selectors/FormSelectors/FormFacultySelector.tsx +2 -2
  72. package/src/components/Selectors/FormSelectors/FormFeeTypeSelector.tsx +2 -2
  73. package/src/components/Selectors/FormSelectors/FormProgramSelector.tsx +2 -2
  74. package/src/components/Selectors/FormSelectors/FormQuotaSelector.tsx +3 -3
  75. package/src/components/Selectors/FormSelectors/FormSemesterSelector.tsx +2 -2
  76. package/src/components/Selectors/FormSelectors/MultiSelect/MultiFacultySelector.tsx +2 -2
  77. package/src/components/Selectors/FormSelectors/MultiSelect/MultiFeeTypeSelector.tsx +2 -2
  78. package/src/components/Selectors/FormSelectors/MultiSelect/MultiProgramSelector.tsx +2 -2
  79. package/src/components/Selectors/FormSelectors/MultiSelect/MultiQuotaSelector.tsx +2 -2
  80. package/src/components/Selectors/ProgramSelector.tsx +3 -3
  81. package/src/components/Selectors/QuotaSelector.tsx +3 -3
  82. package/src/components/Selectors/SemesterSelector.tsx +2 -2
  83. package/src/components/SignatureFooter.tsx +35 -0
  84. package/src/components/SwitchButton.tsx +6 -1
  85. package/src/components/Tables/2DTable/Table.tsx +20 -23
  86. package/src/components/Tables/BasicTable/Table.tsx +22 -13
  87. package/src/components/Tables/BasicTable/TableFooter.tsx +35 -9
  88. package/src/components/Tables/BasicTable/styles.ts +1 -1
  89. package/src/components/Tables/ReactTable/ReactTable.tsx +42 -8
  90. package/src/components/Tables/common/types.ts +1 -0
  91. package/src/components/Tabs/TabsContainer.tsx +5 -5
  92. package/src/components/Tabs/styles.tsx +1 -0
  93. package/src/components/ToastContainer/ToastContainer.tsx +2 -3
  94. package/src/components/UploadButton/UploadButton.tsx +3 -1
  95. package/src/components/UploadButton/types.ts +2 -2
  96. package/src/components/UploadDocument.tsx +3 -0
  97. package/src/components/UploadFileDialog/UploadFileDialog.tsx +20 -9
  98. package/src/components/index.ts +5 -0
  99. package/src/config/axios.ts +5 -19
  100. package/src/constants/UIConstants.ts +65 -2
  101. package/src/constants/isDevelopment.ts +0 -1
  102. package/src/contexts/Providers.tsx +5 -43
  103. package/src/hooks/useAuth.ts +7 -0
  104. package/src/layouts/Components/styles.tsx +25 -7
  105. package/src/permissions/PermissionsStore.ts +658 -55
  106. package/src/permissions/ValidateAccess.tsx +37 -8
  107. package/src/shared-state/PermissionsStore.ts +779 -85
  108. package/src/theme/theme.d.ts +69 -35
  109. package/src/utils/debounce.ts +11 -0
  110. package/src/utils/getUrlParams.ts +13 -0
  111. package/src/utils/index.ts +6 -3
  112. package/src/utils/logout.ts +4 -8
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="18.125" height="20" viewBox="0 0 18.125 20">
2
+ <path id="bell_10_" data-name="bell (10)" d="M18.958,11.384l-1.583-5.7a7.767,7.767,0,0,0-15.064.395L1.085,11.595a4.166,4.166,0,0,0,4.067,5.07H6.08a4.166,4.166,0,0,0,8.166,0h.7a4.166,4.166,0,0,0,4.015-5.281Zm-8.795,6.948a2.5,2.5,0,0,1-2.346-1.666H12.51A2.5,2.5,0,0,1,10.163,18.332Zm6.771-4.32A2.481,2.481,0,0,1,14.944,15H5.152a2.5,2.5,0,0,1-2.44-3.042L3.937,6.444a6.1,6.1,0,0,1,11.832-.31l1.583,5.7a2.481,2.481,0,0,1-.418,2.181Z" transform="translate(-0.986 -0.002)" fill="#121212"/>
3
+ </svg>
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="17.049" height="22.632" viewBox="0 0 17.049 22.632">
2
+ <g id="user" transform="translate(-2.85 0.15)">
3
+ <path id="Path_38566" data-name="Path 38566" d="M11.583,11.166A5.583,5.583,0,1,0,6,5.583a5.583,5.583,0,0,0,5.583,5.583Zm0-9.305A3.722,3.722,0,1,1,7.861,5.583,3.722,3.722,0,0,1,11.583,1.861Z" transform="translate(-0.208 0)" stroke="#fff" stroke-width="0.3"/>
4
+ <path id="Path_38567" data-name="Path 38567" d="M11.375,14A8.384,8.384,0,0,0,3,22.375a.931.931,0,1,0,1.861,0,6.514,6.514,0,1,1,13.027,0,.931.931,0,0,0,1.861,0A8.384,8.384,0,0,0,11.375,14Z" transform="translate(0 -0.973)" stroke="#fff" stroke-width="0.3"/>
5
+ </g>
6
+ </svg>
@@ -0,0 +1,13 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <g id="Group_5289" data-name="Group 5289" transform="translate(-5577 -13602)">
3
+ <g id="Group_5288" data-name="Group 5288">
4
+ <g id="Group_5287" data-name="Group 5287">
5
+ <path id="Vector" d="M4.44,0H15.55C19.11,0,20,.89,20,4.44v6.33c0,3.56-.89,4.44-4.44,4.44H4.44C.89,15.22,0,14.33,0,10.78V4.44C0,.89.89,0,4.44,0Z" transform="translate(5579 13604)" fill="none" stroke="#292d32" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
6
+ <path id="Vector-2" data-name="Vector" d="M0,0V4.78" transform="translate(5589 13619.22)" fill="none" stroke="#292d32" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
7
+ <path id="Vector-3" data-name="Vector" d="M0,0H20" transform="translate(5579 13615)" fill="none" stroke="#292d32" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
8
+ <path id="Vector-4" data-name="Vector" d="M0,0H9" transform="translate(5584.5 13624)" fill="none" stroke="#292d32" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
9
+ <path id="Vector-5" data-name="Vector" d="M0,0H24V24H0Z" transform="translate(5577 13602)" fill="none" opacity="0"/>
10
+ </g>
11
+ </g>
12
+ </g>
13
+ </svg>
@@ -0,0 +1,60 @@
1
+ import { Button, Stack } from '@mui/material'
2
+
3
+ import { useMutation, useQuery } from 'react-query'
4
+ import { NoDataIllustration } from '..'
5
+ import { noDevices } from '../../assets/images'
6
+ import axios from '../../config/axios'
7
+ import useConfirm from '../PopupConfirm/useConfirm'
8
+ import Spinner from '../Spinner'
9
+ import { DeviceInformationCard } from './DeviceInformationCard'
10
+
11
+ const ActiveDevices = ({ close }) => {
12
+ const { isConfirmed } = useConfirm()
13
+ const {
14
+ data: activeSessions,
15
+ isLoading,
16
+ isRefetching,
17
+ refetch,
18
+ } = useQuery('activeDevices', () =>
19
+ axios.get('/auth-server/auth/active-sessions').then((res) => res?.data),
20
+ )
21
+
22
+ const { mutate: logoutAllOtherDevices } = useMutation(
23
+ () => axios.post('/auth-server/auth/logout-active-sessions'),
24
+ {
25
+ onSuccess: () => {
26
+ close()
27
+ },
28
+ },
29
+ )
30
+
31
+ const handleLogoutAllOtherDevices = async () => {
32
+ const confirm = await isConfirmed(
33
+ 'Are you sure you want to logout from all other devices?',
34
+ )
35
+ if (confirm) {
36
+ logoutAllOtherDevices()
37
+ }
38
+ }
39
+ if (isLoading || isRefetching) return <Spinner />
40
+ if (activeSessions?.length == 0)
41
+ return (
42
+ <NoDataIllustration
43
+ height="60vh"
44
+ message="No active devices found"
45
+ imageSrc={noDevices.default}
46
+ />
47
+ )
48
+ return (
49
+ <Stack height="60vh" padding="10px 50px" gap={3} alignItems="flex-end">
50
+ <Button onClick={handleLogoutAllOtherDevices} sx={{ width: '300px' }}>
51
+ Logout from All Other Devices
52
+ </Button>
53
+ {activeSessions?.map((sessionData) => (
54
+ <DeviceInformationCard sessionData={sessionData} refetch={refetch} />
55
+ ))}
56
+ </Stack>
57
+ )
58
+ }
59
+
60
+ export default ActiveDevices
@@ -0,0 +1,97 @@
1
+ import { Box, Button, Stack, Typography, styled } from '@mui/material'
2
+ import { format } from 'date-fns-tz'
3
+ import { useMutation } from 'react-query'
4
+ import { toast } from 'react-toastify'
5
+ import { location, mobile, web } from '../../assets/images'
6
+ import axios from '../../config/axios'
7
+ import useConfirm from '../PopupConfirm/useConfirm'
8
+
9
+ const StyledIconContainer = styled(Box)(({ theme }) => ({
10
+ backgroundColor: '#ED90350F',
11
+ borderRadius: '10px',
12
+ border: '1px solid #ED903580',
13
+ width: '60px',
14
+ height: '60px',
15
+ padding: '17px 0px 0px 17px',
16
+ }))
17
+
18
+ const StyledTrustedContainer = styled(Box)(({ theme }) => ({
19
+ backgroundColor: '#F4A101',
20
+ borderRadius: '15px',
21
+ padding: '3px 15px',
22
+ }))
23
+
24
+ export const DeviceInformationCard = ({ sessionData, refetch }) => {
25
+ const { isConfirmed } = useConfirm()
26
+
27
+ const { mutate: logout } = useMutation(
28
+ () =>
29
+ axios.post('/auth-server/auth/logout-active-sessions', {
30
+ deviceInformationId: sessionData?.deviceInformation?._id,
31
+ }),
32
+ {
33
+ onSuccess: () => {
34
+ refetch()
35
+ },
36
+ onError: (error: any) => {
37
+ toast.error(error?.response?.data?.message)
38
+ },
39
+ },
40
+ )
41
+ const handleLogout = async () => {
42
+ const confirmed = await isConfirmed(
43
+ 'Are you sure you want to logout from this device?',
44
+ )
45
+ if (confirmed) logout()
46
+ }
47
+ return (
48
+ <Box
49
+ width="100%"
50
+ border="1px solid #1212121A"
51
+ borderRadius="10px"
52
+ padding="20px 0px 20px 0px"
53
+ >
54
+ <Stack
55
+ direction="row"
56
+ gap={2}
57
+ alignItems="center"
58
+ justifyContent="space-around"
59
+ >
60
+ <Stack direction="row" alignItems="center" gap={2}>
61
+ <StyledIconContainer>
62
+ <img
63
+ src={(sessionData.type = 'WEB' ? web.default : mobile.default)}
64
+ />
65
+ </StyledIconContainer>
66
+ <Stack>
67
+ <Stack direction="row" alignItems="center" gap={1}>
68
+ <Typography variant="h1" fontSize="15px">
69
+ {`${sessionData?.deviceInformation?.clientName} (${sessionData?.deviceInformation?.os})`}
70
+ </Typography>
71
+ {sessionData?.deviceInformation?.trusted && (
72
+ <StyledTrustedContainer>
73
+ <Typography variant="subtitle1" color="white" fontSize="12px">
74
+ Trusted
75
+ </Typography>
76
+ </StyledTrustedContainer>
77
+ )}
78
+ </Stack>
79
+ <Typography variant="caption">{`Last Active on ${format(
80
+ new Date(sessionData?.lastAccessedAt),
81
+ 'dd MMM, yyyy hh:mm a',
82
+ )}`}</Typography>
83
+ </Stack>
84
+ </Stack>
85
+ <Stack direction="row" alignItems="center" gap={1}>
86
+ <img src={location.default} />
87
+ <Typography variant="caption">
88
+ {sessionData?.deviceInformation?.locationName}
89
+ </Typography>
90
+ </Stack>
91
+ <Button onClick={handleLogout} variant="outlined">
92
+ Logout
93
+ </Button>
94
+ </Stack>
95
+ </Box>
96
+ )
97
+ }
@@ -0,0 +1 @@
1
+ export { default } from './ActiveDevices'
@@ -0,0 +1,268 @@
1
+ import {
2
+ Timeline,
3
+ TimelineContent,
4
+ TimelineItem,
5
+ TimelineSeparator,
6
+ } from '@mui/lab'
7
+ import { Box, SxProps, Typography } from '@mui/material'
8
+ import moment from 'moment'
9
+ import { useCallback, useRef } from 'react'
10
+ import { useInfiniteQuery } from 'react-query'
11
+ import { NoDataIllustration } from '..'
12
+ import axios from '../../config/axios'
13
+ import { useErrorModal } from '../ErrorModalWrapper/ErrorModalWrapper'
14
+ import Spinner from '../Spinner'
15
+ import Table from '../Tables/BasicTable/Table'
16
+ import {
17
+ StyledAvatar,
18
+ StyledCircleIcon,
19
+ StyledSpinnerBox,
20
+ StyledTimeLineDot,
21
+ } from './Styles'
22
+
23
+ interface Props {
24
+ endPoint: string
25
+ tableView: boolean
26
+ enableTitle?: boolean
27
+ params: any
28
+ sx?: SxProps
29
+ }
30
+
31
+ export default function ActivityLog({
32
+ endPoint,
33
+ tableView,
34
+ enableTitle,
35
+ params,
36
+ sx,
37
+ }: Props) {
38
+ const errorModal = useErrorModal()
39
+ const fetchActivities = async ({ pageParam = 0 }) => {
40
+ try {
41
+ const response = await axios.get(endPoint, { params: { ...params } })
42
+ return response?.data
43
+ } catch (error) {
44
+ // eslint-disable-next-line no-console
45
+ console.log(error)
46
+ errorModal({ error: error })
47
+ }
48
+ }
49
+
50
+ const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } =
51
+ useInfiniteQuery('activities', fetchActivities, {
52
+ getNextPageParam: (lastPage) => {
53
+ return lastPage?.length ? lastPage[lastPage?.length - 1].id : null
54
+ },
55
+ })
56
+
57
+ const activitesData = data ? data?.pages?.flatMap((page) => page) : []
58
+
59
+ if (isLoading) return <Spinner />
60
+
61
+ if (data?.pages?.length === 0 || !activitesData?.length) {
62
+ return (
63
+ <NoDataIllustration
64
+ imageSrc=""
65
+ message={
66
+ <Typography
67
+ variant="h6"
68
+ style={{ textAlign: 'center', marginTop: '20px' }}
69
+ >
70
+ {'No data found'}
71
+ </Typography>
72
+ }
73
+ />
74
+ )
75
+ }
76
+
77
+ return (
78
+ <Box>
79
+ {enableTitle && (
80
+ <Typography
81
+ variant="h1"
82
+ sx={{ fontSize: '18px', fontWeight: 700, margin: '20px' }}
83
+ >
84
+ Activity Log
85
+ </Typography>
86
+ )}
87
+
88
+ {!tableView ? (
89
+ <Box
90
+ sx={{
91
+ overflowY: 'scroll',
92
+ '&::-webkit-scrollbar': {
93
+ display: 'none',
94
+ },
95
+ height: '380px',
96
+ }}
97
+ >
98
+ <TimeLineComponent
99
+ activitesData={activitesData}
100
+ fetchNextPage={fetchNextPage}
101
+ isFetchingNextPage={isFetchingNextPage}
102
+ hasNextPage={hasNextPage}
103
+ sx={sx}
104
+ />
105
+ </Box>
106
+ ) : (
107
+ <Box sx={{ margin: '0 25px' }}>
108
+ <Table
109
+ columns={columns}
110
+ dataSource={activitesData}
111
+ loading={isLoading}
112
+ />
113
+ </Box>
114
+ )}
115
+ </Box>
116
+ )
117
+ }
118
+
119
+ const columns = [
120
+ {
121
+ title: 'Date',
122
+ dataIndex: 'timestamp',
123
+ key: 'timestamp',
124
+ render: (timestamp) =>
125
+ moment.utc(timestamp)?.local().format('DD MMM YYYY - hh:mm A'),
126
+ },
127
+ {
128
+ title: 'Action',
129
+ dataIndex: 'action',
130
+ key: 'action',
131
+ },
132
+ {
133
+ title: 'User Name',
134
+ dataIndex: 'userName',
135
+ key: 'userName',
136
+ },
137
+ {
138
+ title: 'Message',
139
+ dataIndex: 'message',
140
+ key: 'message',
141
+ render: (message) => (
142
+ <Typography sx={{ fontSize: '16px' }} variant="subtitle1">
143
+ {message?.split("'")?.map((text: string, index: number) =>
144
+ index % 2 === 0 ? (
145
+ <span key={index}>{text}</span>
146
+ ) : (
147
+ <Typography key={index} sx={{ display: 'inline', fontWeight: 900 }}>
148
+ {text}
149
+ </Typography>
150
+ ),
151
+ )}
152
+ </Typography>
153
+ ),
154
+ },
155
+ ]
156
+
157
+ export const TimeLineComponent = ({
158
+ activitesData,
159
+ isFetchingNextPage,
160
+ hasNextPage,
161
+ fetchNextPage,
162
+ sx,
163
+ }) => {
164
+ const lastItemRef = useIntersectionObserver<HTMLDivElement>(() => {
165
+ if (!isFetchingNextPage && hasNextPage) fetchNextPage()
166
+ })
167
+
168
+ return (
169
+ <Timeline sx={{ padding: 0, margin: 0 }}>
170
+ {activitesData?.map((item, index, items) => (
171
+ <Box
172
+ key={index}
173
+ ref={items.length - 1 === index ? lastItemRef : null}
174
+ sx={{ maxWidth: '550px', ...sx }}
175
+ >
176
+ <TimelineItem
177
+ sx={{
178
+ margin: 2,
179
+ padding: 0,
180
+ '&:before': { display: 'none' },
181
+ }}
182
+ >
183
+ <TimelineSeparator>
184
+ <StyledTimeLineDot>
185
+ <StyledCircleIcon />
186
+ </StyledTimeLineDot>
187
+ {index < activitesData?.length - 1 && (
188
+ <Box
189
+ sx={{
190
+ width: '1px',
191
+ height: '115%',
192
+ bgcolor: '#12121233',
193
+ position: 'absolute',
194
+ top: '25px',
195
+ }}
196
+ />
197
+ )}
198
+ </TimelineSeparator>
199
+ <TimelineContent sx={{ padding: '6px 8px' }}>
200
+ <Box>
201
+ <Typography variant="subtitle2" sx={{ fontSize: '14px' }}>
202
+ {moment
203
+ .utc(item?.timestamp)
204
+ .local()
205
+ .format('DD MMM YYYY - hh:mm A')}
206
+ </Typography>
207
+ <Typography sx={{ fontSize: '16px' }} variant="subtitle1">
208
+ {item?.message
209
+ ?.split("'")
210
+ ?.map((text: string, index: number) =>
211
+ index % 2 === 0 ? (
212
+ <span key={index}>{text}</span>
213
+ ) : (
214
+ <Typography
215
+ key={index}
216
+ sx={{ display: 'inline', fontWeight: 900 }}
217
+ >
218
+ {text}
219
+ </Typography>
220
+ ),
221
+ )}
222
+ </Typography>
223
+ <Typography
224
+ style={{
225
+ display: 'flex',
226
+ alignItems: 'center',
227
+ marginTop: '8px',
228
+ }}
229
+ >
230
+ <StyledAvatar>
231
+ {item?.userName?.charAt(0)?.toUpperCase()}
232
+ </StyledAvatar>
233
+ <Typography sx={{ fontSize: '13px', fontWeight: 900 }}>
234
+ {item?.userName}
235
+ </Typography>
236
+ </Typography>
237
+ </Box>
238
+ </TimelineContent>
239
+ </TimelineItem>
240
+ {isFetchingNextPage && index === items?.length - 1 && hasNextPage && (
241
+ <StyledSpinnerBox>
242
+ <Spinner />
243
+ </StyledSpinnerBox>
244
+ )}
245
+ </Box>
246
+ ))}
247
+ </Timeline>
248
+ )
249
+ }
250
+
251
+ function useIntersectionObserver<T extends HTMLDivElement>(
252
+ callback: () => void,
253
+ ) {
254
+ const observer = useRef<IntersectionObserver | null>(null)
255
+
256
+ const handleObserver = useCallback(
257
+ (node: T) => {
258
+ if (observer?.current) observer?.current?.disconnect()
259
+ observer.current = new IntersectionObserver((entries) => {
260
+ if (entries[0]?.isIntersecting) callback()
261
+ })
262
+ if (node) observer?.current?.observe(node)
263
+ },
264
+ [callback],
265
+ )
266
+
267
+ return handleObserver
268
+ }
@@ -0,0 +1,35 @@
1
+ import CircleRoundedIcon from '@mui/icons-material/CircleRounded'
2
+ import { TimelineDot } from '@mui/lab'
3
+ import { Avatar, Box, styled } from '@mui/material'
4
+
5
+ export const StyledTimeLineDot = styled(TimelineDot)({
6
+ width: '20px',
7
+ height: '20px',
8
+ boxShadow: 'none',
9
+ backgroundColor: '#F5F5F5',
10
+ border: '1px solid #12121233',
11
+ borderRadius: '50%',
12
+ margin: '5.5px 0',
13
+ })
14
+
15
+ export const StyledCircleIcon = styled(CircleRoundedIcon)({
16
+ position: 'relative',
17
+ left: '1px',
18
+ top: '1px',
19
+ color: 'black',
20
+ width: '7px',
21
+ height: '7px',
22
+ })
23
+
24
+ export const StyledAvatar = styled(Avatar)(({ theme }) => ({
25
+ width: 30,
26
+ height: 30,
27
+ marginRight: '8px',
28
+ fontSize: '14px',
29
+ }))
30
+
31
+ export const StyledSpinnerBox = styled(Box)({
32
+ display: 'flex',
33
+ justifyContent: 'center',
34
+ alignItems: 'center',
35
+ })
@@ -0,0 +1 @@
1
+ export { default } from './ActivityLog'
@@ -31,6 +31,7 @@ interface ApplicationProfileProps {
31
31
  | 'hostels'
32
32
  | 'hrms'
33
33
  | 'commute_x'
34
+ | 'admin'
34
35
  title: string
35
36
  permissions?: {
36
37
  add: string
@@ -15,6 +15,7 @@ import {
15
15
  updateCreateUserApplicationProfile,
16
16
  userProfileSchema,
17
17
  } from './services'
18
+ import { useErrorModal } from '../ErrorModalWrapper/ErrorModalWrapper'
18
19
  interface UserProps {
19
20
  options: {
20
21
  label: any
@@ -26,6 +27,7 @@ const getDepartments = () => {
26
27
  return axios.get('/hrms/departments').then((res) => res?.data?.data)
27
28
  }
28
29
  function UserProfileRelation({ close, application, profiles, data }) {
30
+ const errorModal = useErrorModal()
29
31
  const [state, setState] = useImmer<UserProps>({
30
32
  options: [],
31
33
  inputValue: '',
@@ -58,7 +60,8 @@ function UserProfileRelation({ close, application, profiles, data }) {
58
60
  onError: (err) => {
59
61
  // eslint-disable-next-line no-console
60
62
  console.log(err)
61
- axiosErrorToast(err)
63
+ // axiosErrorToast(err)
64
+ errorModal({ error: err })
62
65
  },
63
66
  },
64
67
  )
@@ -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
+ }