@campxdev/campx-web-utils 1.1.8 → 1.2.0
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/export.ts +9 -0
- package/package.json +7 -4
- package/src/App.css +38 -0
- package/src/App.tsx +16 -0
- package/src/AppContent.tsx +19 -0
- package/src/components/ActivityLog.tsx +96 -0
- package/src/components/ChangePassword.tsx +183 -0
- package/src/config/axios.ts +146 -0
- package/src/context/ConfirmDialogProvider.tsx +78 -0
- package/src/context/ErrorBoundary/ErrorBoundary.tsx +131 -0
- package/src/context/ErrorBoundary/Login.tsx +206 -0
- package/src/context/Providers.tsx +76 -0
- package/src/context/SnackbarProvider.tsx +42 -0
- package/src/context/application-store.ts +86 -0
- package/src/context/export.ts +5 -0
- package/src/hooks/export.ts +1 -0
- package/src/hooks/useConfirm.ts +57 -0
- package/src/index.css +13 -0
- package/src/index.tsx +19 -0
- package/src/layout/AppLayout/AppLayout.tsx +155 -0
- package/src/layout/AppLayout/components/HelpDocs.tsx +121 -0
- package/src/logo.svg +1 -0
- package/src/react-app-env.d.ts +1 -0
- package/src/reportWebVitals.ts +15 -0
- package/src/routes/main.tsx +32 -0
- package/src/selectors/BatchSelector.tsx +15 -0
- package/src/selectors/CourseSelector.tsx +16 -0
- package/src/selectors/DepartmentSelector.tsx +19 -0
- package/src/selectors/EmployeesSelector.tsx +20 -0
- package/src/selectors/FeeTypeSelector.tsx +15 -0
- package/src/selectors/HostelBlocksSelector.tsx +19 -0
- package/src/selectors/HostelFloorSelector.tsx +19 -0
- package/src/selectors/HostelRoomSelector.tsx +20 -0
- package/src/selectors/PrintFormatSelector.tsx +17 -0
- package/src/selectors/ProgramSelector.tsx +16 -0
- package/src/selectors/RegulationSelector.tsx +19 -0
- package/src/selectors/SemesterSelector.tsx +16 -0
- package/src/selectors/YearRangeSelector.tsx +26 -0
- package/src/selectors/export.ts +13 -0
- package/src/selectors/utils.tsx +39 -0
- package/src/setupTests.ts +5 -0
- package/src/utils/constants.ts +7 -0
- package/src/utils/functions.tsx +11 -0
package/export.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './src/components/ChangePassword';
|
|
2
|
+
export * from './src/config/axios';
|
|
3
|
+
export * from './src/context/export';
|
|
4
|
+
export * from './src/hooks/export';
|
|
5
|
+
export * from './src/layout/AppLayout/AppLayout';
|
|
6
|
+
export * from './src/layout/AppLayout/components/HelpDocs';
|
|
7
|
+
export * from './src/selectors/export';
|
|
8
|
+
export * from './src/utils/constants';
|
|
9
|
+
export * from './src/utils/functions';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@campxdev/campx-web-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"author": "CampX",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -14,11 +14,13 @@
|
|
|
14
14
|
"module": "dist/esm/index.js",
|
|
15
15
|
"types": "dist/index.d.ts",
|
|
16
16
|
"files": [
|
|
17
|
-
"dist"
|
|
17
|
+
"dist",
|
|
18
|
+
"src",
|
|
19
|
+
"export.ts"
|
|
18
20
|
],
|
|
19
21
|
"private": false,
|
|
20
22
|
"peerDependencies": {
|
|
21
|
-
"@campxdev/react-blueprint": "2.
|
|
23
|
+
"@campxdev/react-blueprint": "2.2.0",
|
|
22
24
|
"@emotion/react": "^11.14.0",
|
|
23
25
|
"@emotion/styled": "^11.14.0",
|
|
24
26
|
"@mui/icons-material": "^7.0.2",
|
|
@@ -29,7 +31,7 @@
|
|
|
29
31
|
"react-router-dom": "^6.24.0"
|
|
30
32
|
},
|
|
31
33
|
"dependencies": {
|
|
32
|
-
"@campxdev/react-blueprint": "2.
|
|
34
|
+
"@campxdev/react-blueprint": "2.2.0",
|
|
33
35
|
"@hookform/resolvers": "^2.9.10",
|
|
34
36
|
"@mui/x-date-pickers": "^8.1.0",
|
|
35
37
|
"axios": "^1.7.2",
|
|
@@ -93,6 +95,7 @@
|
|
|
93
95
|
"test": "craco test",
|
|
94
96
|
"eject": "react-scripts eject",
|
|
95
97
|
"build": "rollup -c --bundleConfigAsCjs",
|
|
98
|
+
"yalc:dev": "cp package.json package.json.backup && node -e \"const fs=require('fs'); const pkg=JSON.parse(fs.readFileSync('./package.json')); pkg.main='export.ts'; pkg.module='export.ts'; pkg.types='export.ts'; pkg.exports={'.':'./export.ts'}; delete pkg.scripts.prepare; fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2));\" && yalc publish --push --sig --no-scripts && mv package.json.backup package.json",
|
|
96
99
|
"prepare": "npm run build"
|
|
97
100
|
},
|
|
98
101
|
"eslintConfig": {
|
package/src/App.css
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
.App {
|
|
2
|
+
text-align: center;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.App-logo {
|
|
6
|
+
height: 40vmin;
|
|
7
|
+
pointer-events: none;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
11
|
+
.App-logo {
|
|
12
|
+
animation: App-logo-spin infinite 20s linear;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.App-header {
|
|
17
|
+
background-color: #282c34;
|
|
18
|
+
min-height: 100vh;
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
font-size: calc(10px + 2vmin);
|
|
24
|
+
color: white;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.App-link {
|
|
28
|
+
color: #61dafb;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@keyframes App-logo-spin {
|
|
32
|
+
from {
|
|
33
|
+
transform: rotate(0deg);
|
|
34
|
+
}
|
|
35
|
+
to {
|
|
36
|
+
transform: rotate(360deg);
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/App.tsx
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useRoutes } from 'react-router-dom';
|
|
2
|
+
|
|
3
|
+
import { Providers } from './context/export';
|
|
4
|
+
import { mainRoutes } from './routes/main';
|
|
5
|
+
|
|
6
|
+
export default function App() {
|
|
7
|
+
return (
|
|
8
|
+
<Providers>
|
|
9
|
+
<AppRouter />
|
|
10
|
+
</Providers>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
const AppRouter = () => {
|
|
14
|
+
const app = useRoutes(mainRoutes);
|
|
15
|
+
return app;
|
|
16
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Breadcrumbs, Icons, TextField } from '@campxdev/react-blueprint';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { CourseSelector } from './selectors/CourseSelector';
|
|
4
|
+
|
|
5
|
+
function AppContent() {
|
|
6
|
+
const [value, setValue] = useState('63b810ee995d6d64f4a248de');
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<>
|
|
10
|
+
<Breadcrumbs pathTrimCount={0} />
|
|
11
|
+
<TextField label="habdkhabs" />
|
|
12
|
+
<Icons.AppsIcon />
|
|
13
|
+
|
|
14
|
+
<CourseSelector onChange={() => {}} />
|
|
15
|
+
</>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default AppContent;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ActivityLogView } from '@campxdev/react-blueprint';
|
|
2
|
+
import { Store } from 'pullstate';
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import { useInfiniteQuery } from 'react-query';
|
|
5
|
+
import { axios } from '../config/axios';
|
|
6
|
+
|
|
7
|
+
function formatDate(date: Date): string {
|
|
8
|
+
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type ActivityLogType = {
|
|
12
|
+
activitiesData: any[];
|
|
13
|
+
lastTimestamp: number | null;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type ActivityLogStoreType = {
|
|
17
|
+
[logType: string]: ActivityLogType;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const ActivityLogStore = new Store<ActivityLogStoreType>({});
|
|
21
|
+
|
|
22
|
+
export default function ActivityLog({
|
|
23
|
+
apiEndpoint,
|
|
24
|
+
params,
|
|
25
|
+
}: {
|
|
26
|
+
apiEndpoint: string;
|
|
27
|
+
params: Record<string, any>;
|
|
28
|
+
}) {
|
|
29
|
+
const [fromDate, setFromDate] = useState<Date | null>(null);
|
|
30
|
+
const [toDate, setToDate] = useState<Date | null>(null);
|
|
31
|
+
|
|
32
|
+
const { activitiesData } = ActivityLogStore.useState(
|
|
33
|
+
(s) => s[params.logType] || { activitiesData: [], lastTimestamp: null },
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const fetchActivities = async ({ pageParam = null }) => {
|
|
37
|
+
const response = await axios.get(apiEndpoint, {
|
|
38
|
+
params: {
|
|
39
|
+
...params,
|
|
40
|
+
lastTimestamp: pageParam,
|
|
41
|
+
...(fromDate && { fromDate: formatDate(fromDate) }),
|
|
42
|
+
...(toDate && { toDate: formatDate(toDate) }),
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
return response.data;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const { fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } =
|
|
49
|
+
useInfiniteQuery(
|
|
50
|
+
['activities', params.logType, fromDate, toDate],
|
|
51
|
+
fetchActivities,
|
|
52
|
+
{
|
|
53
|
+
getNextPageParam: (lastPage) =>
|
|
54
|
+
lastPage?.length
|
|
55
|
+
? new Date(lastPage[lastPage.length - 1]?.timestamp).getTime()
|
|
56
|
+
: null,
|
|
57
|
+
onSuccess: (data) => {
|
|
58
|
+
ActivityLogStore.update((s) => {
|
|
59
|
+
const currentData = s[params.logType] || {
|
|
60
|
+
activitiesData: [],
|
|
61
|
+
lastTimestamp: null,
|
|
62
|
+
};
|
|
63
|
+
currentData.activitiesData = data.pages.flatMap(
|
|
64
|
+
(page) => page ?? [],
|
|
65
|
+
);
|
|
66
|
+
currentData.lastTimestamp = data.pages[data.pages.length - 1]
|
|
67
|
+
?.timestamp
|
|
68
|
+
? new Date(data.pages[data.pages.length - 1].timestamp).getTime()
|
|
69
|
+
: null;
|
|
70
|
+
s[params.logType] = currentData;
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
enabled: activitiesData.length === 0 || !!fromDate || !!toDate,
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (fromDate || toDate) {
|
|
79
|
+
fetchNextPage();
|
|
80
|
+
}
|
|
81
|
+
}, [fromDate, toDate, fetchNextPage]);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<ActivityLogView
|
|
85
|
+
activitiesData={activitiesData}
|
|
86
|
+
isFetchingNextPage={isFetchingNextPage}
|
|
87
|
+
fetchNextPage={fetchNextPage}
|
|
88
|
+
hasNextPage={hasNextPage}
|
|
89
|
+
fromDate={fromDate}
|
|
90
|
+
toDate={toDate}
|
|
91
|
+
isLoading={isLoading}
|
|
92
|
+
setFromDate={setFromDate}
|
|
93
|
+
setToDate={setToDate}
|
|
94
|
+
/>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CircularProgress,
|
|
3
|
+
IconButton,
|
|
4
|
+
InputAdornment,
|
|
5
|
+
MenuItem,
|
|
6
|
+
Stack,
|
|
7
|
+
styled,
|
|
8
|
+
} from '@mui/material';
|
|
9
|
+
import { useState } from 'react';
|
|
10
|
+
import { useForm } from 'react-hook-form';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
Button,
|
|
14
|
+
DialogButton,
|
|
15
|
+
FormControlWrapper,
|
|
16
|
+
TextField,
|
|
17
|
+
} from '@campxdev/react-blueprint';
|
|
18
|
+
import { Visibility, VisibilityOff } from '@mui/icons-material';
|
|
19
|
+
import { toast } from 'react-toastify';
|
|
20
|
+
import { axios } from '../config/axios';
|
|
21
|
+
import { isDevelopment } from '../utils/constants';
|
|
22
|
+
|
|
23
|
+
interface PasswordVisibilityState {
|
|
24
|
+
oldPassword: boolean;
|
|
25
|
+
newPassword: boolean;
|
|
26
|
+
confirmPassword: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface FormData {
|
|
30
|
+
oldPassword: string;
|
|
31
|
+
newPassword: string;
|
|
32
|
+
confirmPassword: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface Field {
|
|
36
|
+
label: string;
|
|
37
|
+
name: keyof PasswordVisibilityState;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface ChangePasswordProps {
|
|
41
|
+
close: () => void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const StyledMenuItem = styled(MenuItem)(() => ({
|
|
45
|
+
display: 'flex',
|
|
46
|
+
alignItems: 'center',
|
|
47
|
+
gap: '5px',
|
|
48
|
+
'& .MuiListItemIcon-root': {
|
|
49
|
+
minWidth: '24px',
|
|
50
|
+
},
|
|
51
|
+
'& .MuiSvgIcon-root': {
|
|
52
|
+
height: '14px',
|
|
53
|
+
width: '14px',
|
|
54
|
+
},
|
|
55
|
+
}));
|
|
56
|
+
|
|
57
|
+
export function ChangePassword({ close }: ChangePasswordProps) {
|
|
58
|
+
const [showPassword, setShowPassword] = useState<PasswordVisibilityState>({
|
|
59
|
+
oldPassword: false,
|
|
60
|
+
newPassword: false,
|
|
61
|
+
confirmPassword: false,
|
|
62
|
+
});
|
|
63
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
64
|
+
|
|
65
|
+
const { handleSubmit, control, reset } = useForm<FormData>();
|
|
66
|
+
|
|
67
|
+
const onSubmit = async (formData: any) => {
|
|
68
|
+
setIsLoading(true);
|
|
69
|
+
const { oldPassword, newPassword, confirmPassword } = formData;
|
|
70
|
+
if (newPassword === confirmPassword) {
|
|
71
|
+
try {
|
|
72
|
+
await axios.post(
|
|
73
|
+
isDevelopment
|
|
74
|
+
? 'https://api.campx.dev/auth-server/auth/change-password'
|
|
75
|
+
: 'https://api.campx.in/auth-server/auth/change-password',
|
|
76
|
+
{
|
|
77
|
+
oldPassword,
|
|
78
|
+
newPassword,
|
|
79
|
+
},
|
|
80
|
+
);
|
|
81
|
+
toast.success('Password Changed Successfully');
|
|
82
|
+
setIsLoading(false);
|
|
83
|
+
reset();
|
|
84
|
+
close();
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.log(error, 'this is error');
|
|
87
|
+
// axiosErrorToast(error);
|
|
88
|
+
setIsLoading(false);
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
toast.error('New Password, Confirm Password must be same');
|
|
92
|
+
setIsLoading(false);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const fields: Field[] = [
|
|
97
|
+
{ label: 'Old Password', name: 'oldPassword' },
|
|
98
|
+
{ label: 'New Password', name: 'newPassword' },
|
|
99
|
+
{ label: 'Confirm Password', name: 'confirmPassword' },
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
104
|
+
<FormControlWrapper control={control}>
|
|
105
|
+
<Stack gap={1} direction="column">
|
|
106
|
+
{fields.map((item) => {
|
|
107
|
+
return (
|
|
108
|
+
<>
|
|
109
|
+
<TextField
|
|
110
|
+
label={item.label}
|
|
111
|
+
name={item.name}
|
|
112
|
+
type={showPassword[item.name] ? 'text' : 'password'}
|
|
113
|
+
required
|
|
114
|
+
InputProps={{
|
|
115
|
+
endAdornment: (
|
|
116
|
+
<InputAdornment position="end">
|
|
117
|
+
<IconButton
|
|
118
|
+
size="small"
|
|
119
|
+
onClick={() =>
|
|
120
|
+
setShowPassword((prev: any) => ({
|
|
121
|
+
...prev,
|
|
122
|
+
[item.name]: !prev[item.name],
|
|
123
|
+
}))
|
|
124
|
+
}
|
|
125
|
+
edge="end"
|
|
126
|
+
>
|
|
127
|
+
{showPassword[item.name] ? (
|
|
128
|
+
<Visibility />
|
|
129
|
+
) : (
|
|
130
|
+
<VisibilityOff />
|
|
131
|
+
)}
|
|
132
|
+
</IconButton>
|
|
133
|
+
</InputAdornment>
|
|
134
|
+
),
|
|
135
|
+
}}
|
|
136
|
+
/>
|
|
137
|
+
</>
|
|
138
|
+
);
|
|
139
|
+
})}
|
|
140
|
+
|
|
141
|
+
<Stack direction={'row'} gap={2} sx={{ marginTop: '15px' }}>
|
|
142
|
+
<Button variant="outlined" onClick={close}>
|
|
143
|
+
Cancel
|
|
144
|
+
</Button>
|
|
145
|
+
<Button
|
|
146
|
+
type="submit"
|
|
147
|
+
variant="contained"
|
|
148
|
+
endIcon={
|
|
149
|
+
isLoading && (
|
|
150
|
+
<CircularProgress
|
|
151
|
+
style={{ color: 'white' }}
|
|
152
|
+
size="30px"
|
|
153
|
+
thickness={1.7}
|
|
154
|
+
/>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
>
|
|
158
|
+
Submit
|
|
159
|
+
</Button>
|
|
160
|
+
</Stack>
|
|
161
|
+
</Stack>
|
|
162
|
+
</FormControlWrapper>
|
|
163
|
+
</form>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export const ChangePasswordDialog: React.FC = () => (
|
|
168
|
+
<>
|
|
169
|
+
<DialogButton
|
|
170
|
+
anchor={({ open }) => (
|
|
171
|
+
<StyledMenuItem
|
|
172
|
+
onClick={() => {
|
|
173
|
+
open();
|
|
174
|
+
}}
|
|
175
|
+
>
|
|
176
|
+
Change Password
|
|
177
|
+
</StyledMenuItem>
|
|
178
|
+
)}
|
|
179
|
+
content={({ close }) => <ChangePassword close={close} />}
|
|
180
|
+
title="Change Password"
|
|
181
|
+
/>
|
|
182
|
+
</>
|
|
183
|
+
);
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import Axios from 'axios';
|
|
2
|
+
import Cookies from 'js-cookie';
|
|
3
|
+
import { ApplicationStore } from '../context/application-store';
|
|
4
|
+
import { institutionCode, isDevelopment, tenantCode } from '../utils/constants';
|
|
5
|
+
|
|
6
|
+
// Declare the extended axios types to include our custom property
|
|
7
|
+
declare module 'axios' {
|
|
8
|
+
export interface AxiosRequestConfig {
|
|
9
|
+
skipInstitutionCode?: boolean;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare global {
|
|
14
|
+
interface ImportMeta {
|
|
15
|
+
env: {
|
|
16
|
+
VITE_APP_API_HOST?: string;
|
|
17
|
+
// Add other Vite environment variables as needed
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Get base URL from environment variables
|
|
23
|
+
const getBaseUrl = (): string => {
|
|
24
|
+
// For Vite
|
|
25
|
+
if (typeof import.meta !== 'undefined' && import.meta.env) {
|
|
26
|
+
return import.meta.env.VITE_APP_API_HOST || '';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// For Create React App
|
|
30
|
+
return process.env.REACT_APP_API_HOST || '';
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Format query parameters
|
|
34
|
+
const formatParams = (params: any) => {
|
|
35
|
+
return Object.fromEntries(
|
|
36
|
+
Object.entries(params ?? {}).map(([key, value]) => [
|
|
37
|
+
key,
|
|
38
|
+
value === '__empty__' ? '' : value,
|
|
39
|
+
]),
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Create axios instance
|
|
44
|
+
export const axios = Axios.create({
|
|
45
|
+
baseURL: getBaseUrl(),
|
|
46
|
+
withCredentials: true,
|
|
47
|
+
headers: {
|
|
48
|
+
'x-tenant-id': tenantCode,
|
|
49
|
+
'x-institution-code': institutionCode,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Request interceptor
|
|
54
|
+
axios.interceptors.request.use(
|
|
55
|
+
(config) => {
|
|
56
|
+
// Format query parameters
|
|
57
|
+
const params = formatParams(config?.params);
|
|
58
|
+
|
|
59
|
+
// Add session tokens from cookies if in development
|
|
60
|
+
const sessionKey = Cookies.get('campx_session_key');
|
|
61
|
+
const openPaymentsKey = Cookies.get('campx_open_payments_key');
|
|
62
|
+
const studentSessionKey = Cookies.get('campx_student_key');
|
|
63
|
+
|
|
64
|
+
if (isDevelopment && sessionKey) {
|
|
65
|
+
config.headers.set('campx_session_key', sessionKey);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (isDevelopment && studentSessionKey) {
|
|
69
|
+
config.headers.set('campx_student_key', studentSessionKey);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (openPaymentsKey) {
|
|
73
|
+
config.headers.set('campx_open_payments_key', openPaymentsKey);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Skip sending X-institution-code if the flag is set
|
|
77
|
+
if (config.skipInstitutionCode) {
|
|
78
|
+
config.headers.delete('x-institution-code');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
...config,
|
|
83
|
+
params,
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
(error) => {
|
|
87
|
+
return Promise.reject(error);
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Response interceptor
|
|
92
|
+
axios.interceptors.response.use(
|
|
93
|
+
(response) => {
|
|
94
|
+
// Check if the response has hideSnackbar property
|
|
95
|
+
const shouldHideSnackbar = response.data?.hideSnackbar === true;
|
|
96
|
+
|
|
97
|
+
// Handle successful responses
|
|
98
|
+
if (
|
|
99
|
+
!shouldHideSnackbar &&
|
|
100
|
+
response.config.method &&
|
|
101
|
+
['put', 'post', 'delete', 'patch'].includes(response.config.method) &&
|
|
102
|
+
[200, 201, 202].includes(response.status)
|
|
103
|
+
) {
|
|
104
|
+
// Use ApplicationStore for snackbar
|
|
105
|
+
ApplicationStore.update((s) => {
|
|
106
|
+
s.snackbar.open = true;
|
|
107
|
+
s.snackbar.message = response.data.message || 'Operation successful';
|
|
108
|
+
s.snackbar.severity = 'success';
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return response;
|
|
113
|
+
},
|
|
114
|
+
(err) => {
|
|
115
|
+
// Check if the error response has hideSnackbar property
|
|
116
|
+
const shouldHideSnackbar = err.response?.data?.hideSnackbar === true;
|
|
117
|
+
|
|
118
|
+
// Handle error responses
|
|
119
|
+
if (!shouldHideSnackbar && [400, 422].includes(err.response?.status)) {
|
|
120
|
+
// Use ApplicationStore for error snackbar
|
|
121
|
+
ApplicationStore.update((s) => {
|
|
122
|
+
s.snackbar.open = true;
|
|
123
|
+
s.snackbar.message = err.response.data.message || 'Bad Request';
|
|
124
|
+
s.snackbar.severity = 'error';
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (
|
|
129
|
+
!shouldHideSnackbar &&
|
|
130
|
+
err.response?.config.method &&
|
|
131
|
+
['put', 'post', 'delete', 'patch'].includes(err.response.config.method) &&
|
|
132
|
+
[404].includes(err.response.status)
|
|
133
|
+
) {
|
|
134
|
+
// Use ApplicationStore for not found error snackbar
|
|
135
|
+
ApplicationStore.update((s) => {
|
|
136
|
+
s.snackbar.open = true;
|
|
137
|
+
s.snackbar.message = err.response.data.message || 'Not Found';
|
|
138
|
+
s.snackbar.severity = 'error';
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return Promise.reject(err);
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
export default axios;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ConfirmDialog, ConfirmDialogType } from '@campxdev/react-blueprint';
|
|
2
|
+
import { createContext, ReactNode } from 'react';
|
|
3
|
+
import { ApplicationStore } from './application-store';
|
|
4
|
+
|
|
5
|
+
interface ConfirmStateProps {
|
|
6
|
+
showConfirmDialog: (
|
|
7
|
+
title: string,
|
|
8
|
+
message: string,
|
|
9
|
+
onConfirm: () => void,
|
|
10
|
+
onCancel: () => void,
|
|
11
|
+
type?: ConfirmDialogType,
|
|
12
|
+
confirmButtonText?: string,
|
|
13
|
+
cancelButtonText?: string,
|
|
14
|
+
) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const ConfirmContext = createContext<ConfirmStateProps>({
|
|
18
|
+
showConfirmDialog: () => {},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const ConfirmDialogProvider = ({
|
|
22
|
+
children,
|
|
23
|
+
}: {
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
}) => {
|
|
26
|
+
const confirmDialog = ApplicationStore.useState((s) => s.confirmDialog);
|
|
27
|
+
|
|
28
|
+
const showConfirmDialog = (
|
|
29
|
+
title: string,
|
|
30
|
+
message: string,
|
|
31
|
+
onConfirm: () => void,
|
|
32
|
+
onCancel: () => void,
|
|
33
|
+
type: ConfirmDialogType = 'confirm',
|
|
34
|
+
confirmButtonText?: string,
|
|
35
|
+
cancelButtonText?: string,
|
|
36
|
+
) => {
|
|
37
|
+
ApplicationStore.update((s) => {
|
|
38
|
+
s.confirmDialog = {
|
|
39
|
+
isOpen: true,
|
|
40
|
+
title,
|
|
41
|
+
message,
|
|
42
|
+
onConfirm,
|
|
43
|
+
onCancel,
|
|
44
|
+
type,
|
|
45
|
+
confirmButtonText,
|
|
46
|
+
cancelButtonText,
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const handleCloseDialog = () => {
|
|
52
|
+
ApplicationStore.update((s) => {
|
|
53
|
+
s.confirmDialog.isOpen = false;
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<ConfirmContext.Provider value={{ showConfirmDialog }}>
|
|
59
|
+
{children}
|
|
60
|
+
<ConfirmDialog
|
|
61
|
+
isOpen={confirmDialog.isOpen}
|
|
62
|
+
title={confirmDialog.title}
|
|
63
|
+
message={confirmDialog.message}
|
|
64
|
+
type={confirmDialog.type}
|
|
65
|
+
confirmButtonText={confirmDialog.confirmButtonText}
|
|
66
|
+
cancelButtonText={confirmDialog.cancelButtonText}
|
|
67
|
+
onConfirm={() => {
|
|
68
|
+
confirmDialog.onConfirm();
|
|
69
|
+
handleCloseDialog();
|
|
70
|
+
}}
|
|
71
|
+
onCancel={() => {
|
|
72
|
+
confirmDialog.onCancel();
|
|
73
|
+
handleCloseDialog();
|
|
74
|
+
}}
|
|
75
|
+
/>
|
|
76
|
+
</ConfirmContext.Provider>
|
|
77
|
+
);
|
|
78
|
+
};
|