amaye15 commited on
Commit
e349c1a
·
1 Parent(s): 8376eb2
Files changed (3) hide show
  1. static/css/style.css +296 -265
  2. static/js/script.js +119 -118
  3. templates/index.html +65 -65
static/css/style.css CHANGED
@@ -1,284 +1,315 @@
1
- /* Default variables for light mode */
2
  :root {
3
- --background-color: #f4f4f4;
4
- --text-color: #333333;
5
- --card-background: #ffffff;
6
- --input-background: #f0f2f5;
7
- --input-border: #e0e3e9;
8
- --input-text: #333333;
9
- --button-primary-bg: #5cb85c;
10
- --button-primary-text: white;
11
- --button-secondary-bg: #e0e3e9;
12
- --button-secondary-text: #4a5568;
13
- --border-color: #ddd;
14
- --separator-color: #e0e3e9;
15
- --error-color: #d9534f;
16
- --success-color: #5cb85c;
17
- --link-color: #3182ce;
18
- --header-color: #333;
19
- --hero-bg: #e7f3fe;
20
- --hero-color: #333;
21
- --welcome-bg: #e7f3fe;
22
- --logout-button-bg: #d9534f;
23
- --logout-button-hover: #c9302c;
24
- }
25
-
26
- /* Dark mode variables */
27
- @media (prefers-color-scheme: dark) {
28
- :root {
29
- --background-color: #1a202c;
30
- --text-color: #e2e8f0;
31
- --card-background: #2d3748;
32
- --input-background: #4a5568;
33
- --input-border: #4a5568;
34
- --input-text: #e2e8f0;
35
- --button-primary-bg: #6cb46c;
36
- --button-primary-text: white;
37
- --button-secondary-bg: #4a5568;
38
- --button-secondary-text: #e2e8f0;
39
- --border-color: #4a5568;
40
- --separator-color: #4a5568;
41
- --error-color: #fc8181;
42
- --success-color: #68d391;
43
- --link-color: #63b3ed;
44
- --header-color: #e2e8f0;
45
- --hero-bg: #2d3748;
46
- --hero-color: #e2e8f0;
47
- --welcome-bg: #2d3748;
48
- --logout-button-bg: #e53e3e;
49
- --logout-button-hover: #c53030;
50
- }
51
- }
52
-
53
- /* Base styles */
54
- * {
55
  box-sizing: border-box;
56
  margin: 0;
57
  padding: 0;
58
- }
59
-
60
- body {
61
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
62
- background-color: var(--background-color);
63
- color: var(--text-color);
64
  line-height: 1.6;
65
- transition: background-color 0.3s ease, color 0.3s ease;
66
- margin: 0;
67
- padding: 0;
68
- }
69
-
70
- h1, h2 {
71
- color: var(--header-color);
72
- }
73
-
74
- .container {
75
- width: 100%;
76
- max-width: 1000px;
77
- margin: 20px auto;
78
- padding: 20px;
79
- }
80
-
81
- .logo {
82
  text-align: center;
83
- margin-bottom: 30px;
84
- }
85
-
86
- .logo h1 {
87
- font-size: 2rem;
88
- margin-bottom: 10px;
89
- }
90
-
91
- .logo p {
 
 
 
 
92
  font-size: 1.1rem;
93
- opacity: 0.8;
94
- }
95
-
96
- /* Auth container styles */
97
- .auth-container {
98
- display: flex;
99
- flex-direction: column;
100
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
101
- border-radius: 8px;
102
- overflow: hidden;
103
- background-color: var(--card-background);
104
- transition: box-shadow 0.3s ease, background-color 0.3s ease;
105
- margin-bottom: 30px;
106
- }
107
-
108
- @media (min-width: 768px) {
109
- .auth-container {
110
- flex-direction: row;
111
- height: 500px;
112
- }
113
- }
114
-
115
- .tabs {
116
- display: flex;
117
- border-bottom: 1px solid var(--separator-color);
118
- }
119
-
120
- .tab {
121
- padding: 15px 0;
122
- flex: 1;
123
- text-align: center;
124
- cursor: pointer;
125
  font-weight: 600;
126
- transition: background-color 0.3s ease;
127
- }
128
-
129
- .tab.active {
130
- border-bottom: 3px solid var(--button-primary-bg);
131
- color: var(--button-primary-bg);
132
- }
133
-
134
- .auth-forms {
135
- display: flex;
136
- flex: 1;
137
- flex-direction: column;
138
- padding: 30px;
139
- }
140
-
141
- .auth-form {
142
- display: none;
143
- }
144
-
145
- .auth-form.active {
146
  display: block;
147
- }
148
-
149
- .hero {
150
- display: none;
151
- flex: 1;
152
- background-color: var(--hero-bg);
153
- color: var(--hero-color);
154
- padding: 40px;
155
- display: flex;
156
- flex-direction: column;
157
- justify-content: center;
158
- }
159
-
160
- @media (min-width: 768px) {
161
- .hero {
162
- display: flex;
163
- }
164
- }
165
-
166
- .hero h2 {
167
- margin-bottom: 20px;
168
- color: var(--hero-color);
169
- }
170
-
171
- /* Form styles */
172
- .form-group {
173
- margin-bottom: 20px;
174
- }
175
-
176
- label {
177
- display: block;
178
- margin-bottom: 7px;
179
  font-weight: 500;
180
- }
181
-
182
- input[type="email"],
183
- input[type="password"] {
 
184
  width: 100%;
185
- padding: 12px 15px;
186
- border-radius: 6px;
187
- border: 1px solid var(--input-border);
188
- background-color: var(--input-background);
189
- color: var(--input-text);
190
- font-size: 16px;
191
- transition: border-color 0.3s ease, box-shadow 0.3s ease;
192
- }
193
-
194
- input:focus {
195
  outline: none;
196
- border-color: var(--button-primary-bg);
197
- box-shadow: 0 0 0 3px rgba(92, 184, 92, 0.25);
198
- }
199
-
200
- .button {
 
 
 
 
201
  display: inline-block;
202
- padding: 12px 20px;
203
- background-color: var(--button-primary-bg);
204
- color: var(--button-primary-text);
 
205
  border: none;
206
- border-radius: 6px;
207
- font-size: 16px;
208
- font-weight: 600;
209
  cursor: pointer;
210
- transition: opacity 0.3s ease, transform 0.1s ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  width: 100%;
212
- text-align: center;
213
- }
214
-
215
- .button:hover {
216
- opacity: 0.9;
217
- }
218
-
219
- .button:active {
220
- transform: translateY(1px);
221
- }
222
-
223
- /* Status messages */
224
- .status-message {
225
- margin-top: 15px;
226
- color: var(--error-color);
227
- font-weight: bold;
228
- display: none;
229
- }
230
-
231
- .status-message.success {
232
- color: var(--success-color);
233
- }
234
-
235
- .error-message {
236
- color: var(--error-color);
237
- font-size: 14px;
238
- margin-top: 5px;
239
  display: none;
240
- }
241
-
242
- /* Welcome section */
243
- #welcome-section {
244
- background-color: var(--welcome-bg);
245
- padding: 20px;
246
- border: 1px solid var(--border-color);
247
- border-radius: 5px;
248
- }
249
-
250
- #logout-button {
251
- background-color: var(--logout-button-bg);
252
- margin-top: 15px;
253
- width: auto;
254
- }
255
-
256
- #logout-button:hover {
257
- background-color: var(--logout-button-hover);
258
- }
259
-
260
- hr {
261
- margin: 20px 0;
262
- border-color: var(--separator-color);
263
- }
264
-
265
- /* Notifications */
266
- #notifications {
267
- margin-top: 15px;
268
- padding: 15px;
269
- border: 1px dashed var(--border-color);
270
- background-color: var(--card-background);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  min-height: 100px;
272
- max-height: 300px;
273
  overflow-y: auto;
