@campxdev/campx-web-utils 0.1.10 → 0.1.11

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/exports.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./src/config/axios";
2
+ export * from "./src/context/SnackbarProvider";
2
3
  export * from "./src/ErrorBoundary/export";
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@campxdev/campx-web-utils",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "main": "./exports.ts",
5
5
  "private": false,
6
6
  "dependencies": {
7
- "@campxdev/react-blueprint": "^1.0.16",
7
+ "@campxdev/react-blueprint": "^1.1.2",
8
+ "@hookform/resolvers": "^3.9.0",
8
9
  "@mui/x-date-pickers": "^7.11.0",
9
10
  "@testing-library/jest-dom": "^5.14.1",
10
11
  "@testing-library/react": "^13.0.0",
@@ -15,14 +16,17 @@
15
16
  "@types/react-dom": "^18.0.0",
16
17
  "axios": "^1.7.2",
17
18
  "cookie-js": "^0.0.1",
19
+ "device-detector-js": "^3.0.3",
18
20
  "pullstate": "^1.25.0",
19
21
  "react": "^18.3.1",
20
22
  "react-dom": "^18.3.1",
23
+ "react-hook-form": "^7.52.1",
21
24
  "react-query": "^3.39.3",
22
25
  "react-scripts": "5.0.1",
23
26
  "react-toastify": "^9.0.1",
24
27
  "typescript": "^4.4.2",
25
- "web-vitals": "^2.1.0"
28
+ "web-vitals": "^2.1.0",
29
+ "yup": "^1.4.0"
26
30
  },
