react-session.manager.sk 2.0.6 → 2.0.8
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/README.md +39 -0
- package/dist/index.js +1 -1
- package/package.json +14 -2
- package/.babelrc +0 -5
- package/.gitattributes +0 -2
- package/.github/workflows/npm-publish.yml +0 -32
- package/dist/index.js.LICENSE.txt +0 -28
- package/src/components/ErrorBoundary.js +0 -142
- package/src/components/FingerPrint.js +0 -11
- package/src/components/versionCompare.js +0 -39
- package/src/index.js +0 -467
- package/src/styling/toast.css +0 -56
- package/webpack.config.js +0 -36
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license React
|
|
3
|
-
* react-is.production.js
|
|
4
|
-
*
|
|
5
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
6
|
-
*
|
|
7
|
-
* This source code is licensed under the MIT license found in the
|
|
8
|
-
* LICENSE file in the root directory of this source tree.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @license React
|
|
13
|
-
* react-jsx-runtime.production.min.js
|
|
14
|
-
*
|
|
15
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
16
|
-
*
|
|
17
|
-
* This source code is licensed under the MIT license found in the
|
|
18
|
-
* LICENSE file in the root directory of this source tree.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/** @license React v16.13.1
|
|
22
|
-
* react-is.production.min.js
|
|
23
|
-
*
|
|
24
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
25
|
-
*
|
|
26
|
-
* This source code is licensed under the MIT license found in the
|
|
27
|
-
* LICENSE file in the root directory of this source tree.
|
|
28
|
-
*/
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { toast } from "react-toastify";
|
|
3
|
-
|
|
4
|
-
class ErrorBoundary extends React.Component {
|
|
5
|
-
constructor(props) {
|
|
6
|
-
super(props);
|
|
7
|
-
this.state = { hasError: false, error: null, errorInfo: null };
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
static getDerivedStateFromError(error) {
|
|
11
|
-
// Update state so the next render will show the fallback UI
|
|
12
|
-
return { hasError: true };
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
componentDidCatch(error, errorInfo) {
|
|
16
|
-
console.error("ErrorBoundary caught an error:", error, errorInfo);
|
|
17
|
-
|
|
18
|
-
this.setState({
|
|
19
|
-
error: error,
|
|
20
|
-
errorInfo: errorInfo,
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
// Check if this is a theme-related error
|
|
24
|
-
const isThemeError =
|
|
25
|
-
error?.message?.includes("Cannot convert undefined or null to object") &&
|
|
26
|
-
(error?.stack?.includes("Object.entries") ||
|
|
27
|
-
error?.stack?.includes("theme") ||
|
|
28
|
-
error?.stack?.includes("Styled"));
|
|
29
|
-
|
|
30
|
-
const errorMessage = isThemeError
|
|
31
|
-
? "A theme error occurred. This might be due to connection issues. Please refresh the page."
|
|
32
|
-
: "Something went wrong. Please refresh the page or try again later.";
|
|
33
|
-
|
|
34
|
-
// Show a toast notification for the error
|
|
35
|
-
toast.error(errorMessage, {
|
|
36
|
-
toastId: "error-boundary",
|
|
37
|
-
icon: "❌",
|
|
38
|
-
autoClose: 10000,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// Log error to external service if needed
|
|
42
|
-
if (this.props.onError) {
|
|
43
|
-
this.props.onError(error, errorInfo);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
handleRetry = () => {
|
|
48
|
-
this.setState({ hasError: false, error: null, errorInfo: null });
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
render() {
|
|
52
|
-
if (this.state.hasError) {
|
|
53
|
-
// Custom error UI
|
|
54
|
-
if (this.props.fallback) {
|
|
55
|
-
return this.props.fallback;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return (
|
|
59
|
-
<div
|
|
60
|
-
style={{
|
|
61
|
-
padding: "20px",
|
|
62
|
-
textAlign: "center",
|
|
63
|
-
backgroundColor: "#f8f9fa",
|
|
64
|
-
border: "1px solid #dee2e6",
|
|
65
|
-
borderRadius: "8px",
|
|
66
|
-
margin: "20px",
|
|
67
|
-
minHeight: "200px",
|
|
68
|
-
display: "flex",
|
|
69
|
-
flexDirection: "column",
|
|
70
|
-
justifyContent: "center",
|
|
71
|
-
alignItems: "center",
|
|
72
|
-
}}
|
|
73
|
-
>
|
|
74
|
-
<div style={{ fontSize: "48px", marginBottom: "16px" }}>⚠️</div>
|
|
75
|
-
<h2 style={{ color: "#343a40", marginBottom: "16px" }}>
|
|
76
|
-
Something went wrong
|
|
77
|
-
</h2>
|
|
78
|
-
<p style={{ color: "#6c757d", marginBottom: "20px" }}>
|
|
79
|
-
An unexpected error occurred. Please try refreshing the page.
|
|
80
|
-
</p>
|
|
81
|
-
<button
|
|
82
|
-
onClick={this.handleRetry}
|
|
83
|
-
style={{
|
|
84
|
-
backgroundColor: "#007bff",
|
|
85
|
-
color: "white",
|
|
86
|
-
border: "none",
|
|
87
|
-
padding: "10px 20px",
|
|
88
|
-
borderRadius: "4px",
|
|
89
|
-
cursor: "pointer",
|
|
90
|
-
marginRight: "10px",
|
|
91
|
-
}}
|
|
92
|
-
>
|
|
93
|
-
Try Again
|
|
94
|
-
</button>
|
|
95
|
-
<button
|
|
96
|
-
onClick={() => window.location.reload()}
|
|
97
|
-
style={{
|
|
98
|
-
backgroundColor: "#28a745",
|
|
99
|
-
color: "white",
|
|
100
|
-
border: "none",
|
|
101
|
-
padding: "10px 20px",
|
|
102
|
-
borderRadius: "4px",
|
|
103
|
-
cursor: "pointer",
|
|
104
|
-
}}
|
|
105
|
-
>
|
|
106
|
-
Refresh Page
|
|
107
|
-
</button>
|
|
108
|
-
|
|
109
|
-
{process.env.NODE_ENV === "development" && this.state.error && (
|
|
110
|
-
<details
|
|
111
|
-
style={{ marginTop: "20px", textAlign: "left", width: "100%" }}
|
|
112
|
-
>
|
|
113
|
-
<summary style={{ cursor: "pointer", fontWeight: "bold" }}>
|
|
114
|
-
Error Details (Development)
|
|
115
|
-
</summary>
|
|
116
|
-
<pre
|
|
117
|
-
style={{
|
|
118
|
-
backgroundColor: "#f8f9fa",
|
|
119
|
-
padding: "10px",
|
|
120
|
-
borderRadius: "4px",
|
|
121
|
-
overflow: "auto",
|
|
122
|
-
fontSize: "12px",
|
|
123
|
-
marginTop: "10px",
|
|
124
|
-
maxHeight: "300px",
|
|
125
|
-
textAlign: "left",
|
|
126
|
-
}}
|
|
127
|
-
>
|
|
128
|
-
{this.state.error && this.state.error.toString()}
|
|
129
|
-
<br />
|
|
130
|
-
{this.state.errorInfo?.componentStack}
|
|
131
|
-
</pre>
|
|
132
|
-
</details>
|
|
133
|
-
)}
|
|
134
|
-
</div>
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return this.props.children;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export default ErrorBoundary;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { ClientJS } from "clientjs";
|
|
2
|
-
|
|
3
|
-
export default function getDeviceFingerprint() {
|
|
4
|
-
if (localStorage.getItem("deviceFingerprint"))
|
|
5
|
-
return localStorage.getItem("deviceFingerprint");
|
|
6
|
-
|
|
7
|
-
const client = new ClientJS();
|
|
8
|
-
const fingerprint = client.getFingerprint();
|
|
9
|
-
localStorage.setItem("deviceFingerprint", fingerprint);
|
|
10
|
-
return fingerprint;
|
|
11
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// Javascript implementation
|
|
2
|
-
|
|
3
|
-
// Method to compare two versions.
|
|
4
|
-
// Returns 1 if v2 is smaller, -1
|
|
5
|
-
// if v1 is smaller, 0 if equal
|
|
6
|
-
export default function versionCompare(v1, v2) {
|
|
7
|
-
// vnum stores each numeric
|
|
8
|
-
// part of version
|
|
9
|
-
var vnum1 = 0,
|
|
10
|
-
vnum2 = 0;
|
|
11
|
-
|
|
12
|
-
// loop until both string are
|
|
13
|
-
// processed
|
|
14
|
-
for (var i = 0, j = 0; i < v1.length || j < v2.length; ) {
|
|
15
|
-
// storing numeric part of
|
|
16
|
-
// version 1 in vnum1
|
|
17
|
-
while (i < v1.length && v1[i] !== ".") {
|
|
18
|
-
vnum1 = vnum1 * 10 + (v1[i] - "0");
|
|
19
|
-
i++;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// storing numeric part of
|
|
23
|
-
// version 2 in vnum2
|
|
24
|
-
while (j < v2.length && v2[j] !== ".") {
|
|
25
|
-
vnum2 = vnum2 * 10 + (v2[j] - "0");
|
|
26
|
-
j++;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (vnum1 > vnum2) return true;
|
|
30
|
-
if (vnum2 > vnum1) return false;
|
|
31
|
-
|
|
32
|
-
// if equal, reset variables and
|
|
33
|
-
// go for next numeric part
|
|
34
|
-
vnum1 = vnum2 = 0;
|
|
35
|
-
i++;
|
|
36
|
-
j++;
|
|
37
|
-
}
|
|
38
|
-
return true;
|
|
39
|
-
}
|
package/src/index.js
DELETED
|
@@ -1,467 +0,0 @@
|
|
|
1
|
-
import React, { createContext, useState, useEffect, useCallback } from "react";
|
|
2
|
-
import versionCompare from "./components/versionCompare";
|
|
3
|
-
import getDeviceFingerprint from "./components/FingerPrint";
|
|
4
|
-
import ErrorBoundary from "./components/ErrorBoundary";
|
|
5
|
-
// Notifications
|
|
6
|
-
import { ToastContainer, toast } from "react-toastify";
|
|
7
|
-
import "react-toastify/dist/ReactToastify.css";
|
|
8
|
-
import "./styling/toast.css";
|
|
9
|
-
|
|
10
|
-
export const SessionManager = createContext({
|
|
11
|
-
isLoggedIn: null,
|
|
12
|
-
header: null,
|
|
13
|
-
isAdmin: null,
|
|
14
|
-
userInfo: null,
|
|
15
|
-
refreshData: null,
|
|
16
|
-
setHeader: null,
|
|
17
|
-
setLoggedin: null,
|
|
18
|
-
setRefreshData: null,
|
|
19
|
-
hasRole: null,
|
|
20
|
-
deviceUID: null,
|
|
21
|
-
loadingUser: null,
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const SessionManagerProvider = ({
|
|
25
|
-
AuthenticatedAxiosObject,
|
|
26
|
-
refreshTimer,
|
|
27
|
-
dataRefresh,
|
|
28
|
-
userLoader,
|
|
29
|
-
refreshToken,
|
|
30
|
-
appVersion,
|
|
31
|
-
toastOptions,
|
|
32
|
-
children,
|
|
33
|
-
}) => {
|
|
34
|
-
// Set deivce UID
|
|
35
|
-
const [deviceUID, setDeviceUID] = useState(
|
|
36
|
-
localStorage.getItem("deviceUID") || false
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
if (!deviceUID) {
|
|
41
|
-
try {
|
|
42
|
-
const uid = getDeviceFingerprint();
|
|
43
|
-
if (uid) {
|
|
44
|
-
setDeviceUID(uid);
|
|
45
|
-
localStorage.setItem("deviceUID", uid);
|
|
46
|
-
AuthenticatedAxiosObject.defaults.headers.common["deviceUID"] = uid;
|
|
47
|
-
}
|
|
48
|
-
} catch (err) {
|
|
49
|
-
console.error("Failed to generate device fingerprint:", err);
|
|
50
|
-
// Generate a fallback UUID
|
|
51
|
-
const fallbackUID =
|
|
52
|
-
"fallback-" +
|
|
53
|
-
Date.now() +
|
|
54
|
-
"-" +
|
|
55
|
-
Math.random().toString(36).substr(2, 9);
|
|
56
|
-
setDeviceUID(fallbackUID);
|
|
57
|
-
localStorage.setItem("deviceUID", fallbackUID);
|
|
58
|
-
AuthenticatedAxiosObject.defaults.headers.common["deviceUID"] =
|
|
59
|
-
fallbackUID;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}, [deviceUID, AuthenticatedAxiosObject]);
|
|
63
|
-
|
|
64
|
-
// State to hold the selected header name
|
|
65
|
-
const [current, setCurrent] = useState("");
|
|
66
|
-
|
|
67
|
-
AuthenticatedAxiosObject.defaults.withCredentials = true;
|
|
68
|
-
AuthenticatedAxiosObject.defaults.headers.common["Authorization"] = current;
|
|
69
|
-
AuthenticatedAxiosObject.defaults.headers.common["deviceUID"] = deviceUID;
|
|
70
|
-
AuthenticatedAxiosObject.defaults.headers.common["appVersion"] = appVersion;
|
|
71
|
-
|
|
72
|
-
const fromPrevious = useCallback(
|
|
73
|
-
(auth, remember) => {
|
|
74
|
-
setCurrent(auth);
|
|
75
|
-
setTimeout(() => {
|
|
76
|
-
refreshToken()
|
|
77
|
-
.then((data) => {
|
|
78
|
-
if (data?.refreshed) {
|
|
79
|
-
const token = `Bearer ${data.access_token}`;
|
|
80
|
-
setCurrent(token);
|
|
81
|
-
AuthenticatedAxiosObject.defaults.headers.common[
|
|
82
|
-
"Authorization"
|
|
83
|
-
] = token;
|
|
84
|
-
if (remember) localStorage.setItem("Authorization", token);
|
|
85
|
-
sessionStorage.setItem("Authorization", token);
|
|
86
|
-
}
|
|
87
|
-
})
|
|
88
|
-
.catch((err) => {
|
|
89
|
-
console.error("Token refresh failed:", err);
|
|
90
|
-
|
|
91
|
-
// Clear invalid tokens
|
|
92
|
-
localStorage.removeItem("Authorization");
|
|
93
|
-
sessionStorage.removeItem("Authorization");
|
|
94
|
-
setCurrent("");
|
|
95
|
-
setCurrentLoggedIn(false);
|
|
96
|
-
|
|
97
|
-
// Only show toast for actual network/server errors, not auth failures
|
|
98
|
-
if (err?.code === "ERR_NETWORK" || err?.response?.status >= 500) {
|
|
99
|
-
// Don't spam with toasts during retries
|
|
100
|
-
const lastToast = sessionStorage.getItem("lastRefreshErrorToast");
|
|
101
|
-
const now = Date.now();
|
|
102
|
-
if (!lastToast || now - parseInt(lastToast) > 30000) {
|
|
103
|
-
// 30 seconds
|
|
104
|
-
toast.error(
|
|
105
|
-
"Unable to refresh your session. Please login again.",
|
|
106
|
-
{
|
|
107
|
-
toastId: "token-refresh-error",
|
|
108
|
-
icon: "🚪",
|
|
109
|
-
}
|
|
110
|
-
);
|
|
111
|
-
sessionStorage.setItem("lastRefreshErrorToast", now.toString());
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
}, 100);
|
|
116
|
-
},
|
|
117
|
-
[AuthenticatedAxiosObject, refreshToken]
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
useEffect(() => {
|
|
121
|
-
const local_auth = localStorage.getItem("Authorization");
|
|
122
|
-
const sess_auth = sessionStorage.getItem("Authorization");
|
|
123
|
-
|
|
124
|
-
if (local_auth) fromPrevious(local_auth, true);
|
|
125
|
-
else if (sess_auth) fromPrevious(sess_auth, false);
|
|
126
|
-
// No cleanup needed here since fromPrevious doesn't create intervals
|
|
127
|
-
}, [fromPrevious]);
|
|
128
|
-
|
|
129
|
-
// Wrap setHeader to store new header names in localStorage
|
|
130
|
-
const setHeader = (header) => {
|
|
131
|
-
setCurrent(header);
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const [currentLoggin, setCurrentLoggedIn] = useState(false);
|
|
135
|
-
const [isAdmin, setIsAdmin] = useState(false);
|
|
136
|
-
const [userInfo, setUserInfo] = useState({});
|
|
137
|
-
const [loadingUser, setLoadingUser] = useState(true);
|
|
138
|
-
|
|
139
|
-
useEffect(() => {
|
|
140
|
-
setTimeout(() => {
|
|
141
|
-
userLoader()
|
|
142
|
-
.then((res) => {
|
|
143
|
-
const data = res?.data;
|
|
144
|
-
if (data) {
|
|
145
|
-
setCurrentLoggedIn(data.logged_in);
|
|
146
|
-
setIsAdmin(data.is_admin);
|
|
147
|
-
setUserInfo(data.Info || {});
|
|
148
|
-
}
|
|
149
|
-
})
|
|
150
|
-
.catch((err) => {
|
|
151
|
-
console.error("User data loading failed:", err);
|
|
152
|
-
|
|
153
|
-
// Don't show toast for auth errors (401, 403)
|
|
154
|
-
if (err?.response?.status !== 401 && err?.response?.status !== 403) {
|
|
155
|
-
if (err?.code === "ERR_NETWORK" || err?.response?.status >= 500) {
|
|
156
|
-
toast.error(
|
|
157
|
-
"Unable to load user information. Some features may be limited.",
|
|
158
|
-
{
|
|
159
|
-
toastId: "user-load-error",
|
|
160
|
-
icon: "⚠️",
|
|
161
|
-
}
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
// Set safe defaults
|
|
166
|
-
setCurrentLoggedIn(false);
|
|
167
|
-
setIsAdmin(false);
|
|
168
|
-
setUserInfo({});
|
|
169
|
-
})
|
|
170
|
-
.finally(() => {
|
|
171
|
-
setLoadingUser(false);
|
|
172
|
-
});
|
|
173
|
-
}, 100);
|
|
174
|
-
}, [current, currentLoggin, userLoader]);
|
|
175
|
-
|
|
176
|
-
const setLoggedin = (status) => {
|
|
177
|
-
setCurrentLoggedIn(status);
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
useEffect(() => {
|
|
181
|
-
const tokenRefreshTimer = () => {
|
|
182
|
-
const mins = refreshTimer || 60;
|
|
183
|
-
return mins * 60 * 1000 || 10000;
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
let interval = null;
|
|
187
|
-
if (currentLoggin) {
|
|
188
|
-
const remember = localStorage.getItem("Authorization") ? true : false;
|
|
189
|
-
interval = setInterval(() => {
|
|
190
|
-
fromPrevious(sessionStorage.getItem("Authorization"), remember);
|
|
191
|
-
}, tokenRefreshTimer());
|
|
192
|
-
} else {
|
|
193
|
-
clearInterval(interval);
|
|
194
|
-
}
|
|
195
|
-
return () => clearInterval(interval);
|
|
196
|
-
}, [fromPrevious, currentLoggin, refreshTimer]);
|
|
197
|
-
|
|
198
|
-
useEffect(() => {
|
|
199
|
-
const customInterceptor = () => {
|
|
200
|
-
AuthenticatedAxiosObject.interceptors.response.use(
|
|
201
|
-
(response) => {
|
|
202
|
-
// Reset connection failure count on successful response
|
|
203
|
-
sessionStorage.setItem("connectionFailures", "0");
|
|
204
|
-
return response;
|
|
205
|
-
},
|
|
206
|
-
(error) => {
|
|
207
|
-
console.error("Axios interceptor caught error:", error);
|
|
208
|
-
|
|
209
|
-
// Handle session expiry (455)
|
|
210
|
-
if (error?.response?.status === 455) {
|
|
211
|
-
try {
|
|
212
|
-
const status = error.response.data?.logged_in || false;
|
|
213
|
-
if (!status) {
|
|
214
|
-
console.log("Session ended");
|
|
215
|
-
setCurrentLoggedIn(false);
|
|
216
|
-
AuthenticatedAxiosObject.defaults.headers.common[
|
|
217
|
-
"Authorization"
|
|
218
|
-
] = ``;
|
|
219
|
-
toast.info(
|
|
220
|
-
"Your session is no longer valid, please login again.",
|
|
221
|
-
{ toastId: "Forced_log_out", icon: "🚪" }
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
} catch (err) {
|
|
225
|
-
console.error("Error handling session expiry:", err);
|
|
226
|
-
// Don't throw here - just log and continue
|
|
227
|
-
}
|
|
228
|
-
return Promise.reject(error);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Handle version mismatch (426)
|
|
232
|
-
if (error?.response?.status === 426) {
|
|
233
|
-
try {
|
|
234
|
-
sessionStorage.setItem("appVersionOld", true);
|
|
235
|
-
sessionStorage.setItem(
|
|
236
|
-
"requiredVersion",
|
|
237
|
-
error.response.data?.minVersion || "unknown"
|
|
238
|
-
);
|
|
239
|
-
let reloads = parseInt(
|
|
240
|
-
sessionStorage.getItem("appReloads") || "0"
|
|
241
|
-
);
|
|
242
|
-
if (reloads < 2) {
|
|
243
|
-
sessionStorage.setItem("appReloads", (reloads + 1).toString());
|
|
244
|
-
setTimeout(() => {
|
|
245
|
-
window.location.reload();
|
|
246
|
-
}, 1000 * reloads);
|
|
247
|
-
} else {
|
|
248
|
-
toast.warning(
|
|
249
|
-
"The application needs to be updated. Please wait for some time then reload the page.",
|
|
250
|
-
{
|
|
251
|
-
toastId: "appReloadError",
|
|
252
|
-
icon: "🔄",
|
|
253
|
-
}
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
} catch (err) {
|
|
257
|
-
console.error("Error handling version mismatch:", err);
|
|
258
|
-
}
|
|
259
|
-
return Promise.reject(error);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Handle network errors and connection issues
|
|
263
|
-
if (!error?.response?.status) {
|
|
264
|
-
if (
|
|
265
|
-
error?.code !== "ERR_CANCELED" &&
|
|
266
|
-
error?.message !== "canceled"
|
|
267
|
-
) {
|
|
268
|
-
const errorMessage =
|
|
269
|
-
error?.code === "ERR_NETWORK"
|
|
270
|
-
? "Unable to connect to the server. Please check your internet connection and try again."
|
|
271
|
-
: "The server is not responding, please reload or try again later.";
|
|
272
|
-
|
|
273
|
-
// Only show toast after multiple failures (retries exhausted)
|
|
274
|
-
const failureCount = parseInt(
|
|
275
|
-
sessionStorage.getItem("connectionFailures") || "0"
|
|
276
|
-
);
|
|
277
|
-
sessionStorage.setItem(
|
|
278
|
-
"connectionFailures",
|
|
279
|
-
(failureCount + 1).toString()
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
// Show toast after 5 failed attempts (representing retry exhaustion)
|
|
283
|
-
if (failureCount >= 5) {
|
|
284
|
-
toast.error(errorMessage, {
|
|
285
|
-
toastId: `connection-failed-final`,
|
|
286
|
-
icon: "⚠️",
|
|
287
|
-
autoClose: 10000, // Keep it longer since this is the final error
|
|
288
|
-
});
|
|
289
|
-
// Reset counter after showing final toast
|
|
290
|
-
sessionStorage.setItem("connectionFailures", "0");
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
return Promise.reject(error);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// For all other errors, just reject without throwing
|
|
297
|
-
return Promise.reject(error);
|
|
298
|
-
}
|
|
299
|
-
);
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
// Call the interceptor setup
|
|
303
|
-
customInterceptor();
|
|
304
|
-
|
|
305
|
-
// Cleanup function to remove interceptors
|
|
306
|
-
return () => {
|
|
307
|
-
if (AuthenticatedAxiosObject?.interceptors?.response) {
|
|
308
|
-
AuthenticatedAxiosObject.interceptors.response.clear();
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
}, [AuthenticatedAxiosObject, currentLoggin, setCurrentLoggedIn]);
|
|
312
|
-
|
|
313
|
-
// We will use the below to refresh our data about the user when ever we flag refreshData as true
|
|
314
|
-
const [refreshData, setRefreshFlag] = useState(false);
|
|
315
|
-
|
|
316
|
-
useEffect(() => {
|
|
317
|
-
const refreshDelay = () => {
|
|
318
|
-
const mins = dataRefresh || 60;
|
|
319
|
-
return mins * 60 * 1000 || 10000;
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
const timer = setTimeout(() => setRefreshFlag(true), refreshDelay());
|
|
323
|
-
return () => clearTimeout(timer);
|
|
324
|
-
}, [dataRefresh]);
|
|
325
|
-
|
|
326
|
-
useEffect(() => {
|
|
327
|
-
if (refreshData) {
|
|
328
|
-
userLoader()
|
|
329
|
-
.then((res) => {
|
|
330
|
-
const data = res?.data;
|
|
331
|
-
if (data) {
|
|
332
|
-
setUserInfo(data.Info || {});
|
|
333
|
-
}
|
|
334
|
-
})
|
|
335
|
-
.catch((err) => {
|
|
336
|
-
console.error("User data refresh failed:", err);
|
|
337
|
-
// Silently fail for data refresh - don't overwhelm user with toasts
|
|
338
|
-
})
|
|
339
|
-
.finally(() => {
|
|
340
|
-
setRefreshData(false);
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
}, [refreshData, userLoader]);
|
|
344
|
-
|
|
345
|
-
const setRefreshData = (status) => {
|
|
346
|
-
setRefreshFlag(status);
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
// Check if user has specific role
|
|
350
|
-
const hasRole = (roles) => {
|
|
351
|
-
if (
|
|
352
|
-
!roles ||
|
|
353
|
-
!Array.isArray(roles) ||
|
|
354
|
-
!userInfo?.roles ||
|
|
355
|
-
!Array.isArray(userInfo.roles)
|
|
356
|
-
) {
|
|
357
|
-
return false;
|
|
358
|
-
}
|
|
359
|
-
try {
|
|
360
|
-
return roles.some((r) => userInfo.roles.indexOf(r) >= 0);
|
|
361
|
-
} catch (err) {
|
|
362
|
-
console.error("Error checking user roles:", err);
|
|
363
|
-
return false;
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
const contextValue = {
|
|
368
|
-
isLoggedIn: currentLoggin,
|
|
369
|
-
header: current,
|
|
370
|
-
isAdmin: isAdmin,
|
|
371
|
-
userInfo: userInfo,
|
|
372
|
-
refreshData: refreshData,
|
|
373
|
-
setHeader: setHeader,
|
|
374
|
-
setLoggedin: setLoggedin,
|
|
375
|
-
setRefreshData: setRefreshData,
|
|
376
|
-
hasRole: hasRole,
|
|
377
|
-
deviceUID: deviceUID,
|
|
378
|
-
loadingUser: loadingUser,
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
// Show loading state while initializing
|
|
382
|
-
return (
|
|
383
|
-
<ErrorBoundary>
|
|
384
|
-
<SessionManager.Provider value={contextValue}>
|
|
385
|
-
{loadingUser && (
|
|
386
|
-
<div
|
|
387
|
-
style={{
|
|
388
|
-
position: "fixed",
|
|
389
|
-
top: "50%",
|
|
390
|
-
left: "50%",
|
|
391
|
-
transform: "translate(-50%, -50%)",
|
|
392
|
-
zIndex: 9999,
|
|
393
|
-
}}
|
|
394
|
-
>
|
|
395
|
-
<div
|
|
396
|
-
style={{
|
|
397
|
-
width: "40px",
|
|
398
|
-
height: "40px",
|
|
399
|
-
border: "4px solid rgba(0,0,0,1)",
|
|
400
|
-
borderTop: "4px solid rgba(0,0,0,0.1)",
|
|
401
|
-
borderRadius: "50%",
|
|
402
|
-
animation: "spin 1s linear infinite",
|
|
403
|
-
}}
|
|
404
|
-
/>
|
|
405
|
-
<style>{`
|
|
406
|
-
@keyframes spin {
|
|
407
|
-
0% { transform: rotate(0deg); }
|
|
408
|
-
100% { transform: rotate(360deg); }
|
|
409
|
-
}
|
|
410
|
-
`}</style>
|
|
411
|
-
</div>
|
|
412
|
-
)}
|
|
413
|
-
<ToastContainer
|
|
414
|
-
position="top-left"
|
|
415
|
-
autoClose={5000}
|
|
416
|
-
closeOnClick
|
|
417
|
-
pauseOnFocusLoss
|
|
418
|
-
pauseOnHover
|
|
419
|
-
newestOnTop={false}
|
|
420
|
-
toastClassName={"custToast materialToast"}
|
|
421
|
-
{...toastOptions}
|
|
422
|
-
/>
|
|
423
|
-
<VersionProtection appVersion={appVersion} />
|
|
424
|
-
{children}
|
|
425
|
-
</SessionManager.Provider>
|
|
426
|
-
</ErrorBoundary>
|
|
427
|
-
);
|
|
428
|
-
};
|
|
429
|
-
|
|
430
|
-
function VersionProtection({ appVersion }) {
|
|
431
|
-
const [oldVersion, setOldVersion] = useState(false);
|
|
432
|
-
|
|
433
|
-
useEffect(() => {
|
|
434
|
-
try {
|
|
435
|
-
const isOldVersion = sessionStorage.getItem("appVersionOld") === "true";
|
|
436
|
-
setOldVersion(isOldVersion);
|
|
437
|
-
|
|
438
|
-
if (
|
|
439
|
-
isOldVersion &&
|
|
440
|
-
sessionStorage.getItem("requiredVersion") &&
|
|
441
|
-
appVersion &&
|
|
442
|
-
versionCompare(appVersion, sessionStorage.getItem("requiredVersion"))
|
|
443
|
-
) {
|
|
444
|
-
console.log("Update Success Toast");
|
|
445
|
-
toast.success("Your application has been updated", {
|
|
446
|
-
toastId: "appReload",
|
|
447
|
-
icon: "✅",
|
|
448
|
-
onClose: () => {
|
|
449
|
-
try {
|
|
450
|
-
sessionStorage.removeItem("appVersionOld");
|
|
451
|
-
sessionStorage.removeItem("requiredVersion");
|
|
452
|
-
sessionStorage.removeItem("appReloads");
|
|
453
|
-
} catch (err) {
|
|
454
|
-
console.error("Error cleaning up version storage:", err);
|
|
455
|
-
}
|
|
456
|
-
},
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
} catch (err) {
|
|
460
|
-
console.error("Error in version protection:", err);
|
|
461
|
-
}
|
|
462
|
-
}, [appVersion]);
|
|
463
|
-
|
|
464
|
-
return <></>;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
export default SessionManagerProvider;
|