274
- }
275
-
276
- #notifications p {
277
- margin: 5px 0;
278
- padding-bottom: 5px;
279
- border-bottom: 1px solid var(--separator-color);
280
- }
281
-
282
- #notifications p:last-child {
283
- border-bottom: none;
284
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Modern UI styling for Auth App */
2
  :root {
3
+ --primary: #4361ee;
4
+ --primary-light: #4cc9f0;
5
+ --primary-dark: #3a0ca3;
6
+ --success: #4ade80;
7
+ --danger: #f87171;
8
+ --text: #1f2937;
9
+ --text-light: #6b7280;
10
+ --bg-light: #f9fafb;
11
+ --bg-dark: #f3f4f6;
12
+ --card-bg: #ffffff;
13
+ --card-border: #e5e7eb;
14
+ --shadow: rgba(0, 0, 0, 0.1);
15
+ --radius: 8px;
16
+ --transition: all 0.3s ease;
17
+ }
18
+
19
+ * {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  box-sizing: border-box;
21
  margin: 0;
22
  padding: 0;
23
+ }
24
+
25
+ body {
26
+ font-family: 'Segoe UI', Roboto, -apple-system, BlinkMacSystemFont, sans-serif;
 
 
27
  line-height: 1.6;
28
+ color: var(--text);
29
+ background: linear-gradient(135deg, var(--bg-light), var(--bg-dark));
30
+ min-height: 100vh;
31
+ padding: 2rem;
32
+ }
33
+
34
+ .container {
35
+ max-width: 1200px;
36
+ margin: 0 auto;
37
+ padding: 0 1rem;
38
+ }
39
+
40
+ .app-header {
 
 
 
 
41
  text-align: center;
42
+ margin-bottom: 2rem;
43
+ animation: fadeIn 0.8s ease;
44
+ }
45
+
46
+ .app-title {
47
+ font-size: 2.5rem;
48
+ color: var(--primary-dark);
49
+ margin-bottom: 0.5rem;
50
+ font-weight: 600;
51
+ }
52
+
53
+ .app-subtitle {
54
+ color: var(--text-light);
55
  font-size: 1.1rem;
56
+ font-weight: 400;
57
+ }
58
+
59
+ .auth-section {
60
+ background-color: var(--card-bg);
61
+ border-radius: var(--radius);
62
+ box-shadow: 0 4px 16px var(--shadow);
63
+ padding: 2rem;
64
+ margin: 0 auto 2rem;
65
+ max-width: 480px;
66
+ transition: var(--transition);
67
+ animation: slideUp 0.5s ease;
68
+ }
69
+
70
+ .auth-section:hover {
71
+ box-shadow: 0 6px 24px rgba(0, 0, 0, 0.15);
72
+ }
73
+
74
+ .section-title {
75
+ font-size: 1.8rem;
76
+ color: var(--primary);
77
+ margin-bottom: 1.5rem;
 
 
 
 
 
 
 
 
 
 
78
  font-weight: 600;
79
+ position: relative;
80
+ padding-bottom: 0.75rem;
81
+ }
82
+
83
+ .section-title::after {
84
+ content: '';
85
+ position: absolute;
86
+ left: 0;
87
+ bottom: 0;
88
+ height: 3px;
89
+ width: 60px;
90
+ background: linear-gradient(to right, var(--primary), var(--primary-light));
91
+ border-radius: 1.5px;
92
+ }
93
+
94
+ .form-group {
95
+ margin-bottom: 1.5rem;
96
+ }
97
+
98
+ label {
99
  display: block;
100
+ margin-bottom: 0.5rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  font-weight: 500;
102
+ color: var(--text);
103
+ }
104
+
105
+ input[type="email"],
106
+ input[type="password"] {
107
  width: 100%;
108
+ padding: 0.75rem 1rem;
109
+ border: 1px solid var(--card-border);
110
+ border-radius: var(--radius);
111
+ font-size: 1rem;
112
+ transition: var(--transition);
 
 
 
 
 
113
  outline: none;
114
+ }
115
+
116
+ input[type="email"]:focus,
117
+ input[type="password"]:focus {
118
+ border-color: var(--primary-light);
119
+ box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.15);
120
+ }
121
+
122
+ .btn {
123
  display: inline-block;
124
+ padding: 0.75rem 1.5rem;
125
+ font-size: 1rem;
126
+ font-weight: 500;
127
+ text-align: center;
128
  border: none;
129
+ border-radius: var(--radius);
 
 
130
  cursor: pointer;
131
+ transition: var(--transition);
132
+ background-color: var(--primary);
133
+ color: white;
134
+ }
135
+
136
+ .btn:hover {
137
+ background-color: var(--primary-dark);
138
+ transform: translateY(-2px);
139
+ }
140
+
141
+ .btn:active {
142
+ transform: translateY(0);
143
+ }
144
+
145
+ .btn-full {
146
  width: 100%;
147
+ }
148
+
149
+ .btn-success {
150
+ background-color: var(--success);
151
+ }
152
+
153
+ .btn-success:hover {
154
+ background-color: #3eb76a;
155
+ }
156
+
157
+ .btn-danger {
158
+ background-color: var(--danger);
159
+ }
160
+
161
+ .btn-danger:hover {
162
+ background-color: #e05252;
163
+ }
164
+
165
+ .status-message {
166
+ margin-top: 1rem;
167
+ padding: 0.75rem 1rem;
168
+ border-radius: var(--radius);
169
+ font-weight: 500;
 
 
 
 
170
  display: none;
171
+ background-color: rgba(248, 113, 113, 0.1);
172
+ color: var(--danger);
173
+ border-left: 4px solid var(--danger);
174
+ }
175
+
176
+ .status-message.success {
177
+ background-color: rgba(74, 222, 128, 0.1);
178
+ color: #16a34a;
179
+ border-color: var(--success);
180
+ }
181
+
182
+ .switch-auth {
183
+ margin-top: 1.5rem;
184
+ text-align: center;
185
+ color: var(--text-light);
186
+ }
187
+
188
+ .switch-auth a {
189
+ color: var(--primary);
190
+ text-decoration: none;
191
+ font-weight: 500;
192
+ transition: var(--transition);
193
+ }
194
+
195
+ .switch-auth a:hover {
196
+ color: var(--primary-dark);
197
+ text-decoration: underline;
198
+ }
199
+
200
+ #welcome-section {
201
+ background-color: var(--card-bg);
202
+ border-radius: var(--radius);
203
+ box-shadow: 0 4px 16px var(--shadow);
204
+ padding: 2rem;
205
+ margin: 0 auto;
206
+ max-width: 800px;
207
+ animation: fadeIn 0.8s ease;
208
+ }
209
+
210
+ .welcome-header {
211
+ display: flex;
212
+ justify-content: space-between;
213
+ align-items: center;
214
+ margin-bottom: 1.5rem;
215
+ }
216
+
217
+ #welcome-message {
218
+ font-size: 1.3rem;
219
+ font-weight: 500;
220
+ color: var(--primary-dark);
221
+ margin-bottom: 1rem;
222
+ }
223
+
224
+ .divider {
225
+ margin: 2rem 0;
226
+ height: 1px;
227
+ background-color: var(--card-border);
228
+ }
229
+
230
+ .notifications-header {
231
+ display: flex;
232
+ justify-content: space-between;
233
+ align-items: center;
234
+ margin-bottom: 1rem;
235
+ }
236
+
237
+ #notifications {
238
+ background-color: var(--bg-light);
239
+ border-radius: var(--radius);
240
+ padding: 1rem;
241
  min-height: 100px;
