earwigmoth commited on
Commit
fbcba92
Β·
verified Β·
1 Parent(s): f0146d8
Files changed (3) hide show
  1. login.css +28 -0
  2. login.html +113 -0
  3. login.js +170 -0
login.css ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* ============================================================
2
+ SpeakFlow AI – login.css (extends signup.css)
3
+ ============================================================ */
4
+
5
+ .form-label{justify-content:space-between;}
6
+ .forgot-link{background:none;border:none;color:var(--blue-light);font-size:0.8rem;font-weight:600;cursor:pointer;padding:0;margin-left:auto;}
7
+ .forgot-link:hover{text-decoration:underline;}
8
+
9
+ /* ── Remember Me ─────────────────────────────────────────── */
10
+ .remember-row{display:flex;align-items:center;margin-bottom:4px;}
11
+ .check-label{display:flex;align-items:center;gap:10px;cursor:pointer;font-size:0.9rem;color:var(--text-muted);user-select:none;}
12
+ .check-label input{display:none;}
13
+ .check-custom{width:18px;height:18px;border:1.5px solid var(--border);border-radius:5px;display:flex;align-items:center;justify-content:center;transition:all var(--transition);background:rgba(30,41,59,.8);flex-shrink:0;}
14
+ .check-label input:checked + .check-custom{background:var(--blue);border-color:var(--blue);}
15
+ .check-label input:checked + .check-custom::after{content:'βœ“';color:#fff;font-size:.75rem;font-weight:700;}
16
+
17
+ /* ── Admin hint ──────────────────────────────────────────── */
18
+ .admin-hint{margin-top:16px;padding:12px 16px;background:rgba(124,58,237,.12);border:1px solid rgba(124,58,237,.25);border-radius:10px;font-size:.83rem;color:var(--text-muted);display:flex;align-items:center;gap:8px;}
19
+ .admin-hint i{color:#A78BFA;}
20
+ .admin-hint strong{color:#A78BFA;}
21
+
22
+ /* ── Forgot Modal ────────────────────────────────────────── */
23
+ .modal-overlay{position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,.6);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;padding:16px;}
24
+ .modal-box{background:rgba(15,23,42,.92);backdrop-filter:blur(20px);border:1px solid var(--border);border-radius:20px;padding:28px;width:100%;max-width:400px;box-shadow:var(--shadow-lg);}
25
+ .modal-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;}
26
+ .modal-header h3{font-size:1.1rem;font-weight:700;display:flex;align-items:center;gap:9px;}
27
+ .sf-modal-close{background:rgba(255,255,255,.07);border:none;color:var(--text-muted);width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:.9rem;}
28
+ .sf-modal-close:hover{background:rgba(255,255,255,.12);}
login.html ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="dark">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Login – SpeakFlow AI</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" />
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet" />
9
+ <link rel="stylesheet" href="signup.css" />
10
+ <link rel="stylesheet" href="login.css" />
11
+ </head>
12
+ <body>
13
+
14
+ <!-- Animated Background -->
15
+ <div class="auth-bg">
16
+ <div class="auth-orb orb-1"></div>
17
+ <div class="auth-orb orb-2"></div>
18
+ <div class="auth-orb orb-3"></div>
19
+ <div class="auth-particles" id="authParticles"></div>
20
+ </div>
21
+
22
+ <!-- Card -->
23
+ <div class="auth-wrapper">
24
+ <div class="auth-card glass-card">
25
+
26
+ <!-- Logo -->
27
+ <div class="auth-logo">
28
+ <div class="auth-logo-icon"><i class="fas fa-wave-square"></i></div>
29
+ <div>
30
+ <span class="auth-logo-text">SpeakFlow <span class="logo-ai">AI</span></span>
31
+ <p class="auth-logo-sub">AI-Powered Speech Analysis</p>
32
+ </div>
33
+ </div>
34
+
35
+ <h1 class="auth-title">Welcome Back</h1>
36
+ <p class="auth-subtitle">Sign in to your account to continue analyzing speech.</p>
37
+
38
+ <form id="loginForm" novalidate autocomplete="off">
39
+
40
+ <!-- Email -->
41
+ <div class="form-group" id="fg-email">
42
+ <label class="form-label"><i class="fas fa-envelope"></i> Email Address</label>
43
+ <div class="input-wrap">
44
+ <input type="email" id="loginEmail" class="form-input" placeholder="you@example.com" autocomplete="email" />
45
+ </div>
46
+ <span class="form-error" id="emailErr"></span>
47
+ </div>
48
+
49
+ <!-- Password -->
50
+ <div class="form-group" id="fg-pass">
51
+ <label class="form-label">
52
+ <i class="fas fa-lock"></i> Password
53
+ <button type="button" class="forgot-link" id="forgotBtn">Forgot Password?</button>
54
+ </label>
55
+ <div class="input-wrap">
56
+ <input type="password" id="loginPass" class="form-input" placeholder="Your password" autocomplete="current-password" />
57
+ <button type="button" class="toggle-pass" id="togglePass" tabindex="-1">
58
+ <i class="fas fa-eye"></i>
59
+ </button>
60
+ </div>
61
+ <span class="form-error" id="passErr"></span>
62
+ </div>
63
+
64
+ <!-- Remember Me -->
65
+ <div class="remember-row">
66
+ <label class="check-label">
67
+ <input type="checkbox" id="rememberMe" />
68
+ <span class="check-custom"></span>
69
+ Remember me
70
+ </label>
71
+ </div>
72
+
73
+ <p class="form-error text-center" id="globalErr"></p>
74
+
75
+ <button type="submit" class="btn-auth" id="loginBtn">
76
+ <span id="loginBtnText"><i class="fas fa-sign-in-alt"></i> Login</span>
77
+ <span id="loginBtnLoader" style="display:none;"><i class="fas fa-spinner fa-spin"></i> Signing in...</span>
78
+ </button>
79
+ </form>
80
+
81
+ <!-- Admin hint -->
82
+ <div class="admin-hint">
83
+ <i class="fas fa-shield-alt"></i>
84
+ Admin? Use <strong>admin@speakflow.ai</strong> to access the dashboard.
85
+ </div>
86
+
87
+ <div class="auth-divider"><span>New to SpeakFlow AI?</span></div>
88
+ <a href="signup.html" class="btn-auth-outline">
89
+ <i class="fas fa-user-plus"></i> Create New Account
90
+ </a>
91
+
92
+ </div>
93
+ </div>
94
+
95
+ <!-- Forgot Password Modal -->
96
+ <div id="forgotModal" class="modal-overlay" style="display:none;">
97
+ <div class="modal-box glass-card">
98
+ <div class="modal-header">
99
+ <h3><i class="fas fa-key"></i> Reset Password</h3>
100
+ <button id="forgotClose" class="sf-modal-close"><i class="fas fa-times"></i></button>
101
+ </div>
102
+ <p style="color:var(--text-muted);font-size:.9rem;margin-bottom:16px;">Enter your email and we'll simulate a password reset (demo only).</p>
103
+ <input type="email" id="forgotEmail" class="form-input" placeholder="your@email.com" />
104
+ <button class="btn-auth" style="margin-top:16px;" id="forgotSubmit">
105
+ <i class="fas fa-paper-plane"></i> Send Reset Link
106
+ </button>
107
+ <p id="forgotMsg" style="margin-top:10px;font-size:.85rem;display:none;"></p>
108
+ </div>
109
+ </div>
110
+
111
+ <script src="login.js"></script>
112
+ </body>
113
+ </html>
login.js ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* ============================================================
2
+ SpeakFlow AI – login.js
3
+ ============================================================ */
4
+ 'use strict';
5
+
6
+ // Admin credentials
7
+ const ADMIN_EMAIL = 'admin@speakflow.ai';
8
+ const ADMIN_PASS = 'admin123';
9
+
10
+ // Redirect if already logged in
11
+ (function(){
12
+ const s = JSON.parse(localStorage.getItem('sf_session')||'null');
13
+ if(s && s.loggedIn){
14
+ if(s.isAdmin) window.location.href='admin.html';
15
+ else window.location.href='index.html';
16
+ }
17
+ })();
18
+
19
+ /* ── Particles ─────────────────────────────────────────────── */
20
+ (function(){
21
+ const c=document.getElementById('authParticles');
22
+ for(let i=0;i<20;i++){
23
+ const p=document.createElement('div');p.className='particle';
24
+ const sz=Math.random()*5+2;
25
+ Object.assign(p.style,{width:sz+'px',height:sz+'px',left:Math.random()*100+'%',bottom:'-10px',animationDuration:(Math.random()*20+15)+'s',animationDelay:(Math.random()*10)+'s',opacity:Math.random()*.4+.1});
26
+ c.appendChild(p);
27
+ }
28
+ })();
29
+
30
+ /* ── Remember Me pre-fill ──────────────────────────────────── */
31
+ const remembered = localStorage.getItem('sf_remember');
32
+ if(remembered){
33
+ document.getElementById('loginEmail').value = remembered;
34
+ document.getElementById('rememberMe').checked = true;
35
+ }
36
+
37
+ document.addEventListener('DOMContentLoaded', () => {
38
+
39
+ const togglePass = document.getElementById('togglePass');
40
+ const loginForm = document.getElementById('loginForm');
41
+ const forgotBtn = document.getElementById('forgotBtn');
42
+ const forgotClose = document.getElementById('forgotClose');
43
+ const forgotModal = document.getElementById('forgotModal');
44
+ const forgotSubmit = document.getElementById('forgotSubmit');
45
+
46
+ // Show/Hide Password
47
+ if (togglePass) {
48
+ togglePass.addEventListener('click', () => {
49
+ const inp = document.getElementById('loginPass');
50
+ const icon = document.querySelector('#togglePass i');
51
+ const show = inp.type === 'password';
52
+ inp.type = show ? 'text' : 'password';
53
+ icon.className = show ? 'fas fa-eye-slash' : 'fas fa-eye';
54
+ });
55
+ }
56
+
57
+ // Login Form
58
+ if (loginForm) {
59
+ loginForm.addEventListener('submit', function (e) {
60
+ e.preventDefault();
61
+
62
+ const email = document.getElementById('loginEmail').value.trim().toLowerCase();
63
+ const pass = document.getElementById('loginPass').value;
64
+ const remember = document.getElementById('rememberMe').checked;
65
+ const globalErr = document.getElementById('globalErr');
66
+ const btnText = document.getElementById('loginBtnText');
67
+ const btnLoader = document.getElementById('loginBtnLoader');
68
+
69
+ if (globalErr) globalErr.textContent = '';
70
+
71
+ let valid = true;
72
+
73
+ if (!email) {
74
+ document.getElementById('emailErr').textContent = 'Email is required.';
75
+ valid = false;
76
+ } else {
77
+ document.getElementById('emailErr').textContent = '';
78
+ }
79
+
80
+ if (!pass) {
81
+ document.getElementById('passErr').textContent = 'Password is required.';
82
+ valid = false;
83
+ } else {
84
+ document.getElementById('passErr').textContent = '';
85
+ }
86
+
87
+ if (!valid) return;
88
+
89
+ if (btnText) btnText.style.display = 'none';
90
+ if (btnLoader) btnLoader.style.display = 'flex';
91
+
92
+ setTimeout(() => {
93
+
94
+ // Admin check
95
+ if (email === ADMIN_EMAIL && pass === ADMIN_PASS) {
96
+ if (remember) localStorage.setItem('sf_remember', email);
97
+ else localStorage.removeItem('sf_remember');
98
+
99
+ localStorage.setItem('sf_session', JSON.stringify({
100
+ loggedIn: true,
101
+ name: 'Admin',
102
+ email,
103
+ isAdmin: true
104
+ }));
105
+
106
+ window.location.href = 'admin.html';
107
+ return;
108
+ }
109
+
110
+ // User check
111
+ const users = JSON.parse(localStorage.getItem('sf_users') || '[]');
112
+ const user = users.find(u => u.email === email && u.password === pass);
113
+
114
+ if (btnText) btnText.style.display = 'flex';
115
+ if (btnLoader) btnLoader.style.display = 'none';
116
+
117
+ if (!user) {
118
+ if (globalErr) globalErr.textContent = '❌ Incorrect email or password.';
119
+ return;
120
+ }
121
+
122
+ if (remember) localStorage.setItem('sf_remember', email);
123
+ else localStorage.removeItem('sf_remember');
124
+
125
+ localStorage.setItem('sf_session', JSON.stringify({
126
+ loggedIn: true,
127
+ name: user.name,
128
+ email: user.email,
129
+ id: user.id,
130
+ isAdmin: false
131
+ }));
132
+
133
+ window.location.href = 'index.html';
134
+
135
+ }, 800);
136
+ });
137
+ }
138
+
139
+ // Forgot modal
140
+ if (forgotBtn && forgotModal) {
141
+ forgotBtn.addEventListener('click', () => {
142
+ forgotModal.style.display = 'flex';
143
+ });
144
+
145
+ forgotClose?.addEventListener('click', () => {
146
+ forgotModal.style.display = 'none';
147
+ });
148
+
149
+ forgotModal.addEventListener('click', function (e) {
150
+ if (e.target === this) this.style.display = 'none';
151
+ });
152
+
153
+ forgotSubmit?.addEventListener('click', () => {
154
+ const email = document.getElementById('forgotEmail').value.trim().toLowerCase();
155
+ const msg = document.getElementById('forgotMsg');
156
+
157
+ if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
158
+ msg.textContent = '❌ Enter a valid email.';
159
+ msg.style.color = 'var(--red)';
160
+ msg.style.display = 'block';
161
+ return;
162
+ }
163
+
164
+ msg.textContent = 'βœ… (Demo) Reset link sent to ' + email;
165
+ msg.style.color = 'var(--green)';
166
+ msg.style.display = 'block';
167
+ });
168
+ }
169
+
170
+ });