amaye15 commited on
Commit
202aa5d
·
1 Parent(s): 8afd545
Files changed (3) hide show
  1. static/css/style.css +45 -194
  2. static/js/script.js +142 -103
  3. templates/index.html +39 -55
static/css/style.css CHANGED
@@ -1,243 +1,94 @@
1
- /* --- CSS Variables --- */
2
- :root {
3
- --body-bg: #f0f0f0; /* Light gray background */
4
- --container-bg: #ffffff;
5
- --text-color: #333333;
6
- --text-color-secondary: #555555;
7
- --border-color: #e0e0e0;
8
- --input-bg: #ffffff;
9
- --input-border: #cccccc;
10
- --input-focus-border: #86b7fe; /* Bootstrap focus blue */
11
- --button-primary-bg: #0d6efd; /* Bootstrap primary */
12
- --button-primary-hover-bg: #0b5ed7;
13
- --button-danger-bg: #dc3545; /* Bootstrap danger */
14
- --button-danger-hover-bg: #bb2d3b;
15
- --button-text-color: #ffffff;
16
- --link-color: #0d6efd;
17
- --status-error-color: #dc3545;
18
- --status-success-color: #198754; /* Bootstrap success */
19
- --notification-bg: #f8f9fa; /* Light gray */
20
- --notification-border: #dee2e6;
21
- --shadow-color: rgba(0, 0, 0, 0.08);
22
- --border-radius: 8px; /* Rounded corners */
23
- }
24
-
25
- .dark-mode {
26
- --body-bg: #1a1a1a; /* Dark charcoal */
27
- --container-bg: #2c2c2c; /* Slightly lighter dark */
28
- --text-color: #e0e0e0; /* Light gray text */
29
- --text-color-secondary: #aaaaaa;
30
- --border-color: #444444; /* Darker border */
31
- --input-bg: #333333;
32
- --input-border: #555555;
33
- --input-focus-border: #0d6efd; /* Keep focus blue */
34
- --button-primary-bg: #0d6efd;
35
- --button-primary-hover-bg: #0b5ed7;
36
- --button-danger-bg: #dc3545;
37
- --button-danger-hover-bg: #bb2d3b;
38
- --button-text-color: #ffffff;
39
- --link-color: #58a6ff; /* Lighter blue for links */
40
- --status-error-color: #f86c7c;
41
- --status-success-color: #4cd08a;
42
- --notification-bg: #333333;
43
- --notification-border: #444444;
44
- --shadow-color: rgba(255, 255, 255, 0.05);
45
- }
46
-
47
- /* --- Global Styles --- */
48
- * {
49
- box-sizing: border-box;
50
- }
51
-
52
  body {
53
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
54
  line-height: 1.6;
55
- margin: 0;
56
- padding: 0;
57
- background-color: var(--body-bg);
58
- color: var(--text-color);
59
- transition: background-color 0.3s ease, color 0.3s ease; /* Smooth theme transition */
60
- }
61
-
62
- /* --- Layout --- */
63
- .container {
64
- max-width: 800px; /* Max width for content */
65
- margin: 30px auto; /* Center container */
66
- padding: 25px;
67
- background-color: var(--container-bg);
68
- border: 1px solid var(--border-color);
69
- border-radius: var(--border-radius);
70
- box-shadow: 0 4px 12px var(--shadow-color);
71
- transition: background-color 0.3s ease, border-color 0.3s ease;
72
- }
73
-
74
- .header {
75
- display: flex;
76
- justify-content: space-between;
77
- align-items: center;
78
- margin-bottom: 25px;
79
- padding-bottom: 15px;
80
- border-bottom: 1px solid var(--border-color);
81
  }
82
 
83
- .content-section {
84
- margin-bottom: 25px;
85
- padding: 20px;
86
- border: 1px solid var(--border-color);
87
- border-radius: var(--border-radius);
88
- background-color: var(--container-bg); /* Same as main container or slightly different */
89
- }
90
- /* Remove extra padding/border if nested inside container with padding */
91
- .container > .content-section {
92
- padding: 0;
93
- border: none;
94
- background: none;
95
- margin-bottom: 20px;
96
- }
97
-
98
-
99
  h1, h2 {
100
- color: var(--text-color);
101
- margin-top: 0;
102
- margin-bottom: 0.75em;
103
  }
104
- h1 { font-size: 1.8em; }
105
- h2 { font-size: 1.4em; }
106
 
