@campxdev/shared 1.8.47 → 1.8.49-alpha.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@campxdev/shared",
3
- "version": "1.8.47",
3
+ "version": "1.8.49-alpha.1",
4
4
  "main": "./exports.ts",
5
5
  "scripts": {
6
6
  "start": "react-scripts start",
@@ -7,7 +7,7 @@ import {
7
7
  Typography,
8
8
  styled,
9
9
  } from '@mui/material'
10
- import { useState } from 'react'
10
+ import { useEffect, useState } from 'react'
11
11
  import { useMutation, useQuery } from 'react-query'
12
12
  import { toast } from 'react-toastify'
13
13
  import { useImmer } from 'use-immer'
@@ -34,7 +34,7 @@ import { ValidateAccess } from '../../permissions'
34
34
  import { Permission, PermissionsStore } from '../../shared-state'
35
35
 
36
36
  interface ApplicationProfileProps {
37
- application: 'exams' | 'square' | 'payments' | 'enroll_x'
37
+ application: 'exams' | 'square' | 'payments' | 'enroll_x' | 'hostels'
38
38
  title: string
39
39
  permissions?: {
40
40
  add: string
@@ -80,14 +80,14 @@ function ApplicationProfile({
80
80
  const columns = [
81
81
  {
82
82
  title: 'User',
83
- key: '',
84
- dataIndex: '',
83
+ key: 'user',
84
+ dataIndex: 'user',
85
85
  render: (_, row) => <UserComponent userData={row} />,
86
86
  },
87
87
  {
88
88
  title: 'Profile',
89
- key: '',
90
- dataIndex: '',
89
+ key: 'profile',
90
+ dataIndex: 'profile',
91
91
  render: (_, row) => (
92
92
  <RenderProfileDropDown
93
93
  profiles={profiles?.profiles}
@@ -218,6 +218,12 @@ export const RenderProfileDropDown = ({
218
218
  profileId: data.profiles[0].id,
219
219
  })
220
220
 
221
+ useEffect(() => {
222
+ setState((pre) => ({
223
+ userId: data.id,
224
+ profileId: data.profiles[0].id,
225
+ }))
226
+ }, [data])
221
227
  const { mutate, isLoading } = useMutation(updateUserApplicationProfile, {
222
228
  onSuccess: (res) => {
223
229
  refetchFn()
@@ -1,24 +1,17 @@
1
1
  import { yupResolver } from '@hookform/resolvers/yup'
2
- import {
3
- Autocomplete,
4
- CircularProgress,
5
- Popper,
6
- Stack,
7
- styled,
8
- } from '@mui/material'
9
- import { useState } from 'react'
10
- import { Controller, useForm } from 'react-hook-form'
2
+ import { Popper, Stack, styled } from '@mui/material'
3
+ import { useForm } from 'react-hook-form'
11
4
  import { useMutation, useQueryClient } from 'react-query'
12
5
  import { toast } from 'react-toastify'
6
+ import { useImmer } from 'use-immer'
7
+ import { axiosErrorToast } from '../../config/axios'
8
+ import ActionButton from '../ActionButton'
9
+ import { FormMultiSelect, FormSingleSelect } from '../HookForm'
13
10
  import {
14
11
  createApplicationUserProfile,
15
12
  fetchUsers,
16
13
  userProfileSchema,
17
14
  } from './Service'
18
- import { useImmer } from 'use-immer'
19
- import { axiosErrorToast } from '../../config/axios'
20
- import { FormMultiSelect, FormSingleSelect } from '../HookForm'
21
- import ActionButton from '../ActionButton'
22
15
  interface UserProps {
23
16
  options: {
24
17
  label: any
@@ -80,6 +73,7 @@ function UserProfileRelation({ close, application, profiles }) {
80
73
  }
81
74
 
82
75
  const handleInputChange = (e) => {
76
+ //TODO : debounce
83
77
  if (e) {
84
78
  setState((s) => {
85
79
  s.inputValue = e.target.value
@@ -12,6 +12,7 @@ import LoginForm from '../LoginForm'
12
12
  import { useModal } from '../DrawerWrapper/DrawerWrapper'
13
13
  import { PermissionsStore } from '../../shared-state'
14
14
  import axios from '../../config/axios'
15
+ import { openRootModal } from '../../contexts/RootModal'
15
16
 
16
17
  const StyledAlert = styled(Alert)(({ theme }) => ({
17
18
  height: '60px',
@@ -187,18 +188,16 @@ export function UnAuth({ resetBoundary }) {
187
188
  setLoading(false)
188
189
  })
189
190
  .catch((e) => {
190
- modal({
191
- title: 'Login',
192
- content: () => <LoginForm />,
191
+ openRootModal({
192
+ key: 'login',
193
193
  })
194
194
  })
195
195
  }
196
196
  function LoginPage() {
197
197
  if (isLocalHost) {
198
198
  if (!sessionCookie) {
199
- modal({
200
- title: 'Login',
201
- content: () => <LoginForm />,
199
+ openRootModal({
200
+ key: 'login',
202
201
  })
203
202
  } else {
204
203
  appinit()
@@ -0,0 +1,80 @@
1
+ import { Box, Typography } from '@mui/material'
2
+ import Image from '../Image/Image'
3
+ import axios from '../../config/axios'
4
+ import { useQuery } from 'react-query'
5
+ import Spinner from '../Spinner'
6
+ import { institutions } from './services'
7
+ import { InsititutionsStore } from '../../shared-state/InstitutionsStore'
8
+
9
+ export default function InsititutionsDialog({ close }) {
10
+ // const { data, isLoading } = useQuery(
11
+ // 'institutions',
12
+ // institutions.fetchInsititutions,
13
+ // )
14
+
15
+ // if (isLoading) return <Spinner />
16
+ const { institutions } = InsititutionsStore.useState((s) => s)
17
+
18
+ return (
19
+ <Box
20
+ sx={{
21
+ padding: '20px',
22
+ }}
23
+ >
24
+ <Typography variant="h3" textAlign={'center'}>
25
+ Select an Instituition
26
+ </Typography>
27
+ <Box>
28
+ <Box
29
+ sx={{
30
+ marginTop: '30px',
31
+ display: 'flex',
32
+ gap: '30px',
33
+ justifyContent: 'center',
34
+ alignItems: 'center',
35
+ }}
36
+ >
37
+ {institutions?.map((item, index) => (
38
+ <InstitutionCard institution={item} key={index} />
39
+ ))}
40
+ </Box>
41
+ </Box>
42
+ </Box>
43
+ )
44
+ }
45
+
46
+ const InstitutionCard = ({ institution }) => {
47
+ const urlTenantKey = window.location.pathname.split('/')[1]
48
+ const handleClick = () => {
49
+ localStorage.setItem('institution_key', institution?.code)
50
+ window.location.replace(
51
+ `${window.location.origin}/${urlTenantKey}/${institution?.code}`,
52
+ )
53
+ }
54
+
55
+ return (
56
+ <Box
57
+ sx={{
58
+ display: 'flex',
59
+ flexDirection: 'column',
60
+ alignItems: 'center',
61
+ gap: '20px',
62
+ padding: '14px',
63
+ border: '1px solid black',
64
+ borderRadius: '10px',
65
+ cursor: 'pointer',
66
+ width: '200px',
67
+ flexShrink: 0,
68
+ }}
69
+ onClick={handleClick}
70
+ >
71
+ <Image
72
+ alt="logo"
73
+ height={100}
74
+ width="auto"
75
+ src={institution?.images?.url}
76
+ />
77
+ <Typography variant="body2">{institution?.name}</Typography>
78
+ </Box>
79
+ )
80
+ }
@@ -0,0 +1,32 @@
1
+ import { useState } from 'react'
2
+ import { FormSingleSelect } from '../HookForm'
3
+ import { SingleSelect } from '../Input'
4
+ import { InsititutionsStore } from '../../shared-state/InstitutionsStore'
5
+ import { urlTenantKey } from '../../contexts/Providers'
6
+
7
+ export default function SchoolSwitch() {
8
+ const { current, institutions } = InsititutionsStore.useState((s) => s)
9
+
10
+ const handleChange = (e) => {
11
+ window.location.replace(
12
+ `${window.location.origin}/${urlTenantKey}/${e.target.value}`,
13
+ )
14
+ }
15
+
16
+ return (
17
+ <SingleSelect
18
+ containerProps={{
19
+ sx: {
20
+ minWidth: '200px',
21
+ },
22
+ }}
23
+ size="small"
24
+ options={institutions?.map((item) => ({
25
+ label: item.name,
26
+ value: item.code,
27
+ }))}
28
+ value={current?.code}
29
+ onChange={handleChange}
30
+ />
31
+ )
32
+ }
@@ -0,0 +1 @@
1
+ export { default } from './InsititutionsDialog'
@@ -0,0 +1,12 @@
1
+ import axios from '../../config/axios'
2
+
3
+ export const institutions = {
4
+ async fetchInsititutions() {
5
+ const res = await axios.get('/square/institutions')
6
+ return res.data
7
+ },
8
+ async fetchInsititutionByCode(code: string) {
9
+ const res = await axios.get(`/square/institutions/code/${code}`)
10
+ return res.data
11
+ },
12
+ }
@@ -46,6 +46,7 @@ interface AppHeaderProps {
46
46
  }[]
47
47
  customHeaderActions?: ReactNode
48
48
  cogWheelMenu?: { label: string; path: string }[]
49
+ institutions: any[]
49
50
  }
50
51
 
51
52
  export default function AppHeader({
@@ -54,6 +55,7 @@ export default function AppHeader({
54
55
  userBoxActions = [],
55
56
  cogWheelMenu = [],
56
57
  customHeaderActions,
58
+ institutions,
57
59
  }: AppHeaderProps) {
58
60
  return (
59
61
  <StyledHeader>
@@ -2,6 +2,7 @@ import { Box } from '@mui/material'
2
2
  import UserBox from './UserBox'
3
3
  import CogWheelMenu from './CogWheelMenu'
4
4
  import FreshDeskHelpButton from './FreshDeskHelpButton'
5
+ import InstitutionsDropDown from '../../../Institutions/InstitutionsDropdown'
5
6
 
6
7
  export default function HeaderActions({
7
8
  cogWheelMenu,
@@ -10,6 +11,7 @@ export default function HeaderActions({
10
11
  }) {
11
12
  return (
12
13
  <>
14
+ <InstitutionsDropDown />
13
15
  <FreshDeskHelpButton />
14
16
  {cogWheelMenu?.length ? <CogWheelMenu menu={cogWheelMenu} /> : null}
15
17
  <UserBox fullName={fullName} actions={userBoxActions} />
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-console */
1
2
  import { Visibility, VisibilityOff } from '@mui/icons-material'
2
3
  import {
3
4
  Alert,
@@ -13,20 +14,21 @@ import axiosBase from 'axios'
13
14
  import Cookies from 'js-cookie'
14
15
  import { useEffect, useState } from 'react'
15
16
  import { useForm } from 'react-hook-form'
17
+ import { Link } from 'react-router-dom'
18
+ import axios from '../config/axios'
19
+ import adminAxios from '../utils/adminAxios'
16
20
  import ActionButton from './ActionButton'
17
21
  import { FormTextField } from './HookForm'
18
- import axios from '../config/axios'
19
22
  import ResetPassword from './ResetPassword'
20
- import { Link } from 'react-router-dom'
21
- import { DialogButton } from './ModalButtons'
22
- import adminAxios from '../utils/adminAxios'
23
+ import Image from './Image/Image'
24
+ import { campxLogoPrimary } from '../assets/images'
23
25
 
24
26
  export function LoginForm({
25
27
  loginUrl,
26
- showSuperAdminForm,
28
+ close,
27
29
  }: {
28
30
  loginUrl?: string
29
- showSuperAdminForm?: boolean
31
+ close: any
30
32
  }) {
31
33
  const [showPassword, setShowPassword] = useState(false)
32
34
  const [forgotMail, setForgotMail] = useState(null)
@@ -86,6 +88,19 @@ export function LoginForm({
86
88
 
87
89
  return (
88
90
  <Box sx={{ position: 'relative', height: '100%' }}>
91
+ <Box
92
+ sx={{
93
+ display: 'flex',
94
+ justifyContent: 'center',
95
+ margin: '30px 0',
96
+ flexDirection: 'column',
97
+ gap: '10px',
98
+ alignItems: 'center',
99
+ }}
100
+ >
101
+ <Image alt="" height="auto" width={'200px'} src={campxLogoPrimary} />
102
+ <Typography variant="body2">Developer Login</Typography>
103
+ </Box>
89
104
  {resetPassword ? (
90
105
  <>
91
106
  <ResetPassword />
@@ -168,26 +183,6 @@ export function LoginForm({
168
183
  {error}
169
184
  </Alert>
170
185
  )}
171
-
172
- {showSuperAdminForm && (
173
- <Box
174
- sx={{
175
- position: 'absolute',
176
- bottom: '20px',
177
- right: '20px',
178
- }}
179
- >
180
- <DialogButton
181
- title="Super Admin Login"
182
- anchor={({ open }) => (
183
- <Button onClick={open} size="small" variant="text">
184
- Super Admin Login
185
- </Button>
186
- )}
187
- content={({ close }) => <SuperAdminLoginForm close={close} />}
188
- />
189
- </Box>
190
- )}
191
186
  </Box>
192
187
  )
193
188
  }
@@ -83,7 +83,7 @@ export default function DialogButton({
83
83
 
84
84
  interface CustomDialogProps {
85
85
  content: (props: { close: () => void }) => ReactNode
86
- title: string
86
+ title?: string
87
87
  onClose: () => void
88
88
  open: boolean
89
89
  dialogProps?: Omit<DialogProps, 'open'>
@@ -96,30 +96,34 @@ export const CustomDialog = ({
96
96
  content,
97
97
  open,
98
98
  }: CustomDialogProps) => {
99
+ const props = {
100
+ PaperProps: {
101
+ ...dialogProps?.PaperProps,
102
+ elevation: 2,
103
+ sx: { borderRadius: '10px' },
104
+ },
105
+ fullWidth: true,
106
+ onClose: onClose,
107
+ open: open,
108
+ transitionDuration: 140,
109
+ TransitionComponent: Transition,
110
+ sx: {
111
+ ...dialogProps?.sx,
112
+ '& .MuiBackdrop-root': { backgroundColor: 'rgba(0, 0, 0, 0.4)' },
113
+ },
114
+ ...dialogProps,
115
+ }
116
+
99
117
  return (
100
- <Dialog
101
- PaperProps={{
102
- ...dialogProps?.PaperProps,
103
- elevation: 2,
104
- sx: { borderRadius: '10px' },
105
- }}
106
- fullWidth
107
- onClose={onClose}
108
- open={open}
109
- transitionDuration={140}
110
- TransitionComponent={Transition}
111
- sx={{
112
- ...dialogProps?.sx,
113
- '& .MuiBackdrop-root': { backgroundColor: 'rgba(0, 0, 0, 0.4)' },
114
- }}
115
- {...dialogProps}
116
- >
117
- <StyledDialogHeader>
118
- <DialogTitle>{title}</DialogTitle>
119
- <IconButton onClick={onClose} sx={{ color: 'black' }}>
120
- <Close />
121
- </IconButton>
122
- </StyledDialogHeader>
118
+ <Dialog {...props}>
119
+ {title && (
120
+ <StyledDialogHeader>
121
+ <DialogTitle>{title}</DialogTitle>
122
+ <IconButton onClick={onClose} sx={{ color: 'black' }}>
123
+ <Close />
124
+ </IconButton>
125
+ </StyledDialogHeader>
126
+ )}
123
127
  <StyledDialogContent>{content({ close: onClose })}</StyledDialogContent>
124
128
  </Dialog>
125
129
  )
@@ -1,9 +1,11 @@
1
- import Axios from 'axios'
1
+ import Axios, { AxiosRequestConfig } from 'axios'
2
2
  import _ from 'lodash'
3
3
  import { toast } from 'react-toastify'
4
4
  import Cookies from 'js-cookie'
5
5
  import { NetworkStore } from '../components/ErrorBoundary/GlobalNetworkLoadingIndicator'
6
6
  import { isDevelopment } from '../constants'
7
+ import { UserStore } from '../shared-state'
8
+ import { InsititutionsStore } from '../shared-state/InstitutionsStore'
7
9
 
8
10
  const sessionKey = Cookies.get('campx_session_key')
9
11
  const clientId = window.location.pathname.split('/')[1] ?? 'campx_dev'
@@ -30,7 +32,12 @@ let axios = Axios.create({
30
32
  })
31
33
 
32
34
  axios.interceptors.request.use(
33
- function (config) {
35
+ function (config: AxiosRequestConfig) {
36
+ const { current } = InsititutionsStore.getRawState()
37
+ if (current) {
38
+ config.headers['x-institution-code'] = current.code
39
+ }
40
+
34
41
  const params = formatParams(config?.params)
35
42
  NetworkStore.update((s) => {
36
43
  s.loading = true
@@ -1,6 +1,7 @@
1
1
  import { createContext, useContext } from 'react'
2
2
  import { LoginForm } from '../components'
3
3
  import { useModal } from '../components/DrawerWrapper/DrawerWrapper'
4
+ import { openRootModal } from './RootModal'
4
5
 
5
6
  const LoginContext = createContext<{
6
7
  openLoginForm: (loginUrl: string) => void
@@ -12,11 +13,8 @@ export default function LoginFormProvider({ children }) {
12
13
  const modal = useModal()
13
14
 
14
15
  const onLogin = (loginUrl: string) => {
15
- modal({
16
- title: 'Developer Login',
17
- content({ close }) {
18
- return <LoginForm loginUrl={loginUrl} />
19
- },
16
+ openRootModal({
17
+ key: 'login',
20
18
  })
21
19
  }
22
20
 
@@ -8,40 +8,49 @@ 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 LoginFormProvider from './LoginFormProvider'
12
11
  import FreshChatButton from '../components/Layout/Header/HeaderActions/FreshChatButton'
12
+ import RootModal from './RootModal'
13
13
 
14
14
  export const campxTenantKey = Cookies.get('campx_tenant')
15
15
  export const urlTenantKey = window.location.pathname.split('/')[1]
16
+ export const instituitionKey = window.location.pathname.split('/')[2]
17
+
18
+ export default function Providers({ children }: { children: ReactNode }) {
19
+ const localInstituitionKey = localStorage.getItem('institution_key')
20
+
21
+ const insititutionCode = instituitionKey
22
+ ? instituitionKey
23
+ : localInstituitionKey
24
+ ? localInstituitionKey
25
+ : null
16
26
 
17
- export default function Providers({
18
- children,
19
- showSuperAdminLoginForm = false,
20
- }: {
21
- children: ReactNode
22
- showSuperAdminLoginForm?: boolean
23
- }) {
24
27
  useEffect(() => {
25
28
  if (!urlTenantKey) {
26
- if (campxTenantKey) {
27
- return window.location.replace(
28
- window.location.origin + `/${campxTenantKey}`,
29
- )
29
+ if (campxTenantKey)
30
+ window.location.replace(window.location.origin + `/${campxTenantKey}`)
31
+ } else {
32
+ if (!instituitionKey) {
33
+ if (localInstituitionKey) {
34
+ window.location.replace(
35
+ window.location.origin + `/${urlTenantKey}/${localInstituitionKey}`,
36
+ )
37
+ }
30
38
  }
31
39
  }
32
40
  }, [])
33
41
 
42
+ const baseName = `${urlTenantKey}/${insititutionCode}`
43
+
34
44
  return (
35
- <BrowserRouter basename={urlTenantKey}>
45
+ <BrowserRouter basename={baseName}>
36
46
  <QueryClientProvider>
37
47
  <MuiThemeProvider>
38
48
  <ConfirmContextProvider>
39
49
  <DialogProvider>
40
- <LoginFormProvider>
41
- {children}
42
- <GlobalNetworkLoadingIndicator />
43
- <FreshChatButton />
44
- </LoginFormProvider>
50
+ {children}
51
+ <GlobalNetworkLoadingIndicator />
52
+ <FreshChatButton />
53
+ <RootModal />
45
54
  <ToastContainer />
46
55
  </DialogProvider>
47
56
  </ConfirmContextProvider>
@@ -0,0 +1,73 @@
1
+ import { Store } from 'pullstate'
2
+ import { CustomDialog, LoginForm } from '../components'
3
+ import InsititutionsDialog from '../components/Institutions'
4
+ import { DialogProps } from '@mui/material'
5
+
6
+ const getRootDialogContent = (
7
+ key: string,
8
+ ): (({ close, data }) => JSX.Element) => {
9
+ switch (key) {
10
+ case 'login':
11
+ return ({ close, data }) => (
12
+ <LoginForm loginUrl={data?.loginUrl} close={close} />
13
+ )
14
+ case 'institutions':
15
+ return ({ close }) => <InsititutionsDialog close={close} />
16
+ default:
17
+ return () => <></>
18
+ }
19
+ }
20
+
21
+ const initState = {
22
+ isOpen: false,
23
+ key: '',
24
+ content: ({ close, data }) => <></>,
25
+ contentData: null,
26
+ dialogProps: null,
27
+ }
28
+
29
+ const rootModalStore = new Store(initState)
30
+
31
+ export default function RootModal() {
32
+ const {
33
+ isOpen,
34
+ content: Content,
35
+ contentData,
36
+ dialogProps,
37
+ } = rootModalStore.useState()
38
+
39
+ const onClose = () => {
40
+ rootModalStore.update((s) => {
41
+ s.isOpen = false
42
+ s.key = ''
43
+ s.content = ({ close }) => <></>
44
+ })
45
+ }
46
+
47
+ const dialogContent = () => (
48
+ <Content close={dialogProps?.onClose ?? onClose} data={contentData} />
49
+ )
50
+
51
+ return (
52
+ <CustomDialog
53
+ content={dialogContent}
54
+ onClose={dialogProps?.onClose ?? onClose}
55
+ open={isOpen}
56
+ dialogProps={dialogProps}
57
+ />
58
+ )
59
+ }
60
+
61
+ export const openRootModal = (props: {
62
+ key: string
63
+ contentData?: any
64
+ dialogProps?: Omit<DialogProps, 'open'>
65
+ }) => {
66
+ rootModalStore.update((s) => {
67
+ s.isOpen = true
68
+ s.key = props.key
69
+ s.content = getRootDialogContent(props.key)
70
+ s.contentData = props.contentData
71
+ s.dialogProps = props.dialogProps
72
+ })
73
+ }
@@ -1,10 +1,14 @@
1
1
  import { AxiosError } from 'axios'
2
2
  import { useEffect, useState } from 'react'
3
3
  import { toast } from 'react-toastify'
4
+ import { institutions } from '../components/Institutions/services'
4
5
  import axios from '../config/axios'
5
6
  import { isDevelopment } from '../constants'
6
- import { useLoginForm } from '../contexts/LoginFormProvider'
7
- import { PermissionsStore, AssetsStore, UserStore } from '../shared-state'
7
+ import { urlTenantKey } from '../contexts/Providers'
8
+ import { openRootModal } from '../contexts/RootModal'
9
+ import { AssetsStore, PermissionsStore, UserStore } from '../shared-state'
10
+ import { InsititutionsStore } from '../shared-state/InstitutionsStore'
11
+ import useInstitution from './useInstitution'
8
12
 
9
13
  const url = window.location.origin
10
14
 
@@ -32,8 +36,8 @@ const ApplicationObj = {
32
36
  ums: 'square',
33
37
  payments: 'payments',
34
38
  exams: 'exams',
39
+ hostel: 'hostels',
35
40
  }
36
-
37
41
  const checkIsAdmin = (user) => {
38
42
  let subDomain = window.location.host.split('.')?.slice(-3)[0]
39
43
  const localSubDomain = process.env.REACT_APP_SUBDOMAIN
@@ -62,10 +66,50 @@ const checkIsAdmin = (user) => {
62
66
  return profile ? (profile.isAdmin == true ? 1 : 0) : 0
63
67
  }
64
68
 
69
+ const loginErrorHandler = ({
70
+ loginUrl,
71
+ setLoading,
72
+ err,
73
+ }: {
74
+ loginUrl: string
75
+ setLoading?: any
76
+ err: AxiosError
77
+ }) => {
78
+ // setLoading && setLoading(false)
79
+ const origin = window.location.origin
80
+ const isStaging = origin.split('campx')[1] === '.dev'
81
+
82
+ if (isDevelopment || isStaging) {
83
+ openRootModal({
84
+ key: 'login',
85
+ contentData: {
86
+ loginUrl,
87
+ },
88
+ dialogProps: {
89
+ disableEscapeKeyDown: true,
90
+ onClose: () => {},
91
+ },
92
+ })
93
+ return
94
+ } else {
95
+ window.location.replace(`https://www.id.campx.in/?redirect_to=${url}`)
96
+ }
97
+
98
+ if (err.response.status !== 401) {
99
+ if (err.response.status > 400 && err.response.status < 500) {
100
+ window.location.replace(`https://www.id.campx.in/?redirect_to=${url}`)
101
+ } else {
102
+ toast.error('Server Error')
103
+ }
104
+ }
105
+ }
106
+
65
107
  function useAuth({ permissionsEndpoint, loginUrl }: AuthParams): AuthResponse {
66
- const { openLoginForm } = useLoginForm()
67
108
  const [loading, setLoading] = useState<boolean>(false)
68
109
  const [data, setData] = useState(null)
110
+ const { current } = InsititutionsStore.useState((s) => s)
111
+
112
+ const { loading: loadingInstituition } = useInstitution()
69
113
 
70
114
  const appInit = async () => {
71
115
  setLoading(true)
@@ -116,26 +160,7 @@ function useAuth({ permissionsEndpoint, loginUrl }: AuthParams): AuthResponse {
116
160
  })
117
161
  })
118
162
  .catch((err: AxiosError) => {
119
- setLoading(false)
120
- const origin = window.location.origin
121
- const isStaging = origin.split('campx')[1] === '.dev'
122
-
123
- if (isDevelopment || isStaging) {
124
- openLoginForm(loginUrl)
125
- return
126
- } else {
127
- window.location.replace(`https://www.id.campx.in/?redirect_to=${url}`)
128
- }
129
-
130
- if (err.response.status !== 401) {
131
- if (err.response.status > 400 && err.response.status < 500) {
132
- window.location.replace(
133
- `https://www.id.campx.in/?redirect_to=${url}`,
134
- )
135
- } else {
136
- toast.error('Server Error')
137
- }
138
- }
163
+ loginErrorHandler({ loginUrl, setLoading, err })
139
164
  })
140
165
  }
141
166
 
@@ -144,7 +169,7 @@ function useAuth({ permissionsEndpoint, loginUrl }: AuthParams): AuthResponse {
144
169
  }, [])
145
170
 
146
171
  return {
147
- loading: loading || !data?.permissions,
172
+ loading: loading || !data?.permissions || !current || loadingInstituition,
148
173
  data,
149
174
  }
150
175
  }
@@ -0,0 +1,94 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { institutions } from '../components/Institutions/services'
3
+ import { urlTenantKey } from '../contexts/Providers'
4
+ import { openRootModal } from '../contexts/RootModal'
5
+ import { InsititutionsStore } from '../shared-state/InstitutionsStore'
6
+
7
+ const getInstitutionKey = () => {
8
+ const instituitionKey = window.location.pathname.split('/')[2]
9
+ // if (!instituitionKey) {
10
+ // const localInstituitionKey = localStorage.getItem('institution_key')
11
+ // if (localInstituitionKey) {
12
+ // return localInstituitionKey
13
+ // } else {
14
+ // return null
15
+ // }
16
+ // } else {
17
+ // return instituitionKey
18
+ // }
19
+ return instituitionKey
20
+ }
21
+
22
+ const getInstitutions = async (startLoading, stopLoading) => {
23
+ const insititutionKey = getInstitutionKey()
24
+ try {
25
+ startLoading()
26
+ const data = await institutions.fetchInsititutions()
27
+ InsititutionsStore.update((s) => {
28
+ s.institutions = data?.institutions
29
+ })
30
+ if (data?.institutions?.length === 1 && !insititutionKey) {
31
+ window.location.replace(
32
+ `${window.location.origin}/${urlTenantKey}/${data?.institutions[0]?.code}`,
33
+ )
34
+ }
35
+ if (data?.institutions?.length > 1 && !insititutionKey) {
36
+ openRootModal({
37
+ key: 'institutions',
38
+ dialogProps: {
39
+ disableEscapeKeyDown: true,
40
+ onClose: () => {},
41
+ },
42
+ })
43
+ }
44
+ stopLoading()
45
+ } catch (error) {
46
+ stopLoading()
47
+ }
48
+ }
49
+
50
+ const getInstitutionsByCode = async (code, startLoading, stopLoading) => {
51
+ try {
52
+ startLoading()
53
+ const data = await institutions.fetchInsititutionByCode(code)
54
+ InsititutionsStore.update((s) => {
55
+ s.current = data
56
+ })
57
+ stopLoading()
58
+ } catch (error) {
59
+ stopLoading()
60
+ openRootModal({
61
+ key: 'institutions',
62
+ dialogProps: {
63
+ disableEscapeKeyDown: true,
64
+ onClose: () => {},
65
+ },
66
+ })
67
+ }
68
+ }
69
+
70
+ export default function useInstitution() {
71
+ const insititutionKey = getInstitutionKey()
72
+ const [loading, setLoading] = useState(false)
73
+
74
+ const startLoading = () => {
75
+ setLoading(true)
76
+ }
77
+ const stopLoading = () => {
78
+ setLoading(false)
79
+ }
80
+
81
+ useEffect(() => {
82
+ getInstitutions(startLoading, stopLoading)
83
+ }, [insititutionKey])
84
+
85
+ useEffect(() => {
86
+ if (insititutionKey) {
87
+ getInstitutionsByCode(insititutionKey, startLoading, stopLoading)
88
+ }
89
+ }, [insititutionKey])
90
+
91
+ return {
92
+ loading: loading,
93
+ }
94
+ }
@@ -0,0 +1,8 @@
1
+ import { Store } from 'pullstate'
2
+
3
+ export const InsititutionsStore = new Store({
4
+ institutions: [],
5
+ loading: false,
6
+ error: null,
7
+ current: null,
8
+ })
@@ -68,6 +68,13 @@ export enum PaymentsPermission {
68
68
  }
69
69
  export enum HostelsPermission {
70
70
  //Hostels
71
+
72
+ // manage hostels profile_permissions
73
+ CAN_MANAGE_HOSTELS_PROFILE_PERMISSIONS_VIEW = 'manage_hostels_profile_permissions_view',
74
+ CAN_MANAGE_HOSTELS_PROFILE_PERMISSIONS_ADD = 'can_manage_hostels_profile_permissions_add',
75
+ CAN_MANAGE_HOSTELS_PROFILE_PERMISSIONS_EDIT = 'can_manage_hostels_profile_permissions_edit',
76
+ CAN_MANAGE_HOSTELS_PROFILE_PERMISSIONS_DELETE = 'can_manage_hostels_profile_permissions_delete',
77
+
71
78
  // Hosteler
72
79
  CAN_HOSTELER_DASHBOARD_VIEW = 'can_hosteler_dashboard_view',
73
80
  CAN_HOSTELER_VIEW = 'can_hosteler_view',
@@ -79,25 +86,55 @@ export enum HostelsPermission {
79
86
  CAN_HOSTELER_REPORTS_VIEW = 'can_hosteler_reports_view',
80
87
 
81
88
  // Hosteler Settings
82
- CAN_HOSTELER_SETTINGS_VIEW_CONFIGURATION = 'can_hosteler_settings_view_configuration',
83
- CAN_HOSTELER_SETTINGS_EDIT_CONFIGURATION = 'can_hosteler_settings_edit_configuration',
84
- CAN_HOSTELER_SETTINGS_ADD_FEE_TYPES = 'can_hosteler_settings_add_fee_types',
85
- CAN_HOSTELER_SETTINGS_VIEW_FEE_TYPES = 'can_hosteler_settings_view_fee_types',
86
- CAN_HOSTELER_SETTINGS_EDIT_FEE_TYPES = 'can_hosteler_settings_edit_fee_types',
87
- CAN_HOSTELER_SETTINGS_DELETE_FEE_TYPES = 'can_hosteler_settings_delete_fee_types',
88
- CAN_HOSTELERS_SETTINGS_BLOCK_VIEW = 'can_hosteler_settings_blocks_view',
89
- CAN_HOSTELERS_SETTINGS_BLOCK_ADD = 'can_hosteler_settings_blocks_add',
90
- CAN_HOSTELERS_SETTINGS_BLOCK_EDIT = 'can_hosteler_settings_blocks_edit',
91
- CAN_HOSTELERS_SETTINGS_BLOCK_DELETE = 'can_hosteler_settings_blocks_delete',
92
- CAN_HOSTELERS_SETTINGS_ROOMS_VIEW = 'can_hosteler_settings_rooms_view',
93
- CAN_HOSTELERS_SETTINGS_ROOMS_ADD = 'can_hosteler_settings_rooms_add',
94
- CAN_HOSTELERS_SETTINGS_ROOMS_EDIT = 'can_hosteler_settings_rooms_edit',
95
- CAN_HOSTELERS_SETTINGS_ROOMS_DELETE = 'can_hosteler_settings_rooms_delete',
96
- CAN_HOSTELERS_SETTINGS_ROOMS_IMPORT = 'can_hosteler_settings_import_hostel_rooms',
97
- CAN_HOSTELERS_SETTINGS_AMENITIES_VIEW = 'can_hosteler_settings_amenities_view',
98
- CAN_HOSTELERS_SETTINGS_AMENITIES_ADD = 'can_hosteler_settings_amenities_add',
99
- CAN_HOSTELERS_SETTINGS_AMENITIES_EDIT = 'can_hosteler_settings_amenities_edit',
100
- CAN_HOSTELERS_SETTINGS_AMENITIES_DELETE = 'can_hosteler_settings_amenities_delete',
89
+ CAN_HOSTEL_DASHBOARD_VIEW = 'can_hostel_dashboard_view',
90
+ CAN_HOSTEL_REPORTS_VIEW = 'can_hostel_reports_view',
91
+ CAN_HOSTEL_FEE_PAYMENTS_VIEW = 'can_hostel_fee_payments_view',
92
+
93
+ // Hostel Fee types
94
+ CAN_HOSTEL_FEE_TYPES_ADD = 'can_hostel_fee_types_add',
95
+ CAN_HOSTEL_FEE_TYPES_VIEW = 'can_hostel_fee_types_view',
96
+ CAN_HOSTEL_FEE_TYPES_EDIT = 'can_hostel_fee_types_edit',
97
+ CAN_HOSTEL_FEE_TYPES_DELETE = 'can_hostel_fee_types_delete',
98
+
99
+ // Hostel Blocks
100
+ CAN_HOSTEL_BLOCK_VIEW = 'can_hostel_blocks_view',
101
+ CAN_HOSTEL_BLOCK_ADD = 'can_hostel_blocks_add',
102
+ CAN_HOSTEL_BLOCK_EDIT = 'can_hostel_blocks_edit',
103
+ CAN_HOSTEL_BLOCK_DELETE = 'can_hostel_blocks_delete',
104
+
105
+ // Hostel Rooms
106
+ CAN_HOSTEL_ROOMS_VIEW = 'can_hostel_rooms_view',
107
+ CAN_HOSTEL_ROOMS_ADD = 'can_hostel_rooms_add',
108
+ CAN_HOSTEL_ROOMS_EDIT = 'can_hostel_rooms_edit',
109
+ CAN_HOSTEL_ROOMS_DELETE = 'can_hostel_rooms_delete',
110
+ CAN_HOSTEL_ROOMS_IMPORT = 'can_hostel_rooms_import',
111
+
112
+ // Hostel Amenitites
113
+ CAN_HOSTEL_AMENITIES_VIEW = 'can_hostel_amenities_view',
114
+ CAN_HOSTEL_AMENITIES_ADD = 'can_hostel_amenities_add',
115
+ CAN_HOSTEL_AMENITIES_EDIT = 'can_hostel_amenities_edit',
116
+ CAN_HOSTEL_AMENITIES_DELETE = 'can_hostel_amenities_delete',
117
+
118
+ // Hostel Ticketing Group
119
+ CAN_HOSTEL_TICKET_GROUP_VIEW = 'can_hostel_ticket_groups_view',
120
+ CAN_HOSTEL_TICKET_GROUP_ADD = 'can_hostel_ticket_groups_add',
121
+ CAN_HOSTEL_TICKET_GROUP_EDIT = 'can_hostel_ticket_groups_edit',
122
+ CAN_HOSTEL_TICKET_GROUP_DELETE = 'can_hostel_ticket_groups_delete',
123
+
124
+ CAN_HOSTEL_TICKET_GROUP_SERVICE_VIEW = 'can_hostel_ticket_group_service_view',
125
+ CAN_HOSTEL_TICKET_GROUP_SERVICE_EDIT = 'can_hostel_ticket_group_service_edit',
126
+ CAN_HOSTEL_TICKET_GROUP_SERVICE_DELETE = 'can_hostel_ticket_group_service_delete',
127
+ CAN_HOSTEL_TICKET_GROUP_SERVICE_ADD = 'can_hostel_ticket_group_service_add',
128
+
129
+ CAN_HOSTEL_TICKET_GROUP_USERS_VIEW = 'can_hostel_ticket_group_users_view',
130
+ CAN_HOSTEL_TICKET_GROUP_USERS_DELETE = 'can_hostel_ticket_group_users_delete',
131
+ CAN_HOSTEL_TICKET_GROUP_USERS_ADD = 'can_hostel_ticket_group_users_add',
132
+
133
+ // Hostel Tickets
134
+ CAN_HOSTEL_TICKETS_VIEW = 'can_hostel_tickets_view',
135
+ CAN_HOSTEL_TICKETS_EDIT = 'can_hostel_tickets_edit',
136
+ CAN_HOSTEL_TICKETS_DELETE = 'can_hostel_tickets_delete',
137
+ CAN_HOSTEL_TICKETS_ADD = 'can_hostel_tickets_add',
101
138
  }
102
139
 
103
140
  export enum SquarePermissions {
@@ -554,6 +591,13 @@ export enum Permission {
554
591
  CAN_MANAGE_PAYMENTS_SETTINGS = 'can_payments_manage_settings',
555
592
 
556
593
  //Hostels
594
+
595
+ // manage hostels profile_permissions
596
+ CAN_MANAGE_HOSTELS_PROFILE_PERMISSIONS_VIEW = 'can_manage_hostels_profile_permissions_view',
597
+ CAN_MANAGE_HOSTELS_PROFILE_PERMISSIONS_ADD = 'can_manage_hostels_profile_permissions_add',
598
+ CAN_MANAGE_HOSTELS_PROFILE_PERMISSIONS_EDIT = 'can_manage_hostels_profile_permissions_edit',
599
+ CAN_MANAGE_HOSTELS_PROFILE_PERMISSIONS_DELETE = 'can_manage_hostels_profile_permissions_delete',
600
+
557
601
  // Hosteler
558
602
  CAN_HOSTELER_DASHBOARD_VIEW = 'can_hosteler_dashboard_view',
559
603
  CAN_HOSTELER_VIEW = 'can_hosteler_view',
@@ -565,25 +609,55 @@ export enum Permission {
565
609
  CAN_HOSTELER_REPORTS_VIEW = 'can_hosteler_reports_view',
566
610
 
567
611
  // Hosteler Settings
568
- CAN_HOSTELER_SETTINGS_VIEW_CONFIGURATION = 'can_hosteler_settings_view_configuration',
569
- CAN_HOSTELER_SETTINGS_EDIT_CONFIGURATION = 'can_hosteler_settings_edit_configuration',
570
- CAN_HOSTELER_SETTINGS_ADD_FEE_TYPES = 'can_hosteler_settings_add_fee_types',
571
- CAN_HOSTELER_SETTINGS_VIEW_FEE_TYPES = 'can_hosteler_settings_view_fee_types',
572
- CAN_HOSTELER_SETTINGS_EDIT_FEE_TYPES = 'can_hosteler_settings_edit_fee_types',
573
- CAN_HOSTELER_SETTINGS_DELETE_FEE_TYPES = 'can_hosteler_settings_delete_fee_types',
574
- CAN_HOSTELERS_SETTINGS_BLOCK_VIEW = 'can_hosteler_settings_blocks_view',
575
- CAN_HOSTELERS_SETTINGS_BLOCK_ADD = 'can_hosteler_settings_blocks_add',
576
- CAN_HOSTELERS_SETTINGS_BLOCK_EDIT = 'can_hosteler_settings_blocks_edit',
577
- CAN_HOSTELERS_SETTINGS_BLOCK_DELETE = 'can_hosteler_settings_blocks_delete',
578
- CAN_HOSTELERS_SETTINGS_ROOMS_VIEW = 'can_hosteler_settings_rooms_view',
579
- CAN_HOSTELERS_SETTINGS_ROOMS_ADD = 'can_hosteler_settings_rooms_add',
580
- CAN_HOSTELERS_SETTINGS_ROOMS_EDIT = 'can_hosteler_settings_rooms_edit',
581
- CAN_HOSTELERS_SETTINGS_ROOMS_DELETE = 'can_hosteler_settings_rooms_delete',
582
- CAN_HOSTELERS_SETTINGS_ROOMS_IMPORT = 'can_hosteler_settings_import_hostel_rooms',
583
- CAN_HOSTELERS_SETTINGS_AMENITIES_VIEW = 'can_hosteler_settings_amenities_view',
584
- CAN_HOSTELERS_SETTINGS_AMENITIES_ADD = 'can_hosteler_settings_amenities_add',
585
- CAN_HOSTELERS_SETTINGS_AMENITIES_EDIT = 'can_hosteler_settings_amenities_edit',
586
- CAN_HOSTELERS_SETTINGS_AMENITIES_DELETE = 'can_hosteler_settings_amenities_delete',
612
+ CAN_HOSTEL_DASHBOARD_VIEW = 'can_hostel_dashboard_view',
613
+ CAN_HOSTEL_REPORTS_VIEW = 'can_hostel_reports_view',
614
+ CAN_HOSTEL_FEE_PAYMENTS_VIEW = 'can_hostel_fee_payments_view',
615
+
616
+ // Hostel Fee types
617
+ CAN_HOSTEL_FEE_TYPES_ADD = 'can_hostel_fee_types_add',
618
+ CAN_HOSTEL_FEE_TYPES_VIEW = 'can_hostel_fee_types_view',
619
+ CAN_HOSTEL_FEE_TYPES_EDIT = 'can_hostel_fee_types_edit',
620
+ CAN_HOSTEL_FEE_TYPES_DELETE = 'can_hostel_fee_types_delete',
621
+
622
+ // Hostel Blocks
623
+ CAN_HOSTEL_BLOCK_VIEW = 'can_hostel_blocks_view',
624
+ CAN_HOSTEL_BLOCK_ADD = 'can_hostel_blocks_add',
625
+ CAN_HOSTEL_BLOCK_EDIT = 'can_hostel_blocks_edit',
626
+ CAN_HOSTEL_BLOCK_DELETE = 'can_hostel_blocks_delete',
627
+
628
+ // Hostel Rooms
629
+ CAN_HOSTEL_ROOMS_VIEW = 'can_hostel_rooms_view',
630
+ CAN_HOSTEL_ROOMS_ADD = 'can_hostel_rooms_add',
631
+ CAN_HOSTEL_ROOMS_EDIT = 'can_hostel_rooms_edit',
632
+ CAN_HOSTEL_ROOMS_DELETE = 'can_hostel_rooms_delete',
633
+ CAN_HOSTEL_ROOMS_IMPORT = 'can_hostel_rooms_import',
634
+
635
+ // Hostel Amenitites
636
+ CAN_HOSTEL_AMENITIES_VIEW = 'can_hostel_amenities_view',
637
+ CAN_HOSTEL_AMENITIES_ADD = 'can_hostel_amenities_add',
638
+ CAN_HOSTEL_AMENITIES_EDIT = 'can_hostel_amenities_edit',
639
+ CAN_HOSTEL_AMENITIES_DELETE = 'can_hostel_amenities_delete',
640
+
641
+ // Hostel Ticketing Group
642
+ CAN_HOSTEL_TICKET_GROUP_VIEW = 'can_hostel_ticket_groups_view',
643
+ CAN_HOSTEL_TICKET_GROUP_ADD = 'can_hostel_ticket_groups_add',
644
+ CAN_HOSTEL_TICKET_GROUP_EDIT = 'can_hostel_ticket_groups_edit',
645
+ CAN_HOSTEL_TICKET_GROUP_DELETE = 'can_hostel_ticket_groups_delete',
646
+
647
+ CAN_HOSTEL_TICKET_GROUP_SERVICE_VIEW = 'can_hostel_ticket_group_service_view',
648
+ CAN_HOSTEL_TICKET_GROUP_SERVICE_EDIT = 'can_hostel_ticket_group_service_edit',
649
+ CAN_HOSTEL_TICKET_GROUP_SERVICE_DELETE = 'can_hostel_ticket_group_service_delete',
650
+ CAN_HOSTEL_TICKET_GROUP_SERVICE_ADD = 'can_hostel_ticket_group_service_add',
651
+
652
+ CAN_HOSTEL_TICKET_GROUP_USERS_VIEW = 'can_hostel_ticket_group_users_view',
653
+ CAN_HOSTEL_TICKET_GROUP_USERS_DELETE = 'can_hostel_ticket_group_users_delete',
654
+ CAN_HOSTEL_TICKET_GROUP_USERS_ADD = 'can_hostel_ticket_group_users_add',
655
+
656
+ // Hostel Tickets
657
+ CAN_HOSTEL_TICKETS_VIEW = 'can_hostel_tickets_view',
658
+ CAN_HOSTEL_TICKETS_EDIT = 'can_hostel_tickets_edit',
659
+ CAN_HOSTEL_TICKETS_DELETE = 'can_hostel_tickets_delete',
660
+ CAN_HOSTEL_TICKETS_ADD = 'can_hostel_tickets_add',
587
661
 
588
662
  // Square
589
663
  // manage