@xouteiro/auth_npm 1.0.10 → 1.0.12
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/package.json +1 -1
- package/src/components/LoginRegisterFlow.jsx +187 -61
package/package.json
CHANGED
|
@@ -2,19 +2,75 @@ import React, { useState } from 'react';
|
|
|
2
2
|
import { signInWithEmail } from '../login';
|
|
3
3
|
import { registerWithEmail } from '../register';
|
|
4
4
|
|
|
5
|
+
// SVG Eye Icon
|
|
6
|
+
const EyeIcon = ({ open }) => (
|
|
7
|
+
<svg
|
|
8
|
+
width="22"
|
|
9
|
+
height="22"
|
|
10
|
+
viewBox="0 0 22 22"
|
|
11
|
+
fill="none"
|
|
12
|
+
style={{ verticalAlign: 'middle' }}
|
|
13
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
14
|
+
>
|
|
15
|
+
{open ? (
|
|
16
|
+
<>
|
|
17
|
+
<path
|
|
18
|
+
d="M1 11C3.5 5.5 11 2 21 11C18.5 16.5 11 20 1 11Z"
|
|
19
|
+
stroke="#0070f3"
|
|
20
|
+
strokeWidth="2"
|
|
21
|
+
fill="none"
|
|
22
|
+
/>
|
|
23
|
+
<circle
|
|
24
|
+
cx="11"
|
|
25
|
+
cy="11"
|
|
26
|
+
r="3"
|
|
27
|
+
stroke="#0070f3"
|
|
28
|
+
strokeWidth="2"
|
|
29
|
+
fill="none"
|
|
30
|
+
/>
|
|
31
|
+
</>
|
|
32
|
+
) : (
|
|
33
|
+
<>
|
|
34
|
+
<path
|
|
35
|
+
d="M1 11C3.5 5.5 11 2 21 11C18.5 16.5 11 20 1 11Z"
|
|
36
|
+
stroke="#0070f3"
|
|
37
|
+
strokeWidth="2"
|
|
38
|
+
fill="none"
|
|
39
|
+
/>
|
|
40
|
+
<circle
|
|
41
|
+
cx="11"
|
|
42
|
+
cy="11"
|
|
43
|
+
r="3"
|
|
44
|
+
stroke="#0070f3"
|
|
45
|
+
strokeWidth="2"
|
|
46
|
+
fill="none"
|
|
47
|
+
/>
|
|
48
|
+
<line
|
|
49
|
+
x1="4"
|
|
50
|
+
y1="18"
|
|
51
|
+
x2="18"
|
|
52
|
+
y2="4"
|
|
53
|
+
stroke="#d32f2f"
|
|
54
|
+
strokeWidth="2"
|
|
55
|
+
/>
|
|
56
|
+
</>
|
|
57
|
+
)}
|
|
58
|
+
</svg>
|
|
59
|
+
);
|
|
60
|
+
|
|
5
61
|
const boxStyle = {
|
|
6
|
-
maxWidth:
|
|
7
|
-
margin: '
|
|
8
|
-
padding:
|
|
9
|
-
borderRadius:
|
|
10
|
-
boxShadow: '0
|
|
62
|
+
maxWidth: 400,
|
|
63
|
+
margin: '48px auto',
|
|
64
|
+
padding: 36,
|
|
65
|
+
borderRadius: 18,
|
|
66
|
+
boxShadow: '0 4px 24px rgba(0,0,0,0.10)',
|
|
11
67
|
background: '#fff',
|
|
12
|
-
fontFamily: 'sans-serif',
|
|
68
|
+
fontFamily: 'Inter, sans-serif',
|
|
13
69
|
};
|
|
14
70
|
|
|
15
71
|
const tabStyle = (active) => ({
|
|
16
72
|
flex: 1,
|
|
17
|
-
padding:
|
|
73
|
+
padding: 14,
|
|
18
74
|
border: 'none',
|
|
19
75
|
borderBottom: active ? '3px solid #0070f3' : '3px solid #eee',
|
|
20
76
|
background: 'none',
|
|
@@ -22,56 +78,89 @@ const tabStyle = (active) => ({
|
|
|
22
78
|
color: active ? '#0070f3' : '#444',
|
|
23
79
|
cursor: 'pointer',
|
|
24
80
|
outline: 'none',
|
|
81
|
+
fontSize: 18,
|
|
82
|
+
transition: 'border-bottom 0.2s',
|
|
25
83
|
});
|
|
26
84
|
|
|
27
85
|
const inputStyle = {
|
|
28
86
|
width: '100%',
|
|
29
|
-
padding:
|
|
30
|
-
margin: '
|
|
31
|
-
borderRadius:
|
|
32
|
-
border: '
|
|
33
|
-
fontSize:
|
|
87
|
+
padding: '12px 44px 12px 12px',
|
|
88
|
+
margin: '12px 0',
|
|
89
|
+
borderRadius: 8,
|
|
90
|
+
border: '1.5px solid #cfd8dc',
|
|
91
|
+
fontSize: 17,
|
|
92
|
+
background: '#f8fafc',
|
|
93
|
+
boxSizing: 'border-box',
|
|
94
|
+
outline: 'none',
|
|
95
|
+
transition: 'border 0.2s',
|
|
34
96
|
};
|
|
35
97
|
|
|
36
98
|
const buttonStyle = {
|
|
37
99
|
width: '100%',
|
|
38
|
-
padding:
|
|
39
|
-
borderRadius:
|
|
100
|
+
padding: 14,
|
|
101
|
+
borderRadius: 8,
|
|
40
102
|
border: 'none',
|
|
41
|
-
background: '#0070f3',
|
|
103
|
+
background: 'linear-gradient(90deg, #0070f3 60%, #0059c1 100%)',
|
|
42
104
|
color: '#fff',
|
|
43
|
-
fontWeight:
|
|
44
|
-
fontSize:
|
|
45
|
-
marginTop:
|
|
105
|
+
fontWeight: 700,
|
|
106
|
+
fontSize: 18,
|
|
107
|
+
marginTop: 16,
|
|
108
|
+
cursor: 'pointer',
|
|
109
|
+
letterSpacing: 1,
|
|
110
|
+
boxShadow: '0 2px 8px rgba(0,112,243,0.08)',
|
|
111
|
+
transition: 'background 0.2s',
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const toggleButtonStyle = {
|
|
115
|
+
position: 'absolute',
|
|
116
|
+
right: 12,
|
|
117
|
+
top: '50%',
|
|
118
|
+
transform: 'translateY(-50%)',
|
|
119
|
+
background: 'none',
|
|
120
|
+
border: 'none',
|
|
121
|
+
color: '#0070f3',
|
|
46
122
|
cursor: 'pointer',
|
|
123
|
+
fontSize: 18,
|
|
124
|
+
padding: 0,
|
|
125
|
+
display: 'flex',
|
|
126
|
+
alignItems: 'center',
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const inputWrapperStyle = {
|
|
130
|
+
position: 'relative',
|
|
131
|
+
width: '100%',
|
|
47
132
|
};
|
|
48
133
|
|
|
49
134
|
const messageStyle = (success) => ({
|
|
50
|
-
margin: '
|
|
135
|
+
margin: '16px 0 0 0',
|
|
51
136
|
color: success ? '#0a0' : '#d32f2f',
|
|
52
137
|
background: success ? '#eafbe7' : '#fdeaea',
|
|
53
|
-
padding:
|
|
54
|
-
borderRadius:
|
|
138
|
+
padding: 10,
|
|
139
|
+
borderRadius: 8,
|
|
55
140
|
textAlign: 'center',
|
|
56
|
-
fontSize:
|
|
141
|
+
fontSize: 16,
|
|
142
|
+
fontWeight: 500,
|
|
143
|
+
letterSpacing: 0.2,
|
|
57
144
|
});
|
|
58
145
|
|
|
59
146
|
export default function LoginRegisterFlow({ onLoginSuccess, onRegisterSuccess }) {
|
|
60
147
|
const [mode, setMode] = useState('login');
|
|
61
148
|
const [identifier, setIdentifier] = useState('');
|
|
62
149
|
const [password, setPassword] = useState('');
|
|
63
|
-
const [
|
|
64
|
-
const [phone, setPhone] = useState('');
|
|
150
|
+
const [confirmPassword, setConfirmPassword] = useState('');
|
|
65
151
|
const [message, setMessage] = useState(null);
|
|
66
152
|
const [success, setSuccess] = useState(false);
|
|
153
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
154
|
+
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
|
67
155
|
|
|
68
156
|
const resetFields = () => {
|
|
69
157
|
setIdentifier('');
|
|
70
158
|
setPassword('');
|
|
71
|
-
|
|
72
|
-
setPhone('');
|
|
159
|
+
setConfirmPassword('');
|
|
73
160
|
setMessage(null);
|
|
74
161
|
setSuccess(false);
|
|
162
|
+
setShowPassword(false);
|
|
163
|
+
setShowConfirmPassword(false);
|
|
75
164
|
};
|
|
76
165
|
|
|
77
166
|
const handleLogin = async (e) => {
|
|
@@ -94,7 +183,12 @@ export default function LoginRegisterFlow({ onLoginSuccess, onRegisterSuccess })
|
|
|
94
183
|
e.preventDefault();
|
|
95
184
|
setMessage(null);
|
|
96
185
|
setSuccess(false);
|
|
97
|
-
|
|
186
|
+
if (password !== confirmPassword) {
|
|
187
|
+
setMessage("Passwords do not match");
|
|
188
|
+
setSuccess(false);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const { user, error } = await registerWithEmail(identifier, password);
|
|
98
192
|
if (error) {
|
|
99
193
|
setMessage(error.message || 'Registration failed');
|
|
100
194
|
setSuccess(false);
|
|
@@ -108,32 +202,45 @@ export default function LoginRegisterFlow({ onLoginSuccess, onRegisterSuccess })
|
|
|
108
202
|
|
|
109
203
|
return (
|
|
110
204
|
<div style={boxStyle}>
|
|
111
|
-
<div style={{ display: 'flex', marginBottom:
|
|
205
|
+
<div style={{ display: 'flex', marginBottom: 28 }}>
|
|
112
206
|
<button style={tabStyle(mode === 'login')} onClick={() => { setMode('login'); setMessage(null); }}>Login</button>
|
|
113
207
|
<button style={tabStyle(mode === 'register')} onClick={() => { setMode('register'); setMessage(null); }}>Register</button>
|
|
114
208
|
</div>
|
|
115
209
|
{mode === 'login' ? (
|
|
116
|
-
<form onSubmit={handleLogin}>
|
|
210
|
+
<form onSubmit={handleLogin} autoComplete="off">
|
|
117
211
|
<input
|
|
118
212
|
style={inputStyle}
|
|
119
213
|
type="text"
|
|
120
|
-
placeholder="Email
|
|
214
|
+
placeholder="Email"
|
|
121
215
|
value={identifier}
|
|
122
216
|
onChange={e => setIdentifier(e.target.value)}
|
|
123
217
|
required
|
|
218
|
+
autoComplete="username"
|
|
124
219
|
/>
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
220
|
+
<div style={inputWrapperStyle}>
|
|
221
|
+
<input
|
|
222
|
+
style={inputStyle}
|
|
223
|
+
type={showPassword ? "text" : "password"}
|
|
224
|
+
placeholder="Password"
|
|
225
|
+
value={password}
|
|
226
|
+
onChange={e => setPassword(e.target.value)}
|
|
227
|
+
required
|
|
228
|
+
autoComplete="current-password"
|
|
229
|
+
/>
|
|
230
|
+
<button
|
|
231
|
+
type="button"
|
|
232
|
+
style={toggleButtonStyle}
|
|
233
|
+
tabIndex={-1}
|
|
234
|
+
aria-label={showPassword ? "Hide password" : "Show password"}
|
|
235
|
+
onClick={() => setShowPassword(v => !v)}
|
|
236
|
+
>
|
|
237
|
+
<EyeIcon open={showPassword} />
|
|
238
|
+
</button>
|
|
239
|
+
</div>
|
|
133
240
|
<button style={buttonStyle} type="submit">Login</button>
|
|
134
241
|
</form>
|
|
135
242
|
) : (
|
|
136
|
-
<form onSubmit={handleRegister}>
|
|
243
|
+
<form onSubmit={handleRegister} autoComplete="off">
|
|
137
244
|
<input
|
|
138
245
|
style={inputStyle}
|
|
139
246
|
type="text"
|
|
@@ -141,29 +248,48 @@ export default function LoginRegisterFlow({ onLoginSuccess, onRegisterSuccess })
|
|
|
141
248
|
value={identifier}
|
|
142
249
|
onChange={e => setIdentifier(e.target.value)}
|
|
143
250
|
required
|
|
251
|
+
autoComplete="username"
|
|
144
252
|
/>
|
|
145
|
-
<
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
253
|
+
<div style={inputWrapperStyle}>
|
|
254
|
+
<input
|
|
255
|
+
style={inputStyle}
|
|
256
|
+
type={showPassword ? "text" : "password"}
|
|
257
|
+
placeholder="Password"
|
|
258
|
+
value={password}
|
|
259
|
+
onChange={e => setPassword(e.target.value)}
|
|
260
|
+
required
|
|
261
|
+
autoComplete="new-password"
|
|
262
|
+
/>
|
|
263
|
+
<button
|
|
264
|
+
type="button"
|
|
265
|
+
style={toggleButtonStyle}
|
|
266
|
+
tabIndex={-1}
|
|
267
|
+
aria-label={showPassword ? "Hide password" : "Show password"}
|
|
268
|
+
onClick={() => setShowPassword(v => !v)}
|
|
269
|
+
>
|
|
270
|
+
<EyeIcon open={showPassword} />
|
|
271
|
+
</button>
|
|
272
|
+
</div>
|
|
273
|
+
<div style={inputWrapperStyle}>
|
|
274
|
+
<input
|
|
275
|
+
style={inputStyle}
|
|
276
|
+
type={showConfirmPassword ? "text" : "password"}
|
|
277
|
+
placeholder="Confirm Password"
|
|
278
|
+
value={confirmPassword}
|
|
279
|
+
onChange={e => setConfirmPassword(e.target.value)}
|
|
280
|
+
required
|
|
281
|
+
autoComplete="new-password"
|
|
282
|
+
/>
|
|
283
|
+
<button
|
|
284
|
+
type="button"
|
|
285
|
+
style={toggleButtonStyle}
|
|
286
|
+
tabIndex={-1}
|
|
287
|
+
aria-label={showConfirmPassword ? "Hide confirm password" : "Show confirm password"}
|
|
288
|
+
onClick={() => setShowConfirmPassword(v => !v)}
|
|
289
|
+
>
|
|
290
|
+
<EyeIcon open={showConfirmPassword} />
|
|
291
|
+
</button>
|
|
292
|
+
</div>
|
|
167
293
|
<button style={buttonStyle} type="submit">Register</button>
|
|
168
294
|
</form>
|
|
169
295
|
)}
|