107
- hr {
108
- border: none;
109
- border-top: 1px solid var(--border-color);
110
- margin: 25px 0;
 
 
 
111
  }
112
 
113
- a {
114
- color: var(--link-color);
115
- text-decoration: none;
116
- }
117
- a:hover {
118
- text-decoration: underline;
119
  }
120
 
121
- /* --- Forms --- */
122
- .form-group {
123
- margin-bottom: 15px;
124
- }
125
 
126
  label {
127
- display: block; /* Label on top */
128
- margin-bottom: 6px;
129
- font-weight: 500;
130
- color: var(--text-color-secondary);
131
  }
132
 
133
  input[type="email"],
134
  input[type="password"] {
135
- width: 100%;
136
- padding: 10px 12px;
137
- border: 1px solid var(--input-border);
138
- border-radius: var(--border-radius);
139
- background-color: var(--input-bg);
140
- color: var(--text-color);
141
- font-size: 1em;
142
- transition: border-color 0.2s ease, background-color 0.2s ease;
143
- }
144
-
145
- input[type="email"]:focus,
146
- input[type="password"]:focus {
147
- outline: none;
148
- border-color: var(--input-focus-border);
149
- box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.25);
150
- }
151
-
152
- .form-switch {
153
- margin-top: 15px;
154
- font-size: 0.9em;
155
- color: var(--text-color-secondary);
156
  }
157
 
158
- /* --- Buttons --- */
159
- .btn {
160
- display: inline-block;
161
- padding: 10px 18px;
162
  border: none;
163
- border-radius: var(--border-radius);
164
  cursor: pointer;
165
  font-size: 1em;
166
- font-weight: 500;
167
- text-align: center;
168
- transition: background-color 0.2s ease;
169
- color: var(--button-text-color); /* Common text color */
170
  }
171
 
172
- .btn-primary {
173
- background-color: var(--button-primary-bg);
174
- }
175
- .btn-primary:hover {
176
- background-color: var(--button-primary-hover-bg);
177
  }
178
 
179
- .btn-danger {
180
- background-color: var(--button-danger-bg);
181
  }
182
- .btn-danger:hover {
183
- background-color: var(--button-danger-hover-bg);
184
  }
185
 
186
- #theme-toggle {
187
- padding: 8px 12px;
188
- font-size: 0.9em;
189
- background-color: #6c757d; /* Secondary button color */
190
- }
191
- #theme-toggle:hover {
192
- background-color: #5c636a;
193
- }
194
-
195
-
196
- /* --- Status Messages --- */
197
  .status-message {
198
  margin-top: 15px;
199
- padding: 10px;
200
- border-radius: var(--border-radius);
201
- font-weight: 500;
202
- display: none; /* Hidden by default */
203
- border: 1px solid transparent;
204
  }
205
  .status-message.success {
206
- color: var(--status-success-color);
207
- background-color: rgba(25, 135, 84, 0.1); /* Light green background */
208
- border-color: rgba(25, 135, 84, 0.2);
209
- }
210
- .status-message:not(.success) { /* Default to error style */
211
- color: var(--status-error-color);
212
- background-color: rgba(220, 53, 69, 0.1); /* Light red background */
213
- border-color: rgba(220, 53, 69, 0.2);
214
  }
215
 
 
 
 
216
 
217
- /* --- Notifications --- */
218
  #notifications {
219
  margin-top: 15px;
220
  padding: 15px;
221
- border: 1px solid var(--notification-border);
222
- border-radius: var(--border-radius);
223
- background-color: var(--notification-bg);
224
  min-height: 100px;
225
  max-height: 300px;
226
  overflow-y: auto;
227
- transition: background-color 0.3s ease, border-color 0.3s ease;
228
  }
229
 
230
  #notifications p {
231
- margin: 0 0 8px 0; /* Space below each notification */
232
- padding: 8px 0;
233
- border-bottom: 1px solid var(--border-color);
234
- font-size: 0.95em;
235
  }
236
  #notifications p:last-child {
237
  border-bottom: none;
238
- margin-bottom: 0;
239
- }
240
- #notifications p em { /* Style the placeholder */
241
- color: var(--text-color-secondary);
242
- font-style: italic;
243
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  body {
2
+ font-family: sans-serif;
3
  line-height: 1.6;
4
+ margin: 20px;
5
+ background-color: #f4f4f4;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  }
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  h1, h2 {
9
+ color: #333;
 
 
10
  }
 
 
11
 
12
+ .auth-section {
13
+ background-color: #fff;
14
+ padding: 20px;
15
+ margin-bottom: 20px;
16
+ border: 1px solid #ddd;
17
+ border-radius: 5px;
18
+ max-width: 400px;
19
  }
20
 
21
+ #welcome-section {
22
+ background-color: #e7f3fe;
23
+ padding: 20px;
24
+ border: 1px solid #c8e1f8;
25
+ border-radius: 5px;
 
26
  }
