@campxdev/shared 1.8.4 → 1.8.5-0.alpha-2

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 (37) hide show
  1. package/package.json +1 -1
  2. package/src/components/ApplicationProfile/ApplicationProfile.tsx +342 -0
  3. package/src/components/ApplicationProfile/Service.ts +68 -0
  4. package/src/components/ApplicationProfile/UserProfileRelation.tsx +174 -0
  5. package/src/components/ApplicationProfile/index.tsx +1 -0
  6. package/src/components/DropDownButton/DropdownMenuItem.tsx +12 -0
  7. package/src/components/ErrorBoundary/ErrorBoundary.tsx +8 -3
  8. package/src/components/ErrorBoundary/ErrorFallback.tsx +7 -2
  9. package/src/components/FloatingContainer.tsx +1 -1
  10. package/src/components/HookForm/MultiSelect.tsx +8 -0
  11. package/src/components/Input/DatePicker.tsx +11 -0
  12. package/src/components/Input/MultiSelect.tsx +11 -0
  13. package/src/components/Layout/Header/AppHeader.tsx +2 -1
  14. package/src/components/Layout/Header/AppsMenu.tsx +51 -23
  15. package/src/components/Layout/Header/HeaderActions/FreshChatButton.tsx +61 -0
  16. package/src/components/Layout/Header/HeaderActions/FreshDeskHelpButton.tsx +36 -7
  17. package/src/components/Layout/Header/HeaderActions/HeaderActions.tsx +2 -0
  18. package/src/components/Layout/Header/SchoolSwitch/SchoolSwitch.tsx +33 -0
  19. package/src/components/Layout/Header/applications.ts +8 -9
  20. package/src/components/Layout/Header/assets/chat_with_us.png +0 -0
  21. package/src/components/Layout/Header/assets/index.ts +2 -0
  22. package/src/components/Layout/Helmet.tsx +91 -32
  23. package/src/components/ModalButtons/PopoverButton.tsx +99 -0
  24. package/src/components/Tabs/TabsContainer.tsx +3 -0
  25. package/src/components/index.ts +4 -0
  26. package/src/config/axios.ts +8 -2
  27. package/src/constants/UIConstants.ts +27 -0
  28. package/src/contexts/LoginFormProvider.tsx +2 -7
  29. package/src/contexts/Providers.tsx +3 -1
  30. package/src/hooks/index.ts +1 -0
  31. package/src/hooks/useAuth.ts +58 -2
  32. package/src/hooks/useExternalScript.ts +38 -0
  33. package/src/hooks/useFilters.ts +6 -3
  34. package/src/shared-state/PermissionsStore.ts +937 -130
  35. package/src/theme/muiTheme.ts +6 -4
  36. package/src/theme/theme.d.ts +2 -0
  37. package/tsconfig.json +19 -19