242
+ max-height: 350px;
243
  overflow-y: auto;
244
+ border: 1px solid var(--card-border);
245
+ }
246
+
247
+ #notifications p {
248
+ padding: 0.75rem;
249
+ margin-bottom: 0.75rem;
250
+ border-radius: var(--radius);
251
+ background-color: white;
252
+ border-left: 3px solid var(--primary-light);
253
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
254
+ animation: fadeIn 0.3s ease;
255
+ }
256
+
257
+ #notifications p:last-child {
258
+ margin-bottom: 0;
259
+ }
260
+
261
+ #notifications p em {
262
+ color: var(--text-light);
263
+ font-style: italic;
264
+ }
265
+
266
+ /* Animation keyframes */
267
+ @keyframes fadeIn {
268
+ from { opacity: 0; }
269
+ to { opacity: 1; }
270
+ }
271
+
272
+ @keyframes slideUp {
273
+ from {
274
+ opacity: 0;
275
+ transform: translateY(20px);
276
+ }
277
+ to {
278
+ opacity: 1;
279
+ transform: translateY(0);
280
+ }
281
+ }
282
+
283
+ /* Responsive styles */
284
+ @media (max-width: 768px) {
285
+ body {
286
+ padding: 1rem;
287
+ }
288
+
289
+ .app-title {
290
+ font-size: 2rem;
291
+ }
292
+
293
+ .auth-section {
294
+ padding: 1.5rem;
295
+ margin-bottom: 1.5rem;
296
+ }
297
+ }
298
+
299
+ @media (max-width: 480px) {
300
+ .app-title {
301
+ font-size: 1.8rem;
302
+ }
303
+
304
+ .section-title {
305
+ font-size: 1.5rem;
306
+ }
307
+
308
+ .auth-section {
309
+ padding: 1.25rem;
310
+ }
311
+
312
+ .btn {
313
+ padding: 0.7rem 1.25rem;
314
+ }
315
+ }
static/js/script.js CHANGED
@@ -1,7 +1,6 @@
1
- const loginTab = document.getElementById('login-tab');
2
- const registerTab = document.getElementById('register-tab');
3
- const loginSection = document.getElementById('login-section');
4
  const registerSection = document.getElementById('register-section');
 