27
 
 
 
 
 
28
 
29
  label {
30
+ display: inline-block;
31
+ margin-bottom: 5px;
 
 
32
  }
33
 
34
  input[type="email"],
35
  input[type="password"] {
36
+ width: calc(100% - 22px); /* Account for padding/border */
37
+ padding: 10px;
38
+ margin-bottom: 10px;
39
+ border: 1px solid #ccc;
40
+ border-radius: 4px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
42
 
43
+ button {
44
+ background-color: #5cb85c;
45
+ color: white;
46
+ padding: 10px 15px;
47
  border: none;
48
+ border-radius: 4px;
49
  cursor: pointer;
50
  font-size: 1em;
 
 
 
 
51
  }
52
 
53
+ button:hover {
54
+ background-color: #4cae4c;
 
 
 
55
  }
56
 
57
+ #logout-button {
58
+ background-color: #d9534f;
59
  }
60
+ #logout-button:hover {
61
+ background-color: #c9302c;
62
  }
63
 
 
 
 
 
 
 
 
 
 
 
 
64
  .status-message {
65
  margin-top: 15px;
66
+ color: red;
67
+ font-weight: bold;
 
 
 
68
  }
69
  .status-message.success {
70
+ color: green;
 
 
 
 
 
 
 
71
  }
72
 
73
+ hr {
74
+ margin: 20px 0;
75
+ }
76
 
 
77
  #notifications {
78
  margin-top: 15px;
79
  padding: 15px;
80
+ border: 1px dashed #aaa;
81
+ background-color: #f9f9f9;
 
82
  min-height: 100px;
83
  max-height: 300px;
84
  overflow-y: auto;
 
85
  }
86
 
87
  #notifications p {
88
+ margin: 5px 0;
89
+ padding-bottom: 5px;
90
+ border-bottom: 1px solid #eee;
 
91
  }
92
  #notifications p:last-child {
93
  border-bottom: none;
 
 
 
 
 
94
  }
static/js/script.js CHANGED
@@ -1,202 +1,241 @@
1
- // --- Existing Constants (remove theme button) ---
2
  const registerSection = document.getElementById('register-section');
3
  const loginSection = document.getElementById('login-section');
4
  const welcomeSection = document.getElementById('welcome-section');
5
- // ... other element constants ...
 
 
 
 
 
6
  const notificationsDiv = document.getElementById('notifications');
7
- const API_URL = '/api';
8
- // const themeToggleButton = document.getElementById('theme-toggle'); // REMOVE
9
 
10
- // --- Existing State Variables ---
 
11
  let webSocket = null;
12
- let authToken = localStorage.getItem('authToken');
13
 
14
- // --- UI Control (showSection, setStatus - keep) ---
15
- // ... showSection and setStatus functions remain the same ...
16
  function showSection(sectionId) {
17
  registerSection.style.display = 'none';
18
  loginSection.style.display = 'none';
19
  welcomeSection.style.display = 'none';
 
20
  const sectionToShow = document.getElementById(sectionId);
21
- if (sectionToShow) { sectionToShow.style.display = 'block'; }
22
- else { loginSection.style.display = 'block'; }
 
 
 
23
  }
 
24
  function setStatus(element, message, isSuccess = false) {
25
  element.textContent = message;
26
  element.className = isSuccess ? 'status-message success' : 'status-message';
27
  element.style.display = message ? 'block' : 'none';
28
  }
29
 
30
-
31
- // --- API Calls (apiRequest - keep) ---
32
- // ... apiRequest function remains the same ...
33
  async function apiRequest(endpoint, method = 'GET', body = null, token = null) {
34
  const headers = { 'Content-Type': 'application/json' };
35
- if (token) { headers['Authorization'] = `Bearer ${token}`; }
36
- const options = { method: method, headers: headers };
37
- if (body && method !== 'GET') { options.body = JSON.stringify(body); }
 
 
 
 
 
 
 
 
 
 
38
  try {
39
  const response = await fetch(`${API_URL}${endpoint}`, options);
40
- const data = await response.json();
 
41
  if (!response.ok) {
 
42
  const errorMessage = data?.detail || response.statusText || `HTTP error ${response.status}`;
43
- console.error("API Error:", errorMessage); throw new Error(errorMessage);
44
- } return data;
45
- } catch (error) { console.error(`API Error to ${endpoint}:`, error); throw error; }
 
 
 
 
 
46
  }