@@ -0,0 +1,99 @@
1
+ import { Grow, Popover, PopoverProps } from '@mui/material'
2
+ import { TransitionProps } from '@mui/material/transitions'
3
+ import { ReactNode, forwardRef, useState } from 'react'
4
+
5
+ export const Transition = forwardRef(function Transition(
6
+ props: TransitionProps & {
7
+ children: React.ReactElement
8
+ },
9
+ ref: React.Ref<unknown>,
10
+ ) {
11
+ return <Grow timeout={1000} ref={ref} {...props} />
12
+ })
13
+
14
+ interface PopoverButtonProps {
15
+ anchor: (props: { open: (e: any) => void }) => ReactNode
16
+ content: (props: { close: () => void }) => ReactNode
17
+ popoverProps?: Omit<PopoverProps, 'open'>
18
+ onDialogClose?: () => void
19
+ }
20
+
21
+ export default function PopoverButton({
22
+ content,
23
+ popoverProps,
24
+ onDialogClose,
25
+ anchor,
26
+ }: PopoverButtonProps) {
27
+ const [popperAnchor, setPopperAnchor] = useState(null)
28
+
29
+ const onClose = () => {
30
+ onDialogClose && onDialogClose()
31
+ setPopperAnchor(null)
32
+ }
33
+
34
+ const onOpen = (e) => {
35
+ setPopperAnchor(e.currentTarget)
36
+ }
37
+
38
+ return (
39
+ <>
40
+ {anchor({
41
+ open: onOpen,
42
+ })}
43
+ <CustomPopover
44
+ open={Boolean(popperAnchor)}
45
+ content={content}
46
+ popoverProps={popoverProps}
47
+ onClose={onClose}
48
+ popperAnchor={popperAnchor}
49
+ />
50
+ </>
51
+ )
52
+ }
53
+
54
+ interface CustomDialogProps {
55
+ content: (props: { close: () => void }) => ReactNode
56
+ onClose: () => void
57
+ open: boolean
58
+ popoverProps?: Omit<PopoverProps, 'open'>
59
+ popperAnchor: any
60
+ }
61
+
62
+ export const CustomPopover = ({
63
+ onClose,
64
+ popoverProps,
65
+ content,
66
+ open,
67
+ popperAnchor,
68
+ }: CustomDialogProps) => {
69
+ return (
70
+ <>
71
+ <Popover
72
+ onClose={onClose}
73
+ open={open}
74
+ transitionDuration={140}
75
+ TransitionComponent={Transition}
76
+ PaperProps={{
77
+ ...popoverProps?.PaperProps,
78
+ elevation: 2,
79
+ sx: {
80
+ borderRadius: '10px',
81
+ border: '1px solid #1212121A',
82
+ boxShadow: '0px 4px 16px #0000000F',
83
+ },
84
+ }}
85
+ anchorEl={popperAnchor}
86
+ anchorOrigin={{
87
+ vertical: 'bottom',
88
+ horizontal: 'left',
89
+ }}
90
+ sx={{
91
+ ...popoverProps?.sx,
92
+ }}
93
+ {...popoverProps}
94
+ >
95
+ {content({ close: onClose })}
96
+ </Popover>
97
+ </>
98
+ )
99
+ }
@@ -10,17 +10,20 @@ export interface TabsContainerProps {
10
10
  }[]
11
11
  size?: 'small' | 'medium'
12
12
  conatinerVariant?: 'box' | 'page'
13
+ onTabChange?: (tabKey: string) => void
13
14
  }
14
15
 
