@visns-studio/visns-components 5.4.7 → 5.4.9

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.
@@ -14,11 +14,12 @@ const TwoFactorAuth = ({ logo, setSystemAuth, setUserProfile }) => {
14
14
  const navigate = useNavigate();
15
15
  const location = useLocation();
16
16
  const [isLoading, setIsLoading] = useState(false);
17
-
17
+
18
18
  // Get user data from location state
19
19
  const userData = location.state?.userData || null;
20
20
  const previousUrl = location.state?.previousUrl || '';
21
-
21
+ const remember = location.state?.remember || false;
22
+
22
23
  // If no userData, redirect to login
23
24
  useEffect(() => {
24
25
  if (!userData) {
@@ -31,82 +32,225 @@ const TwoFactorAuth = ({ logo, setSystemAuth, setUserProfile }) => {
31
32
 
32
33
  const handleChange = (e) => {
33
34
  if (e) {
34
- setVerificationCode(e.target.value);
35
- setCodeClass('');
35
+ const value = e.target.value;
36
+ // Only allow digits and limit to 6 characters
37
+ if (/^\d*$/.test(value) && value.length <= 6) {
38
+ setVerificationCode(value);
39
+ setCodeClass('');
40
+ }
36
41
  }
37
42
  };
38
43
 
39
44
  const handleSubmit = (e) => {
40
45
  if (e) {
41
46
  e.preventDefault();
42
-
43
- if (!verificationCode.trim()) {
47
+
48
+ if (!verificationCode.trim() || verificationCode.length !== 6) {
44
49
  setCodeClass('inputError');
45
- toast.error('Please enter the verification code');
50
+ toast.error('Please enter a valid 6-digit code');
46
51
  return;
47
52
  }
48
-
53
+
49
54
  setIsLoading(true);
50
55
  toast.info('Verifying code...', { autoClose: 2000 });
51
56
 
52
57
  CustomFetch(
53
- '/login/verify-2fa',
58
+ '/login/two-factor-challenge',
54
59
  'POST',
55
60
  {
56
- user_id: userData?.id,
57
- verification_code: verificationCode,
58
- previous_url: previousUrl
61
+ code: verificationCode,
62
+ previous_url: previousUrl,
63
+ remember: remember,
59
64
  },
60
65
  (result) => {
61
66
  setIsLoading(false);
62
- if (result.error === '') {
63
- toast.success('Two-factor authentication successful');
64
- setUserProfile(result.user || userData);
65
67
 
66
- if (result.previous && result.previous !== '') {
67
- window.location.href = result.previous;
68
+ // Log the result to help with debugging
69
+ console.log('2FA Result:', result);
70
+
71
+ // Check if the result contains an error message
72
+ if (result && result.error) {
73
+ setCodeClass('inputError');
74
+ console.log('Found error in result:', result.error);
75
+
76
+ // Check if it's a session timeout error
77
+ if (
78
+ result.error ===
79
+ 'Invalid two-factor authentication session.'
80
+ ) {
81
+ console.log(
82
+ 'Session timeout detected, redirecting to login'
83
+ );
84
+ toast.error(
85
+ 'Your session has expired. Please log in again.'
86
+ );
87
+
88
+ // Redirect to login page
89
+ setTimeout(() => {
90
+ navigate('/login');
91
+ }, 1500); // Short delay to allow the toast to be seen
68
92
  } else {
69
- setSystemAuth(true);
70
- navigate('/');
93
+ // Display the error message
94
+ toast.error(result.error);
71
95
  }
72
- } else {
96
+ return;
97
+ }
98
+
99
+ // Additional check for error in different format
100
+ if (
101
+ typeof result === 'string' &&
102
+ result.includes(
103
+ 'Invalid two-factor authentication session'
104
+ )
105
+ ) {
106
+ console.log(
107
+ 'Session timeout detected in string result, redirecting to login'
108
+ );
73
109
  setCodeClass('inputError');
74
- toast.error(result.error || 'Invalid verification code');
110
+ toast.error(
111
+ 'Your session has expired. Please log in again.'
112
+ );
113
+
114
+ // Redirect to login page
115
+ setTimeout(() => {
116
+ navigate('/login');
117
+ }, 1500);
118
+ return;
75
119
  }
120
+
121
+ // If we get here, it means we received a 200 response with the user object
122
+ console.log(
123
+ 'Authentication successful, updating user profile'
124
+ );
125
+ toast.success('Two-factor authentication successful');
126
+
127
+ // Update the user profile with the received data
128
+ setUserProfile(result);
129
+
130
+ // Set authentication state to true
131
+ setSystemAuth(true);
132
+
133
+ // Use a small delay to ensure state updates are processed
134
+ setTimeout(() => {
135
+ // Navigate based on previous URL if available
136
+ if (previousUrl && previousUrl !== '') {
137
+ window.location.href = previousUrl;
138
+ } else {
139
+ // Use window.location for a full page refresh to ensure session is recognized
140
+ window.location.href = '/';
141
+ }
142
+ }, 500);
76
143
  },
77
144
  (error) => {
78
145
  setIsLoading(false);
79
146
  setCodeClass('inputError');
80
- toast.error(String(error) || 'Failed to verify code');
81
- }
82
- );
83
- }
84
- };
85
147
 
86
- const handleResendCode = () => {
87
- if (!userData?.id) {
88
- toast.error('User information is missing. Please try logging in again.');
89
- navigate('/login');
90
- return;
91
- }
148
+ // Log the error to help with debugging
149
+ console.log('2FA Error:', error);
150
+
151
+ // Log more detailed information about the error response
152
+ if (error.response) {
153
+ console.log('Error Response:', error.response);
154
+ console.log(
155
+ 'Error Response Data:',
156
+ error.response.data
157
+ );
158
+ }
159
+
160
+ // Extract the error message from the response
161
+ let errorMessage =
162
+ 'Invalid verification code. Please try again.';
163
+
164
+ // Direct check for the specific error we're looking for
165
+ // This is a temporary solution to force the redirect
166
+ if (
167
+ error &&
168
+ error.message === 'Request failed with status code 401'
169
+ ) {
170
+ // Force redirect to login for 401 errors
171
+ console.log('Detected 401 error, redirecting to login');
172
+ toast.error(
173
+ 'Your session has expired. Please log in again.'
174
+ );
175
+ setTimeout(() => {
176
+ navigate('/login');
177
+ }, 1500);
178
+ return;
179
+ }
180
+
181
+ try {
182
+ // Try to parse the error response
183
+ if (error.response && error.response.data) {
184
+ console.log(
185
+ 'Response data type:',
186
+ typeof error.response.data
187
+ );
188
+ console.log('Response data:', error.response.data);
92
189
 
93
- toast.info('Requesting new verification code...', { autoClose: 2000 });
94
-
95
- CustomFetch(
96
- '/login/resend-2fa',
97
- 'POST',
98
- { user_id: userData.id },
99
- (result) => {
100
- if (result.error === '') {
101
- toast.success('A new verification code has been sent');
102
- } else {
103
- toast.error(result.error || 'Failed to send new code');
190
+ // If data is a string, use it directly
191
+ if (typeof error.response.data === 'string') {
192
+ errorMessage = error.response.data;
193
+ }
194
+ // If data is an object with an error property
195
+ else if (typeof error.response.data === 'object') {
196
+ if (error.response.data.error) {
197
+ errorMessage = error.response.data.error;
198
+ console.log(
199
+ 'Found error in object:',
200
+ errorMessage
201
+ );
202
+ } else if (error.response.data.message) {
203
+ errorMessage = error.response.data.message;
204
+ console.log(
205
+ 'Found message in object:',
206
+ errorMessage
207
+ );
208
+ }
209
+ }
210
+ // If data is JSON string, try to parse it
211
+ else if (
212
+ typeof error.response.data === 'string' &&
213
+ error.response.data.includes('{')
214
+ ) {
215
+ const parsedData = JSON.parse(
216
+ error.response.data
217
+ );
218
+ if (parsedData.error) {
219
+ errorMessage = parsedData.error;
220
+ console.log(
221
+ 'Found error in parsed JSON:',
222
+ errorMessage
223
+ );
224
+ }
225
+ }
226
+ }
227
+ } catch (e) {
228
+ console.error('Error parsing error response:', e);
229
+ }
230
+
231
+ console.log('Extracted error message:', errorMessage);
232
+
233
+ // Check if it's a session timeout error
234
+ if (
235
+ errorMessage ===
236
+ 'Invalid two-factor authentication session.' ||
237
+ errorMessage.includes(
238
+ 'Invalid two-factor authentication session'
239
+ )
240
+ ) {
241
+ toast.error(
242
+ 'Your session has expired. Please log in again.'
243
+ );
244
+ // Redirect to login page
245
+ setTimeout(() => {
246
+ navigate('/login');
247
+ }, 1500); // Short delay to allow the toast to be seen
248
+ } else {
249
+ toast.error(errorMessage);
250
+ }
104
251
  }
105
- },
106
- (error) => {
107
- toast.error(String(error) || 'Failed to send new code');
108
- }
109
- );
252
+ );
253
+ }
110
254
  };
111
255
 
112
256
  return (
@@ -121,13 +265,18 @@ const TwoFactorAuth = ({ logo, setSystemAuth, setUserProfile }) => {
121
265
  alt="CRM"
122
266
  className={styles.loginlogo}
123
267
  />
124
- <div className={`${styles.formItem} ${styles.fwItem}`}>
268
+ <div
269
+ className={`${styles.formItem} ${styles.fwItem}`}
270
+ >
125
271
  <h2>Two-Factor Authentication</h2>
126
272
  <p className={styles.instructions}>
127
- Please enter the verification code that was sent to your device.
273
+ Please enter the 6-digit code from your
274
+ authenticator app.
128
275
  </p>
129
276
  </div>
130
- <div className={`${styles.formItem} ${styles.fwItem}`}>
277
+ <div
278
+ className={`${styles.formItem} ${styles.fwItem}`}
279
+ >
131
280
  <label className={styles.fi__label}>
132
281
  <input
133
282
  type="text"
@@ -138,17 +287,23 @@ const TwoFactorAuth = ({ logo, setSystemAuth, setUserProfile }) => {
138
287
  className={codeClass}
139
288
  placeholder=" "
140
289
  autoComplete="one-time-code"
290
+ inputMode="numeric"
141
291
  maxLength="6"
142
292
  />
143
293
  <span className={styles.fi__span}>
144
294
  Verification Code
145
295
  </span>
146
296
  <small>
147
- <LockOff strokeWidth={2} size={18} />
297
+ <LockOff
298
+ strokeWidth={2}
299
+ size={18}
300
+ />
148
301
  </small>
149
302
  </label>
150
303
  </div>
151
- <div className={`${styles.formItem} ${styles.fwItem} ${styles.lastItem}`}>
304
+ <div
305
+ className={`${styles.formItem} ${styles.fwItem} ${styles.lastItem}`}
306
+ >
152
307
  <button
153
308
  className={styles.btn}
154
309
  type="submit"
@@ -157,17 +312,25 @@ const TwoFactorAuth = ({ logo, setSystemAuth, setUserProfile }) => {
157
312
  {isLoading ? 'Verifying...' : 'Verify'}
158
313
  </button>
159
314
  </div>
160
- <div className={`${styles.formItem} ${styles.fwItem} ${styles.lastItem} ${styles.forgotten}`}>
315
+ <div
316
+ className={`${styles.formItem} ${styles.fwItem} ${styles.lastItem} ${styles.forgotten}`}
317
+ >
161
318
  <span>
162
- Didn't receive a code?{' '}
163
- <a href="#" onClick={(e) => { e.preventDefault(); handleResendCode(); }}>
164
- Resend Code
165
- </a>
319
+ Having trouble? Contact your
320
+ administrator for assistance.
166
321
  </span>
167
322
  </div>
168
- <div className={`${styles.formItem} ${styles.fwItem} ${styles.lastItem} ${styles.forgotten}`}>
323
+ <div
324
+ className={`${styles.formItem} ${styles.fwItem} ${styles.lastItem} ${styles.forgotten}`}
325
+ >
169
326
  <span>
170
- <a href="#" onClick={(e) => { e.preventDefault(); navigate('/login'); }}>
327
+ <a
328
+ href="#"
329
+ onClick={(e) => {
330
+ e.preventDefault();
331
+ navigate('/login');
332
+ }}
333
+ >
171
334
  Back to Login
172
335
  </a>
173
336
  </span>
@@ -2,6 +2,7 @@
2
2
  width: 50%;
3
3
  position: relative;
4
4
  box-sizing: border-box;
5
+ margin-bottom: 0.5rem;
5
6
 
6
7
  .fi__label {
7
8
  position: relative;
@@ -118,6 +119,41 @@
118
119
  }
119
120
  }
120
121
 
122
+ .rememberMeContainer {
123
+ display: flex;
124
+ align-items: center;
125
+ justify-content: flex-start;
126
+ width: 100%;
127
+ padding: 0.5rem 1rem;
128
+ margin-bottom: 0.75rem;
129
+ box-sizing: border-box;
130
+ margin-top: -0.25rem;
131
+ }
132
+
133
+ .rememberMeCheckboxWrapper {
134
+ display: flex;
135
+ align-items: center;
136
+ justify-content: center;
137
+ margin-right: 0.5rem;
138
+ }
139
+
140
+ .rememberMeLabel {
141
+ cursor: pointer;
142
+ user-select: none;
143
+ color: var(--paragraph-color);
144
+ font-size: 0.85rem;
145
+ white-space: nowrap;
146
+ }
147
+
148
+ .rememberMeCheckbox {
149
+ /* Use the browser's default checkbox but with some styling */
150
+ width: 16px;
151
+ height: 16px;
152
+ cursor: pointer;
153
+ position: relative;
154
+ accent-color: var(--primary-color); /* Modern browsers support this */
155
+ }
156
+
121
157
  .lcontainer {
122
158
  min-height: 100vh;
123
159
  display: flex;