47
 
48
 
49
- // --- WebSocket Handling (connect/disconnectWebSocket, displayNotificationError - keep) ---
50
- // ... connectWebSocket, disconnectWebSocket, displayNotificationError functions remain the same ...
51
  function connectWebSocket(token) {
52
- if (!token) { console.error("No token for WebSocket."); return; }
53
- if (webSocket && webSocket.readyState === WebSocket.OPEN) { console.log("WS already open."); return; }
 
 
 
 
 
 
 
 
54
  const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
55
  const wsUrl = `${wsProtocol}//${window.location.host}${API_URL}/ws/${token}`;
56
- console.log("Connecting WebSocket:", wsUrl);
 
 
57
  webSocket = new WebSocket(wsUrl);
 
58
  webSocket.onopen = (event) => {
59
- console.log("WS opened:", event);
60
- if (notificationsDiv.querySelector('p em')) { notificationsDiv.innerHTML = ''; }
 
 
 
61
  };
 
62
  webSocket.onmessage = (event) => {
63
- console.log("WS message received:", event.data);
64
  try {
65
  const messageData = JSON.parse(event.data);
66
  if (messageData.type === 'new_user' && messageData.message) {
67
  const p = document.createElement('p');
68
  p.textContent = messageData.message;
 
69
  notificationsDiv.insertBefore(p, notificationsDiv.firstChild);
70
- while (notificationsDiv.children.length > 10) { notificationsDiv.removeChild(notificationsDiv.lastChild); }
 
 
 
 
71
  }
72
- } catch (error) { console.error("WS message parse error:", error); }
 
 
 
 
 
 
 
 
 
 
73
  };
74
- webSocket.onerror = (event) => { console.error("WS error:", event); displayNotificationError("WebSocket error occurred."); };
75
  webSocket.onclose = (event) => {
76
- console.log("WS closed:", event);
77
- if (!event.wasClean) { displayNotificationError(`WebSocket disconnected (Code: ${event.code}). Refresh maybe needed.`); }
78
- webSocket = null;
 
 
 
 
 
 
 
79
  };
80
  }
 
81
  function disconnectWebSocket() {
82
- if (webSocket) { console.log("Closing WS."); webSocket.close(); webSocket = null; }
83
- }
84
- function displayNotificationError(message) {
85
- const p = document.createElement('p');
86
- p.textContent = message;
87
- p.style.color = 'orange';
88
- p.style.fontWeight = 'bold';
89
- notificationsDiv.insertBefore(p, notificationsDiv.firstChild);
90
  }
91
 
92
-
93
- // --- Authentication Logic (handleLogin, handleRegister, showWelcomePage, handleLogout - keep) ---
94
- // ... handleLogin, handleRegister, showWelcomePage, handleLogout functions remain the same ...
95
  async function handleLogin(email, password) {
96
  setStatus(loginStatus, "Logging in...");
97
  try {
98
  const data = await apiRequest('/login', 'POST', { email, password });
99
  if (data.access_token) {
100
  authToken = data.access_token;
101
- localStorage.setItem('authToken', authToken);
102
- setStatus(loginStatus, "");
103
- await showWelcomePage();
104
- } else { throw new Error("No token received."); }
105
- } catch (error) { setStatus(loginStatus, `Login failed: ${error.message}`); }
 
 
 
 
106
  }
 
107
  async function handleRegister(email, password) {
108
  setStatus(registerStatus, "Registering...");
109
  try {
110
  const data = await apiRequest('/register', 'POST', { email, password });
111
  setStatus(registerStatus, `Registration successful for ${data.email}! Please log in.`, true);
112
- registerForm.reset();
113
- showSection('login-section');
114
- } catch (error) { setStatus(registerStatus, `Registration failed: ${error.message}`); }
 
 
115
  }
 
116
  async function showWelcomePage() {
117
- if (!authToken) { showSection('login-section'); return; }
 
 
 
118
  try {
 
119
  const user = await apiRequest('/users/me', 'GET', null, authToken);
120
  welcomeMessage.textContent = `Welcome, ${user.email}!`;
121
  showSection('welcome-section');
122
- connectWebSocket(authToken);
123
- } catch (error) { console.error("Failed to fetch user:", error); handleLogout(); }
 
 
 
 
124
  }
 