5
  const welcomeSection = document.getElementById('welcome-section');
6
  const registerForm = document.getElementById('register-form');
7
  const loginForm = document.getElementById('login-form');
@@ -16,43 +15,67 @@ const API_URL = '/api'; // Use relative path
16
  let webSocket = null;
17
  let authToken = localStorage.getItem('authToken'); // Load token on script start
18
 
19
- // --- UI Control & Tab Switching ---
20
  function showSection(sectionId) {
21
- loginSection.style.display = 'none';
22
- registerSection.style.display = 'none';
23
- welcomeSection.style.display = 'none';
 
24
 
25
- const sectionToShow = document.getElementById(sectionId);
26
- if (sectionToShow) {
27
- sectionToShow.style.display = 'block';
28
- } else {
29
- loginSection.style.display = 'block'; // Default to login
30
- }
31
- }
32
 
33
- // Tab switching logic
34
- loginTab.addEventListener('click', () => {
35
- loginTab.classList.add('active');
36
- registerTab.classList.remove('active');
37
- loginSection.classList.add('active');
38
- registerSection.classList.remove('active');
39
- });
 
 
 
40
 
41
- registerTab.addEventListener('click', () => {
42
- registerTab.classList.add('active');
43
- loginTab.classList.remove('active');
44
- registerSection.classList.add('active');
45
- loginSection.classList.remove('active');
46
- });
 
47
 
48
- // Initialize first tab as active
49
- loginTab.classList.add('active');
50
- loginSection.classList.add('active');
 
 
 
 
 
 
51
 
52
  function setStatus(element, message, isSuccess = false) {
 
 
 
 
 
53
  element.textContent = message;
54
  element.className = isSuccess ? 'status-message success' : 'status-message';
55
- element.style.display = message ? 'block' : 'none';
 
 
 
 
 
 
 
 
 
 
56
  }
57
 
58
  // --- API Calls ---
