source: login.html@ b374c85

main
Last change on this file since b374c85 was d8deee6, checked in by GitHub <noreply@โ€ฆ>, 3 months ago

Add files via upload

  • Property mode set to 100644
File size: 17.0 KB
Lineย 
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8"/>
5 <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6 <title>Wedding Planner โ€” Login</title>
7 <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400;1,600&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet"/>
8 <style>
9 :root {
10 --cream:#fdf8f3; --cream-2:#f9f1e8; --cream-3:#f2e8db;
11 --blush-light:#fce8e8; --blush:#f4b8b8; --blush-mid:#e8949c;
12 --rose:#d4606a; --rose-deep:#b84550;
13 --gold-light:#f7e6b0; --gold:#c9a84c; --gold-dark:#9e7a28;
14 --text-dark:#2c1f1f; --text-mid:#6b4f4f; --text-light:#a08080;
15 --border:#ecddd0; --border-light:#f4ebe2;
16 --green:#3a9e6b; --red:#c94545;
17 --shadow-md:0 6px 20px rgba(44,31,31,.10),0 2px 6px rgba(44,31,31,.06);
18 --shadow-lg:0 16px 40px rgba(44,31,31,.14),0 4px 12px rgba(44,31,31,.08);
19 --radius-md:10px; --radius-lg:16px; --radius-xl:22px;
20 --transition:200ms cubic-bezier(.4,0,.2,1);
21 }
22 *,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
23 html,body{height:100%;font-family:'DM Sans',sans-serif;background:var(--cream);color:var(--text-dark);-webkit-font-smoothing:antialiased;}
24
25 body {
26 display:flex; align-items:center; justify-content:center;
27 min-height:100vh;
28 background: linear-gradient(135deg, #fdf8f3 0%, #f9f1e8 50%, #fce8e8 100%);
29 }
30
31 /* floating petals background */
32 body::before {
33 content:'๐Ÿ’';
34 position:fixed; top:10%; left:8%; font-size:80px; opacity:.06;
35 transform:rotate(-15deg); pointer-events:none;
36 }
37 body::after {
38 content:'๐ŸŒธ';
39 position:fixed; bottom:10%; right:8%; font-size:100px; opacity:.06;
40 transform:rotate(20deg); pointer-events:none;
41 }
42
43 .auth-wrapper {
44 width:100%; max-width:460px; padding:20px;
45 }
46
47 /* logo top */
48 .auth-logo {
49 text-align:center; margin-bottom:28px;
50 }
51 .auth-logo .logo-mark {
52 width:52px; height:52px; margin:0 auto 12px;
53 background:linear-gradient(135deg,var(--rose),var(--blush-mid));
54 border-radius:14px;
55 display:flex; align-items:center; justify-content:center;
56 box-shadow:0 4px 16px rgba(212,96,106,.35);
57 }
58 .auth-logo .logo-mark svg{width:26px;height:26px;}
59 .auth-logo h1 {
60 font-family:'Cormorant Garamond',serif;
61 font-size:28px; font-weight:700; color:var(--text-dark);
62 }
63 .auth-logo p { font-size:13px; color:var(--text-light); margin-top:4px; }
64
65 /* card */
66 .auth-card {
67 background:#fff;
68 border-radius:var(--radius-xl);
69 border:1px solid var(--border-light);
70 box-shadow:var(--shadow-lg);
71 overflow:hidden;
72 }
73
74 /* tabs */
75 .auth-tabs {
76 display:grid; grid-template-columns:1fr 1fr;
77 border-bottom:1px solid var(--border-light);
78 }
79 .auth-tab {
80 padding:16px; text-align:center; cursor:pointer;
81 font-size:14px; font-weight:500; color:var(--text-light);
82 background:#fff; border:none; outline:none;
83 transition:color var(--transition), background var(--transition);
84 }
85 .auth-tab.active {
86 color:var(--rose); font-weight:600;
87 background:var(--blush-light);
88 border-bottom:2px solid var(--rose);
89 }
90 .auth-tab:hover:not(.active){ background:var(--cream); color:var(--text-mid); }
91
92 /* form panels */
93 .auth-panel { display:none; padding:32px; }
94 .auth-panel.active { display:block; }
95
96 .gold-rule {
97 width:36px; height:2px;
98 background:linear-gradient(90deg,var(--gold),transparent);
99 border-radius:2px; margin:0 0 22px;
100 }
101
102 .form-group { margin-bottom:16px; }
103 .form-label {
104 display:block; font-size:12px; font-weight:600;
105 color:var(--text-mid); text-transform:uppercase;
106 letter-spacing:.06em; margin-bottom:6px;
107 }
108 input, select {
109 width:100%; padding:10px 14px;
110 border-radius:var(--radius-md); border:1px solid var(--border);
111 background:var(--cream); font-family:'DM Sans',sans-serif;
112 font-size:14px; color:var(--text-dark); outline:none;
113 transition:border-color var(--transition), background var(--transition);
114 }
115 input:focus, select:focus { border-color:var(--blush-mid); background:#fff; }
116
117 .form-row { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
118
119 .btn-submit {
120 width:100%; padding:12px;
121 background:var(--rose); color:#fff; border:none;
122 border-radius:var(--radius-md); font-family:'DM Sans',sans-serif;
123 font-size:15px; font-weight:600; cursor:pointer; margin-top:8px;
124 box-shadow:0 3px 12px rgba(212,96,106,.35);
125 transition:background var(--transition), box-shadow var(--transition), transform var(--transition);
126 }
127 .btn-submit:hover { background:var(--rose-deep); box-shadow:0 5px 18px rgba(212,96,106,.45); }
128 .btn-submit:active { transform:translateY(1px); }
129 .btn-submit:disabled { background:var(--blush); cursor:not-allowed; box-shadow:none; }
130
131 /* alert */
132 .alert {
133 padding:10px 14px; border-radius:var(--radius-md);
134 font-size:13px; font-weight:500; margin-bottom:16px;
135 display:none;
136 }
137 .alert.show { display:block; }
138 .alert-error { background:var(--red-light,#fdeaea); color:var(--red); border:1px solid #f5c2c2; }
139 .alert-success { background:#e4f5ec; color:var(--green); border:1px solid #b2e0ca; }
140
141 /* divider */
142 .form-divider {
143 display:flex; align-items:center; gap:10px;
144 margin:14px 0; color:var(--text-light); font-size:12px;
145 }
146 .form-divider::before, .form-divider::after {
147 content:''; flex:1; height:1px; background:var(--border-light);
148 }
149
150 /* footer note */
151 .auth-footer {
152 text-align:center; padding:16px 32px;
153 background:var(--cream); border-top:1px solid var(--border-light);
154 font-size:12px; color:var(--text-light);
155 }
156
157 /* loading spinner inside button */
158 .spinner {
159 display:inline-block; width:14px; height:14px;
160 border:2px solid rgba(255,255,255,.4);
161 border-top-color:#fff; border-radius:50%;
162 animation:spin .6s linear infinite; margin-right:6px; vertical-align:middle;
163 }
164 @keyframes spin { to { transform:rotate(360deg); } }
165 </style>
166</head>
167<body>
168
169<div class="auth-wrapper">
170
171 <!-- Logo -->
172 <div class="auth-logo">
173 <div class="logo-mark">
174 <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
175 <path d="M12 21.593c-5.63-5.539-11-10.297-11-14.402C1 3.402 4.248 1 7.5 1 9.849 1 11.271 2.007 12 3.011 12.729 2.007 14.151 1 16.5 1 19.752 1 23 3.402 23 7.191c0 4.105-5.37 8.863-11 14.402z"/>
176 </svg>
177 </div>
178 <h1>Wedding Planner</h1>
179 <p>Plan your perfect day</p>
180 </div>
181
182 <!-- Card -->
183 <div class="auth-card">
184
185 <!-- Tabs -->
186 <div class="auth-tabs">
187 <button class="auth-tab active" id="tabLogin" onclick="switchTab('login')">Sign In</button>
188 <button class="auth-tab" id="tabRegister" onclick="switchTab('register')">Create Account</button>
189 </div>
190
191 <!-- LOGIN PANEL -->
192 <div class="auth-panel active" id="panelLogin">
193 <div class="gold-rule"></div>
194
195 <div class="alert alert-error" id="loginError"></div>
196 <div class="alert alert-success" id="loginSuccess"></div>
197
198 <div class="form-group">
199 <label class="form-label">Email Address</label>
200 <input type="email" id="loginEmail" placeholder="your@email.com" autocomplete="email"/>
201 </div>
202 <div class="form-group">
203 <label class="form-label">Password</label>
204 <input type="password" id="loginPassword" placeholder="Your password" autocomplete="current-password"/>
205 </div>
206
207 <button class="btn-submit" id="btnLogin" onclick="doLogin()">Sign In</button>
208
209 <div class="form-divider">or</div>
210 <p style="text-align:center;font-size:13px;color:var(--text-light);">
211 Don't have an account?
212 <a href="#" onclick="switchTab('register')" style="color:var(--rose);font-weight:600;text-decoration:none;">Create one free</a>
213 </p>
214 </div>
215
216 <!-- REGISTER PANEL -->
217 <div class="auth-panel" id="panelRegister">
218 <div class="gold-rule"></div>
219
220 <div class="alert alert-error" id="registerError"></div>
221 <div class="alert alert-success" id="registerSuccess"></div>
222
223 <div class="form-row">
224 <div class="form-group">
225 <label class="form-label">First Name</label>
226 <input type="text" id="regFirst" placeholder="Ana"/>
227 </div>
228 <div class="form-group">
229 <label class="form-label">Last Name</label>
230 <input type="text" id="regLast" placeholder="Trajkovska"/>
231 </div>
232 </div>
233
234 <div class="form-group">
235 <label class="form-label">Email Address</label>
236 <input type="email" id="regEmail" placeholder="your@email.com"/>
237 </div>
238
239 <div class="form-group">
240 <label class="form-label">Password</label>
241 <input type="password" id="regPassword" placeholder="Min. 6 characters"/>
242 </div>
243
244 <div class="form-row">
245 <div class="form-group">
246 <label class="form-label">Phone (optional)</label>
247 <input type="text" id="regPhone" placeholder="+38970..."/>
248 </div>
249 <div class="form-group">
250 <label class="form-label">Gender (optional)</label>
251 <select id="regGender">
252 <option value="">Select...</option>
253 <option value="Female">Female</option>
254 <option value="Male">Male</option>
255 <option value="Other">Other</option>
256 </select>
257 </div>
258 </div>
259
260 <div class="form-group">
261 <label class="form-label">Birthday (optional)</label>
262 <input type="date" id="regBirthday"/>
263 </div>
264
265 <button class="btn-submit" id="btnRegister" onclick="doRegister()">Create Account</button>
266
267 <div class="form-divider">or</div>
268 <p style="text-align:center;font-size:13px;color:var(--text-light);">
269 Already have an account?
270 <a href="#" onclick="switchTab('login')" style="color:var(--rose);font-weight:600;text-decoration:none;">Sign in</a>
271 </p>
272 </div>
273
274 <div class="auth-footer">
275 Wedding Planner ยท University Database Project 2025
276 </div>
277 </div>
278</div>
279
280<script>
281 const API = 'http://localhost:3000';
282
283 // โ”€โ”€ Check if already logged in โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
284 window.addEventListener('DOMContentLoaded', async () => {
285 try {
286 const res = await fetch(`${API}/api/auth/me`, { credentials: 'include' });
287 if (res.ok) {
288 window.location.href = 'Wedding_Planner.html';
289 }
290 } catch (e) { /* not logged in, stay on page */ }
291 });
292
293 // โ”€โ”€ Tab switching โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
294 function switchTab(tab) {
295 document.getElementById('panelLogin').classList.toggle('active', tab === 'login');
296 document.getElementById('panelRegister').classList.toggle('active', tab === 'register');
297 document.getElementById('tabLogin').classList.toggle('active', tab === 'login');
298 document.getElementById('tabRegister').classList.toggle('active', tab === 'register');
299 clearAlerts();
300 }
301
302 function clearAlerts() {
303 ['loginError','loginSuccess','registerError','registerSuccess'].forEach(id => {
304 const el = document.getElementById(id);
305 el.classList.remove('show');
306 el.textContent = '';
307 });
308 }
309
310 function showAlert(id, msg) {
311 const el = document.getElementById(id);
312 el.textContent = msg;
313 el.classList.add('show');
314 }
315
316 function setLoading(btnId, loading) {
317 const btn = document.getElementById(btnId);
318 if (loading) {
319 btn.disabled = true;
320 btn.innerHTML = '<span class="spinner"></span>Please wait...';
321 } else {
322 btn.disabled = false;
323 btn.innerHTML = btnId === 'btnLogin' ? 'Sign In' : 'Create Account';
324 }
325 }
326
327 // โ”€โ”€ Login โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
328 async function doLogin() {
329 clearAlerts();
330 const email = document.getElementById('loginEmail').value.trim();
331 const password = document.getElementById('loginPassword').value;
332
333 if (!email || !password) {
334 showAlert('loginError', 'Please enter your email and password.');
335 return;
336 }
337
338 setLoading('btnLogin', true);
339 try {
340 const res = await fetch(`${API}/api/auth/login`, {
341 method: 'POST',
342 headers: { 'Content-Type': 'application/json' },
343 credentials: 'include',
344 body: JSON.stringify({ email, password })
345 });
346 const data = await res.json();
347
348 if (!res.ok) {
349 showAlert('loginError', data.error || 'Login failed.');
350 } else {
351 showAlert('loginSuccess', `Welcome back, ${data.user.first_name}! Redirecting...`);
352 setTimeout(() => { window.location.href = 'Wedding_Planner.html'; }, 900);
353 }
354 } catch (err) {
355 showAlert('loginError', 'Cannot connect to server. Is it running?');
356 } finally {
357 setLoading('btnLogin', false);
358 }
359 }
360
361 // โ”€โ”€ Register โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
362 async function doRegister() {
363 clearAlerts();
364 const first_name = document.getElementById('regFirst').value.trim();
365 const last_name = document.getElementById('regLast').value.trim();
366 const email = document.getElementById('regEmail').value.trim();
367 const password = document.getElementById('regPassword').value;
368 const phone_number = document.getElementById('regPhone').value.trim();
369 const gender = document.getElementById('regGender').value;
370 const birthday = document.getElementById('regBirthday').value;
371
372 if (!first_name || !last_name || !email || !password) {
373 showAlert('registerError', 'First name, last name, email and password are required.');
374 return;
375 }
376 if (password.length < 6) {
377 showAlert('registerError', 'Password must be at least 6 characters.');
378 return;
379 }
380
381 setLoading('btnRegister', true);
382 try {
383 const res = await fetch(`${API}/api/auth/register`, {
384 method: 'POST',
385 headers: { 'Content-Type': 'application/json' },
386 credentials: 'include',
387 body: JSON.stringify({ first_name, last_name, email, password, phone_number, gender, birthday })
388 });
389 const data = await res.json();
390
391 if (!res.ok) {
392 showAlert('registerError', data.error || 'Registration failed.');
393 } else {
394 showAlert('registerSuccess', `Account created! Welcome, ${data.user.first_name}! Redirecting...`);
395 setTimeout(() => { window.location.href = 'Wedding_Planner.html'; }, 1200);
396 }
397 } catch (err) {
398 showAlert('registerError', 'Cannot connect to server. Is it running?');
399 } finally {
400 setLoading('btnRegister', false);
401 }
402 }
403
404 // โ”€โ”€ Allow Enter key to submit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
405 document.addEventListener('keydown', e => {
406 if (e.key === 'Enter') {
407 const loginActive = document.getElementById('panelLogin').classList.contains('active');
408 if (loginActive) doLogin(); else doRegister();
409 }
410 });
411</script>
412</body>
413</html>
Note: See TracBrowser for help on using the repository browser.