125
  function handleLogout() {
126
  authToken = null;
127
  localStorage.removeItem('authToken');
128
  disconnectWebSocket();
129
- setStatus(loginStatus, "");
130
- notificationsDiv.innerHTML = '<p><em>Notifications will appear here...</em></p>';
131
  showSection('login-section');
132
  }
133
 
134
- // --- Theme Handling ---
135
- function applyTheme(isDarkMode) {
136
- if (isDarkMode) {
137
- document.body.classList.add('dark-mode');
138
- console.log("Applied dark theme.");
139
- } else {
140
- document.body.classList.remove('dark-mode');
141
- console.log("Applied light theme.");
142
- }
143
- }
144
 
145
- // --- Event Listeners (Forms & Logout) ---
146
  registerForm.addEventListener('submit', (e) => {
147
  e.preventDefault();
148
- // ... (validation and call handleRegister - keep) ...
149
  const email = document.getElementById('reg-email').value;
150
  const password = document.getElementById('reg-password').value;
151
  const confirmPassword = document.getElementById('reg-confirm-password').value;
152
- if (password !== confirmPassword) { setStatus(registerStatus, "Passwords do not match."); return; }
153
- if (password.length < 8) { setStatus(registerStatus, "Password must be >= 8 characters."); return; }
 
 
 
 
 
 
154
  handleRegister(email, password);
155
  });
 
156
  loginForm.addEventListener('submit', (e) => {
157
  e.preventDefault();
158
- // ... (call handleLogin - keep) ...
159
  const email = document.getElementById('login-email').value;
160
  const password = document.getElementById('login-password').value;
161
  handleLogin(email, password);
162
  });
 
163
  logoutButton.addEventListener('click', handleLogout);
164
- // themeToggleButton.addEventListener('click', toggleTheme); // REMOVE
165
 
166
 
167
  // --- Initial Page Load Logic ---
168
  document.addEventListener('DOMContentLoaded', () => {
169
- // System Theme Detection
170
- const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
171
-
172
- // Apply theme based on current system preference
173
- applyTheme(prefersDarkScheme.matches);
174
-
175
- // Listen for changes in system theme preference
176
- try {
177
- // Newer browsers
178
- prefersDarkScheme.addEventListener('change', (e) => {
179
- console.log("System theme preference changed.");
180
- applyTheme(e.matches);
181
- });
182
- } catch (e1) {
183
- try {
184
- // Older browsers (legacy method)
185
- prefersDarkScheme.addListener((e) => {
186
- console.log("System theme preference changed (legacy listener).");
187
- applyTheme(e.matches);
188
- });
189
- } catch (e2) {
190
- console.error("Browser doesn't support dynamic theme changes via matchMedia listeners.");
191
- }
192
- }
193
-
194
- // Check auth token after setting theme
195
  if (authToken) {
196
- console.log("Token found, showing welcome page.");
197
- showWelcomePage();
198
  } else {
199
  console.log("No token found, showing login page.");
200
- showSection('login-section');
201
  }
202
  });
 
 
1
  const registerSection = document.getElementById('register-section');
2
  const loginSection = document.getElementById('login-section');
3
  const welcomeSection = document.getElementById('welcome-section');
4
+ const registerForm = document.getElementById('register-form');
5
+ const loginForm = document.getElementById('login-form');
6
+ const registerStatus = document.getElementById('register-status');
7
+ const loginStatus = document.getElementById('login-status');
8
+ const welcomeMessage = document.getElementById('welcome-message');
9
+ const logoutButton = document.getElementById('logout-button');
10
  const notificationsDiv = document.getElementById('notifications');
 
 
11
 
12
+ const API_URL = '/api'; // Use relative path
13
+
14
  let webSocket = null;
15
+ let authToken = localStorage.getItem('authToken'); // Load token on script start
16
 
17
+ // --- UI Control ---
 
18
  function showSection(sectionId) {
19
  registerSection.style.display = 'none';
20
  loginSection.style.display = 'none';
21
  welcomeSection.style.display = 'none';
22
+
23
  const sectionToShow = document.getElementById(sectionId);
24
+ if (sectionToShow) {
25
+ sectionToShow.style.display = 'block';
26
+ } else {
27
+ loginSection.style.display = 'block'; // Default to login
28
+ }
29
  }