27
31
  "devDependencies": {
28
32
  "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
@@ -1,16 +1,19 @@
1
1
  import {
2
2
  Button,
3
+ CustomDialog,
3
4
  InternalServerError,
4
5
  NoInterneConnection,
6
+ PageNotFound,
5
7
  UnAuthorized,
6
8
  } from "@campxdev/react-blueprint";
7
9
  import { Alert, Box, styled } from "@mui/material";
8
10
  import Cookies from "js-cookie";
9
- import { ReactNode } from "react";
11
+ import { ReactNode, useState } from "react";
10
12
  import { ErrorBoundary as ReactErrorBoundary } from "react-error-boundary";
11
13
  import { QueryErrorResetBoundary } from "react-query";
12
14
  import { useLocation, useNavigate } from "react-router-dom";
13
15
  import { axios } from "../config/axios";
16
+ import { Login } from "./Login";
14
17
 
15
18
  export type ErrorBoundaryProps = {
16
19
  children: ReactNode;
@@ -55,14 +58,15 @@ export const ErrorBoundary = (props: ErrorBoundaryProps) => {
55
58
  };
56
59
 
57
60
  export const ErrorFallback = ({ error, resetErrorBoundary }: any) => {
58
- console.log(error, "llll");
59
61
  if (error?.response?.status) {
60
62
  switch (error?.response?.status) {
61
63
  case 401:
62
- return <UnAuth resetErrorBoundary={resetErrorBoundary} />;
64
+ return <UnAuth />;
63
65
 
64
66
  case 500:
65
67
  return <InternalServerError resetBoundary={resetErrorBoundary} />;
68
+ case 404:
69
+ return <PageNotFound />;
66
70
  }
67
71
  }
68
72
 
@@ -88,8 +92,9 @@ export const ErrorFallback = ({ error, resetErrorBoundary }: any) => {
88
92
  );
89
93
  };
90
94
 
91
- const UnAuth = ({ resetBoundary }: any) => {
95
+ const UnAuth = () => {
92
96
  const navigate = useNavigate();
97
+ const [isModalOpen, setModalOpen] = useState(false);
93
98
 
94
99
  const sessionCookie = Cookies.get("campx_session_key");
95
100
 
@@ -100,10 +105,14 @@ const UnAuth = ({ resetBoundary }: any) => {
100
105
  };
101
106
 
102
107
  const handleLoginClick = () => {
103
- if (!sessionCookie) {
104
- navigate("/auth/login");
108
+ if (window.location.hostname == "localhost") {
109
+ setModalOpen(true);
105
110
  } else {
106
- appinit();
111
+ if (!sessionCookie) {
112
+ navigate("/auth/login");
113
+ } else {
114
+ appinit();
115
+ }
107
116
  }
108
117
  };
109
118
 
@@ -120,6 +129,11 @@ const UnAuth = ({ resetBoundary }: any) => {
120
129
  >
121
130
  Click Here To Login
122
131
  </Button>
132
+ <CustomDialog
133
+ open={isModalOpen}
134
+ onClose={() => setModalOpen(false)}
135
+ content={({ close }) => <Login close={close} />}
136
+ />
123
137
  </>
124
138
  }
125
139
  />
@@ -0,0 +1,194 @@
1
+ import { axios } from "@campxdev/campx-web-utils";
2
+ import { Button, Icons, TextField } from "@campxdev/react-blueprint";
3
+ import { yupResolver } from "@hookform/resolvers/yup";
4
+ import { Box, Stack } from "@mui/material";
5
+ import DeviceDetector from "device-detector-js";
6
+ import Cookies from "js-cookie";
7
+ import { useEffect, useState } from "react";
8
+ import { Controller, useForm } from "react-hook-form";
9
+ import { useMutation } from "react-query";
10
+
11
+ import * as Yup from "yup";
12
+
13
+ type DeviceInformation = {
14
+ deviceType: string;
15
+ clientName: string;
16
+ os: string;
17
+ osVersion: string;
18
+ latitude: number | null;
19
+ longitude: number | null;
20
+ tokenType: string;
21
+ };
22
+
23
+ export type DeviceState = {
24
+ deviceInformation: DeviceInformation;
25
+ isLocationAllowed: boolean;
26
+ };
27
+
28
+ const performLogin = async (body: any) => {
29
+ if (!body.latitude || !body.longitude) {
30
+ const response = await fetch(
31
+ "https://www.googleapis.com/geolocation/v1/geolocate?key=AIzaSyB2YCpo1yi107RYj1LdZu2DCcpcO93reFY",
32
+ {
33
+ method: "POST",
34
+ headers: {
35
+ "Content-Type": "application/json",
36
+ },
37
+ body: JSON.stringify({
38
+ considerIp: "true",
39
+ }),
40
+ }
41
+ );
42
+ const data = await response.json();
43
+ body.latitude = data.location.lat;
44
+ body.longitude = data.location.lng;
45
+ }
46
+ return axios.post(`/auth-server/auth/login`, body).then((res) => res.data);
47
+ };
48
+
49
+ const validationSchema = Yup.object().shape({
50
+ username: Yup.string().required("Username is required"),
51
+ password: Yup.string().required("Password is required"),
52
+ });
53
+
54
+ export const Login = ({ close }: { close: () => void }) => {
55
+ const [deviceState, setDeviceState] = useState<DeviceState>({
56
+ deviceInformation: {
57
+ deviceType: "browser",
58
+ clientName: "unknown",
59
+ os: "unknown",
60
+ osVersion: "unknown",
61
+ latitude: null,
62
+ longitude: null,
63
+ tokenType: "WEB",
64
+ },
65
+ isLocationAllowed: true,
66
+ });
67
+
68
+ useEffect(() => {
69
+ navigator.geolocation.getCurrentPosition((position) => {
70
+ setDeviceState((s) => ({
71
+ ...s,
72
+ deviceInformation: {
73
+ ...s.deviceInformation,
74
+ latitude: position.coords.latitude,
75
+ longitude: position.coords.longitude,
76
+ },
77
+ }));
78
+ });
79
+ navigator.permissions
80
+ .query({ name: "geolocation" })
81
+ .then((permissionStatus) => {
82
+ if (permissionStatus.state === "denied") {
83
+ setDeviceState((s) => ({
84
+ ...s,
85
+ isLocationAllowed: false,
86
+ }));
87
+ }
88
+ });
89
+
90
+ const detector = new DeviceDetector();
91
+ const deviceInfo = detector.parse(navigator.userAgent);
92
+ setDeviceState((s) => ({
93
+ ...s,
94
+ deviceInformation: {
95
+ ...s.deviceInformation,
96
+ clientName: deviceInfo.client?.name || s.deviceInformation.clientName,
97
+ os: deviceInfo.os?.name || s.deviceInformation.os,
98
+ osVersion: deviceInfo.os?.version || s.deviceInformation.osVersion,
99
+ },
100
+ }));
101
+ }, []);
102
+
103
+ const {
104
+ handleSubmit,
105
+ control,
106
+ formState: { errors },
107
+ } = useForm({ resolver: yupResolver(validationSchema) });
108
+
109
+ const { mutate, isLoading } = useMutation(performLogin, {
110
+ onSuccess(data) {
111
+ if (data.loginSuccessful) {
112
+ Cookies.remove("campx_session_key");
113
+ Cookies.remove("campx_tenant");
114
+ Cookies.remove("campx_institution");
115
+ Cookies.set("campx_session_key", data?.token);
116
+ Cookies.set("campx_tenant", data?.subDomain);
117
+ Cookies.set("campx_institution", data?.institutionCode);
118
+ close();
119
+ window.location.reload();
120
+ }
121
+ },
122
+ });
123
+ const onSubmit = async (body: any) => {
124
+ mutate({ ...body, ...deviceState.deviceInformation });
125
+ };
126
+
127
+ return (
128
+ <>
129
+ <Stack gap="16px" justifyContent="space-between" alignItems="center">
130
+ <Box height="36px" width="194.8px">
131
+ <Icons.CampxFullLogoIcon />
132
+ </Box>
133
+ <Stack gap="10px" justifyContent="center" alignItems="center">
134
+ <Controller
135
+ control={control}
136
+ render={({ field }: { field: any }) => {
137
+ return (
138
+ <TextField
139
+ size="medium"
140
+ name="username"
141
+ sx={{
142
+ width: "400px",
143
+ }}
144
+ placeholder="Enter Username or Email"
145
+ containerProps={{ my: "0" }}
146
+ onChange={field.onChange}
147
+ label="Username or Email"
148
+ error={!!errors.username}
149
+ required
150
+ />
151
+ );
152
+ }}
153
+ name="username"
154
+ />
155
+ <Controller
156
+ control={control}
157
+ render={({ field }: { field: any }) => {
158
+ return (
159
+ <TextField
160
+ size="medium"
161
+ name="password"
162
+ sx={{
163
+ width: "400px",
164
+ }}
165
+ placeholder="Enter password"
166
+ onChange={field.onChange}
167
+ containerProps={{ my: "0" }}
168
+ label="Password"
169
+ type="password"
170
+ error={!!errors.password}
171
+ required
172
+ />
173
+ );
174
+ }}
175
+ name="password"
176
+ />
177
+ <Button
178
+ type="submit"
179
+ color="primary"
180
+ variant="contained"
181
+ sx={{
182
+ width: "400px",
183
+ }}
184
+ onClick={handleSubmit(onSubmit)}
185
+ disabled={isLoading}
186
+ loading={isLoading}
187
+ >
188
+ Login
189
+ </Button>
190
+ </Stack>
191
+ </Stack>
192
+ </>
193
+ );
194
+ };