15
16
  export default function TabsContainer({
16
17
  tabs,
17
18
  size = 'small',
18
19
  conatinerVariant = 'box',
20
+ onTabChange,
19
21
  }: TabsContainerProps) {
20
22
  const [currentTab, setCurrentTab] = useState(tabs[0].key)
21
23
 
22
24
  const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => {
23
25
  setCurrentTab(value)
26
+ onTabChange && onTabChange(value)
24
27
  }
25
28
  useEffect(() => {
26
29
  setCurrentTab(tabs[0].key)
@@ -35,6 +35,8 @@ import { CustomDialog } from './ModalButtons/DialogButton'
35
35
  import { CustomDrawer } from './ModalButtons/DrawerButton'
36
36
  import ExcelJsonUpload from './ExcelToJsonInput/ExcelJsonUpload'
37
37
  import ExcelToJsonInput from './ExcelToJsonInput'
38
+ import ApplicationProfile from './ApplicationProfile'
39
+ import PopoverButton from './ModalButtons/PopoverButton'
38
40
  export { default as Image } from './Image'
39
41
  export { default as PageHeader } from './PageHeader'
40
42
  export { PageContent } from './PageContent'
@@ -98,6 +100,8 @@ export {
98
100
  UserBox,
99
101
  ExcelJsonUpload,
100
102
  ExcelToJsonInput,
103
+ ApplicationProfile,
104
+ PopoverButton,
101
105
  }
102
106
 
103
107
  export * from './UploadButton/types'
@@ -1,9 +1,10 @@
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'
7
8
 
8
9
  const sessionKey = Cookies.get('campx_session_key')
9
10
  const clientId = window.location.pathname.split('/')[1] ?? 'campx_dev'
@@ -30,7 +31,12 @@ let axios = Axios.create({
30
31
  })
31
32
 
32
33
  axios.interceptors.request.use(
33
- function (config) {
34
+ function (config: AxiosRequestConfig) {
35
+ const userState = UserStore.getRawState()
36
+ if (userState?.username) {
37
+ config.headers.test = userState.username
38
+ }
39
+
34
40
  const params = formatParams(config?.params)
35
41
  NetworkStore.update((s) => {
36
42
  s.loading = true
@@ -95,3 +95,30 @@ export function getRandomColor(name) {
95
95
  character: firstAlphabet?.toUpperCase(),
96
96
  }
97
97
  }
98
+
99
+ export const ProfileApplicationType = [
100
+ {
101
+ label: 'Square',
102
+ value: 'square',
103
+ },
104
+ {
105
+ label: 'Exams',
106
+ value: 'exams',
107
+ },
108
+ {
109
+ label: 'Payments',
110
+ value: 'payments',
111
+ },
112
+ {
113
+ label: 'EnrollX',
114
+ value: 'enroll_x',
115
+ },
116
+ {
117
+ label: 'Hostels',
118
+ value: 'hostels',
119
+ },
120
+ {
121
+ label: 'Commute',
122
+ value: 'commute',
123
+ },
124
+ ]
@@ -8,19 +8,14 @@ const LoginContext = createContext<{
8
8
  openLoginForm: (loginUrl: string) => {},
9
9
  })
10
10
 
11
- export default function LoginFormProvider({ children, showSuperAdminForm }) {
11
+ export default function LoginFormProvider({ children }) {
12
12
  const modal = useModal()
13
13
 
14
14
  const onLogin = (loginUrl: string) => {
15
15
  modal({
16
16
  title: 'Developer Login',
17
17
  content({ close }) {
18
- return (
19
- <LoginForm
20
- loginUrl={loginUrl}
21
- showSuperAdminForm={showSuperAdminForm}
22
- />
23
- )
18
+ return <LoginForm loginUrl={loginUrl} />
24
19
  },
25
20
  })
26
21
  }
@@ -9,6 +9,7 @@ import { ToastContainer } from '../components'
9
9
  import DialogProvider from '../components/DrawerWrapper/DrawerWrapper'
10
10
  import GlobalNetworkLoadingIndicator from '../components/ErrorBoundary/GlobalNetworkLoadingIndicator'
11
11
  import LoginFormProvider from './LoginFormProvider'
12
+ import FreshChatButton from '../components/Layout/Header/HeaderActions/FreshChatButton'
12
13
 
13
14
  export const campxTenantKey = Cookies.get('campx_tenant')
14
15
  export const urlTenantKey = window.location.pathname.split('/')[1]
@@ -36,9 +37,10 @@ export default function Providers({
36
37
  <MuiThemeProvider>
37
38
  <ConfirmContextProvider>
38
39
  <DialogProvider>
39
- <LoginFormProvider showSuperAdminForm={showSuperAdminLoginForm}>
40
+ <LoginFormProvider>
40
41
  {children}
41
42
  <GlobalNetworkLoadingIndicator />
43
+ <FreshChatButton />
42
44
  </LoginFormProvider>
43
45
  <ToastContainer />
44
46
  </DialogProvider>
@@ -2,3 +2,4 @@ export { default as useFetch } from './useFetch'
2
2
  export * from './useRouter'
3
3
  export { default as useAuth } from './useAuth'
4
4
  export { default as useFilter } from './useFilters'
5
+ export { default as useExternalScript } from './useExternalScript'
@@ -4,7 +4,6 @@ import { toast } from 'react-toastify'
4
4
  import axios from '../config/axios'
5
5
  import { isDevelopment } from '../constants'
6
6
  import { useLoginForm } from '../contexts/LoginFormProvider'
7
- import { campxTenantKey, urlTenantKey } from '../contexts/Providers'
8
7
  import { PermissionsStore, AssetsStore, UserStore } from '../shared-state'
9
8
 
10
9
  const url = window.location.origin
@@ -28,6 +27,41 @@ type AuthResponse = {
28
27
  }
29
28
  }
30
29
 
30
+ const ApplicationObj = {
31
+ enroll: 'enroll_x',
32
+ ums: 'square',
33
+ payments: 'payments',
34
+ exams: 'exams',
35
+ }
36
+
37
+ const checkIsAdmin = (user) => {
38
+ let subDomain = window.location.host.split('.')?.slice(-3)[0]
39
+ const localSubDomain = process.env.REACT_APP_SUBDOMAIN
40
+
41
+ if (user?.isSuperuser) return 1
42
+
43
+ if (process.env.NODE_ENV === 'development') {
44
+ subDomain = localSubDomain
45
+
46
+ if (!localSubDomain) {
47
+ toast.warn('missing REACT_APP_SUBDOMAIN in .env')
48
+ }
49
+ }
50
+
51
+ // eslint-disable-next-line no-console
52
+ console.log(
53
+ 'Current App ->',
54
+ ApplicationObj[subDomain],
55
+ '; subdomain env ->',
56
+ subDomain,
57
+ )
58
+
59
+ const profile = user?.profiles?.find(
60
+ (item) => item.application == ApplicationObj[subDomain],
61
+ )
62
+ return profile ? (profile.isAdmin == true ? 1 : 0) : 0
63
+ }
64
+
31
65
  function useAuth({ permissionsEndpoint, loginUrl }: AuthParams): AuthResponse {
32
66
  const { openLoginForm } = useLoginForm()
33
67
  const [loading, setLoading] = useState<boolean>(false)
@@ -38,13 +72,32 @@ function useAuth({ permissionsEndpoint, loginUrl }: AuthParams): AuthResponse {
38
72
  axios
39
73
  .get(permissionsEndpoint)
40
74
  .then((res) => {
75
+ const origin = window.location.origin
76
+ const originSubdomain =
77
+ window.location.host.split('.')?.slice(-3)[0] ?? 'ums'
78
+
79
+ const isStaging = origin.split('campx')[1] === '.dev'
80
+
81
+ if (isDevelopment == false && isStaging == false) {
82
+ if (
83
+ !res.data.applications.includes(ApplicationObj[originSubdomain])
84
+ ) {
85
+ window.location.replace(
86
+ `https://www.id.campx.in/apps?redirect_to=${url}`,
87
+ )
88
+ }
89
+ }
90
+
41
91
  setLoading(false)
42
92
  setData(res.data)
43
93
  UserStore.update((s) => {
44
- s.username = res.data?.username
94
+ s.username = res.data?.user?.username
45
95
  s.user = res.data?.user
46
96
  s.roles = res.data?.roles
47
97
  })
98
+ const isAdmin = checkIsAdmin(res.data.user)
99
+ // eslint-disable-next-line no-console
100
+ console.log('Is Admin -> ', isAdmin)
48
101
 
49
102
  PermissionsStore.update((s) => {
50
103
  s.permissions = {
@@ -52,7 +105,10 @@ function useAuth({ permissionsEndpoint, loginUrl }: AuthParams): AuthResponse {
52
105
  can_settings_view: 1,
53
106
  can_dashboard_view: 1,
54
107
  can_individual_pages_view: 1,
108
+ can_analatics_view: isAdmin,
109
+ can_admin_view: isAdmin,
55
110
  } as any
111
+ s.applications = res.data?.applications ?? []
56
112
  })
57
113
  AssetsStore.update((s) => {
58
114
  s.logo = res.data?.assets.logo
@@ -0,0 +1,38 @@
1
+ import { useEffect, useState } from 'react'
2
+ const useExternalScript = (url) => {
3
+ let [state, setState] = useState(url ? 'loading' : 'idle')
4
+
5
+ useEffect(() => {
6
+ if (!url) {
7
+ setState('idle')
8
+ return
9
+ }
10
+ let script: any = document.querySelector(`script[src="${url}"]`)
11
+
12
+ const handleScript = (e) => {
13
+ setState(e.type === 'load' ? 'ready' : 'error')
14
+ }
15
+ if (!script) {
16
+ script = document.createElement('script')
17
+ script.type = 'application/javascript'
18
+ script.src = url
19
+ script.async = true
20
+ document.body.appendChild(script)
21
+ script.addEventListener('load', handleScript)
22
+ script.addEventListener('error', handleScript)
23
+ }
24
+
25
+ script.addEventListener('load', handleScript)
26
+ script.addEventListener('error', handleScript)
27
+
28
+ return () => {
29
+ script.removeEventListener('load', handleScript)
30
+ script.removeEventListener('error', handleScript)
31
+ document.body.removeChild(script)
32
+ }
33
+ }, [url])
34
+
35
+ return state
36
+ }
37
+
38
+ export default useExternalScript
@@ -13,10 +13,10 @@ export default function useFilters(initialState, key?: any) {
13
13
  }, [])
14
14
 
15
15
  function onFilterChange(modified) {
16
- setFilters({
17
- ...filters,
16
+ setFilters((prev) => ({
17
+ ...prev,
18
18
  ...modified,
19
- })
19
+ }))
20
20
 
21
21
  const filterData = {
22
22
  ...filters,
@@ -36,6 +36,9 @@ export default function useFilters(initialState, key?: any) {
36
36
  if (filteredSessionArray?.length > 0) {
37
37
  sessionStorage.setItem(key, JSON.stringify(filteredSessionArray))
38
38
  setFiltersApplied(true)
39
+ } else {
40
+ sessionStorage.removeItem(key)
41
+ setFiltersApplied(false)
39
42
  }
40
43
  }
41
44