30
+
31
  function setStatus(element, message, isSuccess = false) {
32
  element.textContent = message;
33
  element.className = isSuccess ? 'status-message success' : 'status-message';
34
  element.style.display = message ? 'block' : 'none';
35
  }
36
 
37
+ // --- API Calls ---
 
 
38
  async function apiRequest(endpoint, method = 'GET', body = null, token = null) {
39
  const headers = { 'Content-Type': 'application/json' };
40
+ if (token) {
41
+ headers['Authorization'] = `Bearer ${token}`;
42
+ }
43
+
44
+ const options = {
45
+ method: method,
46
+ headers: headers,
47
+ };
48
+
49
+ if (body && method !== 'GET') {
50
+ options.body = JSON.stringify(body);
51
+ }
52
+
53
  try {
54
  const response = await fetch(`${API_URL}${endpoint}`, options);
55
+ const data = await response.json(); // Try to parse JSON regardless of status
56
+
57
  if (!response.ok) {
58
+ // Use detail from JSON if available, otherwise status text
59
  const errorMessage = data?.detail || response.statusText || `HTTP error ${response.status}`;
60
+ console.error("API Error:", errorMessage);
61
+ throw new Error(errorMessage);
62
+ }
63
+ return data;
64
+ } catch (error) {
65
+ console.error(`Error during API request to ${endpoint}:`, error);
66
+ throw error; // Re-throw to be caught by caller
67
+ }
68
  }
69
 
70
 
71
+ // --- WebSocket Handling ---
 
72
  function connectWebSocket(token) {
73
+ if (!token) {
74
+ console.error("No token available for WebSocket connection.");
75
+ return;
76
+ }
77
+ if (webSocket && webSocket.readyState === WebSocket.OPEN) {
78
+ console.log("WebSocket already connected.");
79
+ return;
80
+ }
81
+
82
+ // Construct WebSocket URL dynamically (replace http/https with ws/wss)
83
  const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
84
  const wsUrl = `${wsProtocol}//${window.location.host}${API_URL}/ws/${token}`;
85
+ console.log("Attempting to connect WebSocket:", wsUrl);
86
+
87
+
88
  webSocket = new WebSocket(wsUrl);
89
+
90
  webSocket.onopen = (event) => {
91
+ console.log("WebSocket connection opened:", event);
92
+ // Clear placeholder message on successful connect
93
+ if (notificationsDiv.querySelector('p em')) {
94
+ notificationsDiv.innerHTML = '';
95
+ }
96
  };
97
+
98
  webSocket.onmessage = (event) => {
99
+ console.log("WebSocket message received:", event.data);
100
  try {
101
  const messageData = JSON.parse(event.data);
102
  if (messageData.type === 'new_user' && messageData.message) {
103
  const p = document.createElement('p');
104
  p.textContent = messageData.message;
105
+ // Add message to the top
106
  notificationsDiv.insertBefore(p, notificationsDiv.firstChild);
107
+ // Limit number of messages shown (optional)
108
+ while (notificationsDiv.children.length > 10) {
109
+ notificationsDiv.removeChild(notificationsDiv.lastChild);
110
+ }
111
+
112
  }
113
+ } catch (error) {
114
+ console.error("Error parsing WebSocket message:", error);
115
+ }
116
+ };
117
+
118
+ webSocket.onerror = (event) => {
119
+ console.error("WebSocket error:", event);
120
+ const p = document.createElement('p');
121
+ p.textContent = `WebSocket error occurred. Notifications may stop.`;
122
+ p.style.color = 'red';
123
+ notificationsDiv.insertBefore(p, notificationsDiv.firstChild);
124
  };
125
+
126
  webSocket.onclose = (event) => {
127
+ console.log("WebSocket connection closed:", event);
128
+ webSocket = null; // Reset WebSocket variable
129
+ // Optionally try to reconnect or inform user
130
+ // Do not clear notifications on close, user might want to see history
131
+ if (!event.wasClean) {
132
+ const p = document.createElement('p');
133
+ p.textContent = `WebSocket disconnected unexpectedly (Code: ${event.code}). Please refresh or log out/in.`;
134
+ p.style.color = 'orange';
135
+ notificationsDiv.insertBefore(p, notificationsDiv.firstChild);
136
+ }
137
  };
138
  }
