@campxdev/campx-web-utils 1.1.9 → 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/dist/cjs/index.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/index.d.ts +2 -152
- 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/dist/cjs/types/src/layout/AppLayout/AppLayoutV2.d.ts +0 -150
- package/dist/esm/types/src/layout/AppLayout/AppLayoutV2.d.ts +0 -150
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,6 @@ import * as react from 'react';
|
|
|
5
5
|
import { ReactNode } from 'react';
|
|
6
6
|
import * as _mui_material_OverridableComponent from '@mui/material/OverridableComponent';
|
|
7
7
|
import * as _mui_material from '@mui/material';
|
|
8
|
-
import { SxProps } from '@mui/material';
|
|
9
8
|
import * as _axios from 'axios';
|
|
10
9
|
import { Severity, ConfirmDialogType, SideMenuItemProps, SingleSelectProps } from '@campxdev/react-blueprint';
|
|
11
10
|
import { Store } from 'pullstate';
|
|
@@ -147,155 +146,6 @@ type Props = {
|
|
|
147
146
|
};
|
|
148
147
|
declare const AppLayout: React.FC<Props>;
|
|
149
148
|
|
|
150
|
-
/**
|
|
151
|
-
* AppLayoutV2 - An improved layout component for application pages
|
|
152
|
-
*
|
|
153
|
-
* @example Basic usage with menu-based sidebar:
|
|
154
|
-
* ```tsx
|
|
155
|
-
* function MyApp() {
|
|
156
|
-
* const [collapsed, setCollapsed] = useState(false);
|
|
157
|
-
* const menuItems = [
|
|
158
|
-
* { label: 'Dashboard', path: '/dashboard', icon: <DashboardIcon /> },
|
|
159
|
-
* { label: 'Users', path: '/users', icon: <UsersIcon /> },
|
|
160
|
-
* ];
|
|
161
|
-
*
|
|
162
|
-
* return (
|
|
163
|
-
* <AppLayoutV2
|
|
164
|
-
* userFullName="John Doe"
|
|
165
|
-
* designation="Software Engineer"
|
|
166
|
-
* collapsed={collapsed}
|
|
167
|
-
* onToggleSidebar={() => setCollapsed(!collapsed)}
|
|
168
|
-
* menu={menuItems}
|
|
169
|
-
* headerActions={[<Button key="add">Add New</Button>]}
|
|
170
|
-
* profileActions={[<MenuItem key="settings">Settings</MenuItem>]}
|
|
171
|
-
* institutionsData={institutions}
|
|
172
|
-
* onLogoutClick={handleLogout}
|
|
173
|
-
* >
|
|
174
|
-
* <YourPageContent />
|
|
175
|
-
* </AppLayoutV2>
|
|
176
|
-
* );
|
|
177
|
-
* }
|
|
178
|
-
* ```
|
|
179
|
-
*
|
|
180
|
-
* @example With custom sidebar and breadcrumbs:
|
|
181
|
-
* ```tsx
|
|
182
|
-
* function MyAppWithCustomSidebar() {
|
|
183
|
-
* const [collapsed, setCollapsed] = useState(false);
|
|
184
|
-
*
|
|
185
|
-
* const customSidebar = (
|
|
186
|
-
* <div style={{ padding: '16px' }}>
|
|
187
|
-
* <nav>
|
|
188
|
-
* <ul>
|
|
189
|
-
* <li><Link to="/dashboard">Dashboard</Link></li>
|
|
190
|
-
* <li><Link to="/reports">Reports</Link></li>
|
|
191
|
-
* </ul>
|
|
192
|
-
* </nav>
|
|
193
|
-
* </div>
|
|
194
|
-
* );
|
|
195
|
-
*
|
|
196
|
-
* const breadcrumbs = (
|
|
197
|
-
* <Breadcrumbs aria-label="breadcrumb">
|
|
198
|
-
* <Link to="/">Home</Link>
|
|
199
|
-
* <Link to="/dashboard">Dashboard</Link>
|
|
200
|
-
* <Typography color="textPrimary">Current Page</Typography>
|
|
201
|
-
* </Breadcrumbs>
|
|
202
|
-
* );
|
|
203
|
-
*
|
|
204
|
-
* return (
|
|
205
|
-
* <AppLayoutV2
|
|
206
|
-
* userFullName="Jane Smith"
|
|
207
|
-
* designation="Project Manager"
|
|
208
|
-
* collapsed={collapsed}
|
|
209
|
-
* onToggleSidebar={() => setCollapsed(!collapsed)}
|
|
210
|
-
* sidebar={customSidebar}
|
|
211
|
-
* breadcrumbs={breadcrumbs}
|
|
212
|
-
* headerActions={[
|
|
213
|
-
* <IconButton key="notifications"><NotificationsIcon /></IconButton>,
|
|
214
|
-
* <Button key="export">Export</Button>
|
|
215
|
-
* ]}
|
|
216
|
-
* showActiveDevices={true}
|
|
217
|
-
* clientName="My Company"
|
|
218
|
-
* headerSx={{ backgroundColor: 'primary.light' }}
|
|
219
|
-
* onLogoutClick={handleLogout}
|
|
220
|
-
* >
|
|
221
|
-
* <Dashboard />
|
|
222
|
-
* </AppLayoutV2>
|
|
223
|
-
* );
|
|
224
|
-
* }
|
|
225
|
-
* ```
|
|
226
|
-
*
|
|
227
|
-
* @example Minimal usage (no sidebar):
|
|
228
|
-
* ```tsx
|
|
229
|
-
* function SimpleLayout() {
|
|
230
|
-
* return (
|
|
231
|
-
* <AppLayoutV2
|
|
232
|
-
* userFullName="Admin User"
|
|
233
|
-
* collapsed={false} // This won't matter since no sidebar
|
|
234
|
-
* onLogoutClick={() => console.log('Logout')}
|
|
235
|
-
* >
|
|
236
|
-
* <SimplePageContent />
|
|
237
|
-
* </AppLayoutV2>
|
|
238
|
-
* );
|
|
239
|
-
* }
|
|
240
|
-
* ```
|
|
241
|
-
*/
|
|
242
|
-
|
|
243
|
-
interface AppLayoutV2Props {
|
|
244
|
-
/** Main content to be rendered in the layout */
|
|
245
|
-
children: ReactNode;
|
|
246
|
-
/** Optional sidebar content */
|
|
247
|
-
sidebar?: ReactNode;
|
|
248
|
-
/** Actions to be displayed in the header */
|
|
249
|
-
headerActions?: ReactNode[];
|
|
250
|
-
/** Actions for the user profile dropdown */
|
|
251
|
-
profileActions?: ReactNode[];
|
|
252
|
-
/** User's full name */
|
|
253
|
-
userFullName: string;
|
|
254
|
-
/** User's designation/title */
|
|
255
|
-
designation?: string;
|
|
256
|
-
/** Whether the sidebar is collapsed */
|
|
257
|
-
collapsed: boolean;
|
|
258
|
-
/** Callback to toggle sidebar state */
|
|
259
|
-
onToggleSidebar?: () => void;
|
|
260
|
-
/** Breadcrumbs component */
|
|
261
|
-
breadcrumbs?: ReactNode;
|
|
262
|
-
/** Institution data for multi-tenant scenarios */
|
|
263
|
-
institutionsData?: any[];
|
|
264
|
-
/** Custom styling for the profile component */
|
|
265
|
-
profileSx?: SxProps;
|
|
266
|
-
/** Logout click handler */
|
|
267
|
-
onLogoutClick?: () => void;
|
|
268
|
-
/** Whether to show active devices option */
|
|
269
|
-
showActiveDevices?: boolean;
|
|
270
|
-
/** Custom styling for the header */
|
|
271
|
-
headerSx?: SxProps;
|
|
272
|
-
/** Custom styling for the main content container */
|
|
273
|
-
mainContainerSx?: SxProps;
|
|
274
|
-
/** Client/organization name */
|
|
275
|
-
clientName?: string;
|
|
276
|
-
/** Menu items for sidebar (only used when sidebar prop is not provided) */
|
|
277
|
-
menu?: SideMenuItemProps[];
|
|
278
|
-
/** Help documentation configuration */
|
|
279
|
-
helpDocsConfig?: Record<string, {
|
|
280
|
-
actions: {
|
|
281
|
-
name: string;
|
|
282
|
-
onClick: () => void;
|
|
283
|
-
}[];
|
|
284
|
-
}>;
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* AppLayoutV2 - A flexible and responsive layout component for application pages.
|
|
288
|
-
*
|
|
289
|
-
* This component provides a modern layout structure with:
|
|
290
|
-
* - Responsive header using AppHeader
|
|
291
|
-
* - Optional collapsible sidebar
|
|
292
|
-
* - Flexible main content area
|
|
293
|
-
* - Mobile-responsive design
|
|
294
|
-
* - Error boundaries and loading states
|
|
295
|
-
* - Help documentation support
|
|
296
|
-
*/
|
|
297
|
-
declare const AppLayoutV2: React.FC<AppLayoutV2Props>;
|
|
298
|
-
|
|
299
149
|
interface HelpDocsActionType {
|
|
300
150
|
name: string;
|
|
301
151
|
onClick?: () => void;
|
|
@@ -343,5 +193,5 @@ declare const workspace: string;
|
|
|
343
193
|
|
|
344
194
|
declare const openInNewTab: (pathAfterBase: string) => void;
|
|
345
195
|
|
|
346
|
-
export { AppLayout,
|
|
347
|
-
export type {
|
|
196
|
+
export { AppLayout, ApplicationStore, BatchSelector, ChangePassword, ChangePasswordDialog, ConfirmDialogProvider, CourseSelector, DepartmentSelector, EmployeesSelector, ErrorBoundary, FeeTypeSelector, HelpDocs, HostelBlocksSelector, HostelFloorSelector, HostelRoomSelector, PrintFormatSelector, ProgramSelector, Providers, RegulationSelector, SemesterSelector, SnackbarProvider, StyledMenuItem, YearRangeSelector, axios, initialApplicationState, institutionCode, isDevelopment, openInNewTab, tenantCode, useConfirm, workspace };
|
|
197
|
+
export type { ApplicationStoreType, ErrorBoundaryProps };
|
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
|
+
);
|