@@ -119,14 +142,7 @@ function connectWebSocket(token) {
119
  try {
120
  const messageData = JSON.parse(event.data);
121
  if (messageData.type === 'new_user' && messageData.message) {
122
- const p = document.createElement('p');
123
- p.textContent = messageData.message;
124
- // Add message to the top
125
- notificationsDiv.insertBefore(p, notificationsDiv.firstChild);
126
- // Limit number of messages shown (optional)
127
- while (notificationsDiv.children.length > 10) {
128
- notificationsDiv.removeChild(notificationsDiv.lastChild);
129
- }
130
  }
131
  } catch (error) {
132
  console.error("Error parsing WebSocket message:", error);
@@ -135,25 +151,48 @@ function connectWebSocket(token) {
135
 
136
  webSocket.onerror = (event) => {
137
  console.error("WebSocket error:", event);
138
- const p = document.createElement('p');
139
- p.textContent = `WebSocket error occurred. Notifications may stop.`;
140
- p.style.color = 'red';
141
- notificationsDiv.insertBefore(p, notificationsDiv.firstChild);
142
  };
143
 
144
  webSocket.onclose = (event) => {
145
  console.log("WebSocket connection closed:", event);
146
  webSocket = null; // Reset WebSocket variable
147
- // Optionally try to reconnect or inform user
148
  if (!event.wasClean) {
149
- const p = document.createElement('p');
150
- p.textContent = `WebSocket disconnected unexpectedly (Code: ${event.code}). Please refresh or log out/in.`;
151
- p.style.color = 'orange';
152
- notificationsDiv.insertBefore(p, notificationsDiv.firstChild);
153
  }
154
  };
155
  }
156
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  function disconnectWebSocket() {
158
  if (webSocket) {
159
  console.log("Closing WebSocket connection.");
@@ -181,30 +220,31 @@ async function handleLogin(email, password) {
181
  }
182
 
183
  async function handleRegister(email, password) {
184
- setStatus(registerStatus, "Registering...");
185
- try {
186
  const data = await apiRequest('/register', 'POST', { email, password });
187
  setStatus(registerStatus, `Registration successful for ${data.email}! Please log in.`, true);
188
  registerForm.reset(); // Clear form
189
- // Switch to login tab
190
- loginTab.click();
191
- } catch (error) {
192
  setStatus(registerStatus, `Registration failed: ${error.message}`);
193
- }
194
  }
195
 
196
  async function showWelcomePage() {
197
  if (!authToken) {
198
  showSection('login-section');
199
- loginTab.click();
200
  return;
201
  }
202
  try {
203
  // Fetch user details using the token
204
  const user = await apiRequest('/users/me', 'GET', null, authToken);
205
- welcomeMessage.textContent = `Welcome, ${user.email}!`;
206
  showSection('welcome-section');
207
  connectWebSocket(authToken); // Connect WS after successful login and user fetch
 
 
 
208
  } catch (error) {
209
  // If fetching user fails (e.g., invalid/expired token), logout
210
  console.error("Failed to fetch user details:", error);
@@ -218,13 +258,19 @@ function handleLogout() {
218
  disconnectWebSocket();
219
  setStatus(loginStatus, ""); // Clear any previous login errors
220
  showSection('login-section');
221
- loginTab.click();
222
  }
223
 
224
  // --- Form Validation ---
225
- function validateEmail(email) {
226
- const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
227
- return re.test(String(email).toLowerCase());
 
 
 
 
 
 
 
228
  }
229
 
230
  // --- Event Listeners ---
@@ -234,73 +280,28 @@ registerForm.addEventListener('submit', (e) => {
234
  const password = document.getElementById('reg-password').value;
235
  const confirmPassword = document.getElementById('reg-confirm-password').value;
236
 
237
- const emailError = document.getElementById('register-email-error');
238
- const passwordError = document.getElementById('register-password-error');
239
- const confirmError = document.getElementById('register-confirm-error');
240
-
241
- let isValid = true;
242
-
243
- // Email validation
244
- if (!validateEmail(email)) {
245
- emailError.style.display = 'block';
246
- isValid = false;
247
- } else {
248
- emailError.style.display = 'none';
249
- }
250
-
251
- // Password validation
252
- if (password.length < 8) {
253
- passwordError.style.display = 'block';
254
- isValid = false;
255
- } else {
256
- passwordError.style.display = 'none';
257
- }
258
-
259
- // Confirm password
260
- if (password !== confirmPassword) {
261
- confirmError.style.display = 'block';
262
- isValid = false;
263
- } else {
264
- confirmError.style.display = 'none';
265
  }
266
 
267
- if (isValid) {
268
- handleRegister(email, password);
269
- }
270
  });
271
 
272
  loginForm.addEventListener('submit', (e) => {
273
  e.preventDefault();
274
  const email = document.getElementById('login-email').value;
275
  const password = document.getElementById('login-password').value;
276
-
277
- const emailError = document.getElementById('login-email-error');
278
- const passwordError = document.getElementById('login-password-error');
279
-
280
- let isValid = true;
281
-
282
- // Email validation
283
- if (!validateEmail(email)) {
284
- emailError.style.display = 'block';
285
- isValid = false;
286
- } else {
287
- emailError.style.display = 'none';
288
- }
289
-
290
- // Simple password validation
291
- if (password.length < 1) {
292
- passwordError.style.display = 'block';
293
- isValid = false;
294
- } else {
295
- passwordError.style.display = 'none';
296
- }
297
-
298
- if (isValid) {
299
- handleLogin(email, password);
300
- }
301
  });
302
 
303
- logoutButton.addEventListener('click', handleLogout);
 
 
 
 
 
304
 
305
  // --- Initial Page Load Logic ---
306
  document.addEventListener('DOMContentLoaded', () => {
@@ -309,6 +310,6 @@ document.addEventListener('DOMContentLoaded', () => {
309
  showWelcomePage(); // Try to fetch user info and show welcome
310
  } else {
311
  console.log("No token found, showing login page.");
312
- loginTab.click(); // Activate login tab
313
  }
314
  });
 
1
+ // DOM Elements
 
 
2
  const registerSection = document.getElementById('register-section');
3
+ const loginSection = document.getElementById('login-section');
4
  const welcomeSection = document.getElementById('welcome-section');
5
  const registerForm = document.getElementById('register-form');
6
  const loginForm = document.getElementById('login-form');
 
15
  let webSocket = null;
16
  let authToken = localStorage.getItem('authToken'); // Load token on script start
17
 
18
+ // --- UI Control ---
19
  function showSection(sectionId) {
20
+ // Hide all sections first with a fade effect
21
+ fadeOut(registerSection);
22
+ fadeOut(loginSection);
23
+ fadeOut(welcomeSection);
24
 
25
+ // Show the requested section with a fade-in effect
26
+ setTimeout(() => {
27
+ registerSection.style.display = 'none';
28
+ loginSection.style.display = 'none';
29
+ welcomeSection.style.display = 'none';
 
 
30
 
31
+ const sectionToShow = document.getElementById(sectionId);
32
+ if (sectionToShow) {
33
+ sectionToShow.style.display = 'block';
34
+ fadeIn(sectionToShow);
35
+ } else {
36
+ loginSection.style.display = 'block';
37
+ fadeIn(loginSection);
38
+ }
39
+ }, 300); // Wait for fade out to complete
40
+ }
41
 
42
+ function fadeOut(element) {
43
+ if (element && element.style.display !== 'none') {
44
+ element.style.opacity = '1';
45
+ element.style.transition = 'opacity 300ms';
46
+ element.style.opacity = '0';
47
+ }
48
+ }
49
 
50
+ function fadeIn(element) {
51
+ if (element) {
52
+ element.style.opacity = '0';
53
+ element.style.transition = 'opacity 300ms';
54
+ setTimeout(() => {
55
+ element.style.opacity = '1';
56
+ }, 50);
57
+ }
58
+ }
59
 
60
  function setStatus(element, message, isSuccess = false) {
61
+ if (!message) {
62
+ element.style.display = 'none';
63
+ return;
64
+ }
65
+
66
  element.textContent = message;
67
  element.className = isSuccess ? 'status-message success' : 'status-message';
68
+
69
+ // Show with animation
70
+ element.style.display = 'block';
71
+ element.style.opacity = '0';
72
+ element.style.transform = 'translateY(-10px)';
73
+ element.style.transition = 'opacity 300ms, transform 300ms';
74
+
75
+ setTimeout(() => {
76
+ element.style.opacity = '1';
77
+ element.style.transform = 'translateY(0)';
78
+ }, 10);
79
  }
80
 
81
  // --- API Calls ---
 
142
  try {
143
  const messageData = JSON.parse(event.data);
144
  if (messageData.type === 'new_user' && messageData.message) {
145
+ addNotification(messageData.message);
 
 
 
 
 
 
 
146
  }
147
  } catch (error) {
148
  console.error("Error parsing WebSocket message:", error);
 
151
 
152
  webSocket.onerror = (event) => {
153
  console.error("WebSocket error:", event);
154
+ addNotification(`WebSocket error occurred. Notifications may stop.`, 'error');
 
 
 
155
  };
156
 
157
  webSocket.onclose = (event) => {
158
  console.log("WebSocket connection closed:", event);
159
  webSocket = null; // Reset WebSocket variable
 
160
  if (!event.wasClean) {
161
+ addNotification(`WebSocket disconnected unexpectedly (Code: ${event.code}). Please refresh or log out/in.`, 'warning');
 
 
 
162
  }
163
  };
164
  }
165
 
166
+ function addNotification(message, type = 'info') {
167
+ const p = document.createElement('p');
168
+ p.classList.add(type);
169
+
170
+ // Add icon based on notification type
171
+ let icon = 'bell';
172
+ if (type === 'error') icon = 'exclamation-circle';
173
+ if (type === 'warning') icon = 'exclamation-triangle';
174
+ if (type === 'success') icon = 'check-circle';
175
+
176
+ p.innerHTML = `<i class="fas fa-${icon}"></i> ${message}`;
177
+
178
+ // Add message to the top with animation
179
+ p.style.opacity = '0';
180
+ p.style.transform = 'translateY(-10px)';
181
+ notificationsDiv.insertBefore(p, notificationsDiv.firstChild);
182
+
183
+ // Trigger animation
184
+ setTimeout(() => {
185
+ p.style.transition = 'opacity 300ms, transform 300ms';
186
+ p.style.opacity = '1';
187
+ p.style.transform = 'translateY(0)';
188
+ }, 10);
189
+
190
+ // Limit number of messages shown (optional)
191
+ while (notificationsDiv.children.length > 10) {
192
+ notificationsDiv.removeChild(notificationsDiv.lastChild);
193
+ }
194
+ }
195
+
196
  function disconnectWebSocket() {
197
  if (webSocket) {
198
  console.log("Closing WebSocket connection.");
 
220
  }
221
 
222
  async function handleRegister(email, password) {
223
+ setStatus(registerStatus, "Registering...");
224
+ try {
225
  const data = await apiRequest('/register', 'POST', { email, password });
226
  setStatus(registerStatus, `Registration successful for ${data.email}! Please log in.`, true);
227
  registerForm.reset(); // Clear form
228
+ showSection('login-section'); // Switch to login
229
+ } catch (error) {
 
230
  setStatus(registerStatus, `Registration failed: ${error.message}`);
231
+ }
232
  }
233
 
234
  async function showWelcomePage() {
235
  if (!authToken) {
236
  showSection('login-section');
 
237
  return;
238
  }
239
  try {
240
  // Fetch user details using the token
241
  const user = await apiRequest('/users/me', 'GET', null, authToken);
242
+ welcomeMessage.innerHTML = `<i class="fas fa-user-circle"></i> Welcome back, <strong>${user.email}</strong>!`;
243
  showSection('welcome-section');
244
  connectWebSocket(authToken); // Connect WS after successful login and user fetch
245
+
246
+ // Add a welcome notification
247
+ addNotification(`You are now logged in as ${user.email}`, 'success');
248
  } catch (error) {
249
  // If fetching user fails (e.g., invalid/expired token), logout
250
  console.error("Failed to fetch user details:", error);
 
258
  disconnectWebSocket();
259
  setStatus(loginStatus, ""); // Clear any previous login errors
260
  showSection('login-section');
 
261
  }
262
 
263
  // --- Form Validation ---
264
+ function validatePassword(password, confirmPassword) {
265
+ if (password.length < 8) {
266
+ return "Password must be at least 8 characters.";
267
+ }
268
+
269
+ if (password !== confirmPassword) {
270
+ return "Passwords do not match.";
271
+ }
272
+
273
+ return null; // No error
274
  }
275
 
276
  // --- Event Listeners ---
 
280
  const password = document.getElementById('reg-password').value;
281
  const confirmPassword = document.getElementById('reg-confirm-password').value;
282
 
283
+ const passwordError = validatePassword(password, confirmPassword);
284
+ if (passwordError) {
285
+ setStatus(registerStatus, passwordError);
286
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  }
288
 
289
+ handleRegister(email, password);
 
 
290
  });
291
 
292
  loginForm.addEventListener('submit', (e) => {
293
  e.preventDefault();
294
  const email = document.getElementById('login-email').value;
295
  const password = document.getElementById('login-password').value;
296
+ handleLogin(email, password);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  });
298
 
299
+ logoutButton.addEventListener('click', () => {
300
+ // Add a confirmation dialog
301
+ if (confirm('Are you sure you want to log out?')) {
302
+ handleLogout();
303
+ }
304
+ });
305
 
306
  // --- Initial Page Load Logic ---
307
  document.addEventListener('DOMContentLoaded', () => {
 
310
  showWelcomePage(); // Try to fetch user info and show welcome
311
  } else {
312
  console.log("No token found, showing login page.");
313
+ showSection('login-section'); // Show login if no token
314
  }
315
  });
templates/index.html CHANGED
@@ -4,86 +4,86 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Auth App</title>
 
7
  <link rel="stylesheet" href="/static/css/style.css">
8
  </head>
9
  <body>
10
  <div class="container">
11
- <div class="logo">
12
- <h1>Auth & Notification App</h1>
13
- <p>Welcome! Please login to your account or create a new one.</p>
14
- </div>
15
-
16
- <div class="auth-container">
17
- <div class="hero">
18
- <h2>Welcome</h2>
19
- <p>Please sign in or create a new account to continue.</p>
20
- </div>
21
 
22
- <div class="auth-forms">
23
- <div class="tabs">
24
- <div class="tab" id="login-tab">Login</div>
25
- <div class="tab" id="register-tab">Register</div>
 
 
 
26
  </div>
27
-
28
- <!-- Login Form -->
29
- <div class="auth-form" id="login-section">
30
- <h2>Login to your account</h2>
31
- <form id="login-form">
32
- <div class="form-group">
33
- <label for="login-email">Email Address</label>
34
- <input type="email" id="login-email" name="email" placeholder="[email protected]" required>
35
- <div class="error-message" id="login-email-error">Please enter a valid email address</div>
36
- </div>
37
-
38
- <div class="form-group">
39
- <label for="login-password">Password</label>
40
- <input type="password" id="login-password" name="password" placeholder="Enter your password" required>
41
- <div class="error-message" id="login-password-error">Password must be at least 8 characters</div>
42
- </div>
43
-
44
- <button type="submit" class="button">Login Now</button>
45
- <p id="login-status" class="status-message"></p>
46
- </form>
47
  </div>
 
 
 
 
 
 
 
 
 
 
48
 
49
- <!-- Registration Form -->
50
- <div class="auth-form" id="register-section">
51
- <h2>Create your account</h2>
52
- <form id="register-form">
53
- <div class="form-group">
54
- <label for="reg-email">Email Address</label>
55
- <input type="email" id="reg-email" name="email" placeholder="youremail@example.com" required>
56
- <div class="error-message" id="register-email-error">Please enter a valid email address</div>
57
- </div>
58
-
59
- <div class="form-group">
60
- <label for="reg-password">Password</label>
61
- <input type="password" id="reg-password" name="password" placeholder="Create a password" required minlength="8">
62
- <div class="error-message" id="register-password-error">Password must be at least 8 characters</div>
63
- </div>
64
-
65
- <div class="form-group">
66
- <label for="reg-confirm-password">Confirm Password</label>
67
- <input type="password" id="reg-confirm-password" name="confirm_password" placeholder="Confirm your password" required minlength="8">
68
- <div class="error-message" id="register-confirm-error">Passwords do not match</div>
69
- </div>
70
-
71
- <button type="submit" class="button">Create Account</button>
72
- <p id="register-status" class="status-message"></p>
73
- </form>
74
  </div>
 
 
 
 
 
 
 
 
75
  </div>
76
  </div>
77
 
78
  <!-- Welcome Section (shown after login) -->
79
  <div id="welcome-section" style="display: none;">
80
- <h2>Welcome!</h2>
81
- <p id="welcome-message">Welcome, user!</p>
82
- <button id="logout-button" class="button">Logout</button>
83
- <hr>
84
- <h2>Real-time Notifications</h2>
 
 
 
 
 
 
 
 
85
  <div id="notifications">
86
- <p><em>Notifications will appear here...</em></p>
87
  </div>
88
  </div>
89
  </div>
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Auth App</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
8
  <link rel="stylesheet" href="/static/css/style.css">
9
  </head>
10
  <body>
11
  <div class="container">
12
+ <header class="app-header">
13
+ <h1 class="app-title">Auth & Notification App</h1>
14
+ <p class="app-subtitle">Secure login and real-time notifications</p>
15
+ </header>
 
 
 
 
 
 
16
 
17
+ <!-- Registration Section -->
18
+ <div id="register-section" class="auth-section">
19
+ <h2 class="section-title">Create Account</h2>
20
+ <form id="register-form">
21
+ <div class="form-group">
22
+ <label for="reg-email"><i class="fas fa-envelope"></i> Email Address</label>
23
+ <input type="email" id="reg-email" name="email" placeholder="[email protected]" required>
24
  </div>
25
+
26
+ <div class="form-group">
27
+ <label for="reg-password"><i class="fas fa-lock"></i> Password</label>
28
+ <input type="password" id="reg-password" name="password" placeholder="Minimum 8 characters" required minlength="8">
29
+ </div>
30
+
31
+ <div class="form-group">
32
+ <label for="reg-confirm-password"><i class="fas fa-lock"></i> Confirm Password</label>
33
+ <input type="password" id="reg-confirm-password" name="confirm_password" placeholder="Confirm your password" required minlength="8">
 
 
 
 
 
 
 
 
 
 
 
34
  </div>
35
+
36
+ <button type="submit" class="btn btn-full"><i class="fas fa-user-plus"></i> Register</button>
37
+ </form>
38
+
39
+ <div id="register-status" class="status-message"></div>
40
+
41
+ <div class="switch-auth">
42
+ Already have an account? <a href="#" onclick="showSection('login-section')">Sign in</a>
43
+ </div>
44
+ </div>
45
 
46
+ <!-- Login Section -->
47
+ <div id="login-section" class="auth-section" style="display: none;">
48
+ <h2 class="section-title">Welcome Back</h2>
49
+ <form id="login-form">
50
+ <div class="form-group">
51
+ <label for="login-email"><i class="fas fa-envelope"></i> Email Address</label>
52
+ <input type="email" id="login-email" name="email" placeholder="you@example.com" required>
53
+ </div>
54
+
55
+ <div class="form-group">
56
+ <label for="login-password"><i class="fas fa-lock"></i> Password</label>
57
+ <input type="password" id="login-password" name="password" placeholder="Your password" required>
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  </div>
59
+
60
+ <button type="submit" class="btn btn-full"><i class="fas fa-sign-in-alt"></i> Login</button>
61
+ </form>
62
+
63
+ <div id="login-status" class="status-message"></div>
64
+
65
+ <div class="switch-auth">
66
+ Don't have an account? <a href="#" onclick="showSection('register-section')">Create one now</a>
67
  </div>
68
  </div>
69
 
70
  <!-- Welcome Section (shown after login) -->
71
  <div id="welcome-section" style="display: none;">
72
+ <div class="welcome-header">
73
+ <h2 class="section-title">Dashboard</h2>
74
+ <button id="logout-button" class="btn btn-danger"><i class="fas fa-sign-out-alt"></i> Logout</button>
75
+ </div>
76
+
77
+ <div id="welcome-message">Welcome back, user!</div>
78
+
79
+ <div class="divider"></div>
80
+
81
+ <div class="notifications-header">
82
+ <h2 class="section-title">Real-time Notifications</h2>
83
+ </div>
84
+
85
  <div id="notifications">
86
+ <p><em><i class="fas fa-bell"></i> Notifications will appear here...</em></p>
87
  </div>
88
  </div>
89
  </div>