139
+
140
  function disconnectWebSocket() {
141
+ if (webSocket) {
142
+ console.log("Closing WebSocket connection.");
143
+ webSocket.close();
144
+ webSocket = null;
145
+ }
 
 
 
146
  }
147
 
148
+ // --- Authentication Logic ---
 
 
149
  async function handleLogin(email, password) {
150
  setStatus(loginStatus, "Logging in...");
151
  try {
152
  const data = await apiRequest('/login', 'POST', { email, password });
153
  if (data.access_token) {
154
  authToken = data.access_token;
155
+ localStorage.setItem('authToken', authToken); // Store token
156
+ setStatus(loginStatus, ""); // Clear status
157
+ await showWelcomePage(); // Fetch user info and show welcome
158
+ } else {
159
+ throw new Error("Login failed: No token received.");
160
+ }
161
+ } catch (error) {
162
+ setStatus(loginStatus, `Login failed: ${error.message}`);
163
+ }
164
  }
165
+
166
  async function handleRegister(email, password) {
167
  setStatus(registerStatus, "Registering...");
168
  try {
169
  const data = await apiRequest('/register', 'POST', { email, password });
170
  setStatus(registerStatus, `Registration successful for ${data.email}! Please log in.`, true);
171
+ registerForm.reset(); // Clear form
172
+ showSection('login-section'); // Switch to login
173
+ } catch (error) {
174
+ setStatus(registerStatus, `Registration failed: ${error.message}`);
175
+ }
176
  }
177
+
178
  async function showWelcomePage() {
179
+ if (!authToken) {
180
+ showSection('login-section');
181
+ return;
182
+ }
183
  try {
184
+ // Fetch user details using the token
185
  const user = await apiRequest('/users/me', 'GET', null, authToken);
186
  welcomeMessage.textContent = `Welcome, ${user.email}!`;
187
  showSection('welcome-section');
188
+ connectWebSocket(authToken); // Connect WS after successful login and user fetch
189
+ } catch (error) {
190
+ // If fetching user fails (e.g., invalid/expired token), logout
191
+ console.error("Failed to fetch user details:", error);
192
+ handleLogout();
193
+ }
194
  }
195
+
196
  function handleLogout() {
197
  authToken = null;
198
  localStorage.removeItem('authToken');
199
  disconnectWebSocket();
200
+ setStatus(loginStatus, ""); // Clear any previous login errors
 
201
  showSection('login-section');
202
  }
203
 
 
 
 
 
 
 
 
 
 
 
204
 
205
+ // --- Event Listeners ---
206
  registerForm.addEventListener('submit', (e) => {
207
  e.preventDefault();
 
208
  const email = document.getElementById('reg-email').value;
209
  const password = document.getElementById('reg-password').value;
210
  const confirmPassword = document.getElementById('reg-confirm-password').value;
211
+ if (password !== confirmPassword) {
212
+ setStatus(registerStatus, "Passwords do not match.");
213
+ return;
214
+ }
215
+ if (password.length < 8) {
216
+ setStatus(registerStatus, "Password must be at least 8 characters.");
217
+ return;
218
+ }
219
  handleRegister(email, password);
220
  });
221
+
222
  loginForm.addEventListener('submit', (e) => {
223
  e.preventDefault();
 
224
  const email = document.getElementById('login-email').value;
225
  const password = document.getElementById('login-password').value;
226
  handleLogin(email, password);
227
  });
228
+
229
  logoutButton.addEventListener('click', handleLogout);
 
230
 
231
 
232
  // --- Initial Page Load Logic ---
233
  document.addEventListener('DOMContentLoaded', () => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  if (authToken) {
235
+ console.log("Token found, attempting to show welcome page.");
236
+ showWelcomePage(); // Try to fetch user info and show welcome
237
  } else {
238
  console.log("No token found, showing login page.");
239
+ showSection('login-section'); // Show login if no token
240
  }
241
  });
templates/index.html CHANGED
@@ -7,65 +7,49 @@
7
  <link rel="stylesheet" href="/static/css/style.css">
8
  </head>
9
  <body>
10
- <div class="container">
11
- <div class="header">
12
- <h1>Auth & Notification App</h1>
13
- <!-- REMOVE THE BUTTON HERE -->
14
- <!-- <button id="theme-toggle">Toggle Theme</button> -->
15
- </div>
16
 
17
- <!-- Registration Section -->
18
- <div id="register-section" class="content-section auth-section">
19
- <h2>Register</h2>
20
- <form id="register-form">
21
- <div class="form-group">
22
- <label for="reg-email">Email:</label>
23
- <input type="email" id="reg-email" name="email" required>
24
- </div>
25
- <div class="form-group">
26
- <label for="reg-password">Password:</label>
27
- <input type="password" id="reg-password" name="password" required minlength="8">
28
- </div>
29
- <div class="form-group">
30
- <label for="reg-confirm-password">Confirm Password:</label>
31
- <input type="password" id="reg-confirm-password" name="confirm_password" required minlength="8">
32
- </div>
33
- <button type="submit" class="btn btn-primary">Register</button>
34
- </form>
35
- <p id="register-status" class="status-message"></p>
36
- <p class="form-switch">Already have an account? <a href="#" onclick="showSection('login-section')">Login here</a></p>
37
- </div>
38
 
39
- <!-- Login Section -->
40
- <div id="login-section" class="content-section auth-section" style="display: none;">
41
- <h2>Login</h2>
42
- <form id="login-form">
43
- <div class="form-group">
44
- <label for="login-email">Email:</label>
45
- <input type="email" id="login-email" name="email" required>
46
- </div>
47
- <div class="form-group">
48
- <label for="login-password">Password:</label>
49
- <input type="password" id="login-password" name="password" required>
50
- </div>
51
- <button type="submit" class="btn btn-primary">Login</button>
52
- </form>
53
- <p id="login-status" class="status-message"></p>
54
- <p class="form-switch">Don't have an account? <a href="#" onclick="showSection('register-section')">Register here</a></p>
55
- </div>
56
 
57
- <!-- Welcome Section (shown after login) -->
58
- <div id="welcome-section" class="content-section" style="display: none;">
59
- <h2>Welcome!</h2>
60
- <p id="welcome-message">Welcome, user!</p>
61
- <button id="logout-button" class="btn btn-danger">Logout</button>
62
- <hr>
63
- <h2>Real-time Notifications</h2>
64
- <div id="notifications">
65
- <p><em>Notifications will appear here...</em></p>
66
- </div>
67
  </div>
68
- </div><!-- end container -->
69
 
70
  <script src="/static/js/script.js"></script>
71
  </body>
 
7
  <link rel="stylesheet" href="/static/css/style.css">
8
  </head>
9
  <body>
10
+ <h1>Auth & Notification App</h1>
 
 
 
 
 
11
 
12
+ <!-- Registration Section -->
13
+ <div id="register-section" class="auth-section">
14
+ <h2>Register</h2>
15
+ <form id="register-form">
16
+ <label for="reg-email">Email:</label>
17
+ <input type="email" id="reg-email" name="email" required><br><br>
18
+ <label for="reg-password">Password:</label>
19
+ <input type="password" id="reg-password" name="password" required minlength="8"><br><br>
20
+ <label for="reg-confirm-password">Confirm Password:</label>
21
+ <input type="password" id="reg-confirm-password" name="confirm_password" required minlength="8"><br><br>
22
+ <button type="submit">Register</button>
23
+ </form>
24
+ <p id="register-status" class="status-message"></p>
25
+ <p>Already have an account? <a href="#" onclick="showSection('login-section')">Login here</a></p>
26
+ </div>
 
 
 
 
 
 
27
 
28
+ <!-- Login Section -->
29
+ <div id="login-section" class="auth-section" style="display: none;">
30
+ <h2>Login</h2>
31
+ <form id="login-form">
32
+ <label for="login-email">Email:</label>
33
+ <input type="email" id="login-email" name="email" required><br><br>
34
+ <label for="login-password">Password:</label>
35
+ <input type="password" id="login-password" name="password" required><br><br>
36
+ <button type="submit">Login</button>
37
+ </form>
38
+ <p id="login-status" class="status-message"></p>
39
+ <p>Don't have an account? <a href="#" onclick="showSection('register-section')">Register here</a></p>
40
+ </div>
 
 
 
 
41
 
42
+ <!-- Welcome Section (shown after login) -->
43
+ <div id="welcome-section" style="display: none;">
44
+ <h2>Welcome!</h2>
45
+ <p id="welcome-message">Welcome, user!</p>
46
+ <button id="logout-button">Logout</button>
47
+ <hr>
48
+ <h2>Real-time Notifications</h2>
49
+ <div id="notifications">
50
+ <p><em>Notifications will appear here...</em></p>
 
51
  </div>
52
+ </div>
53
 
54
  <script src="/static/js/script.js"></script>
55
  </body>