Fix: otp verification

This commit is contained in:
hyzen
2026-06-07 00:30:37 +05:30
parent 28a6e82bfc
commit b05f1a731d
4 changed files with 80 additions and 286 deletions

View File

@@ -199,75 +199,6 @@
color: #fff; color: #fff;
} }
/* ── OTP step indicator ─────────────────────────────────────────────────── */
.otp-steps {
display: flex;
align-items: center;
margin-bottom: 1.6rem;
}
.otp-steps__line {
flex: 1;
height: 2px;
background: var(--background-color1, #ccc);
transition: background 0.25s ease;
}
.otp-steps__item {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.3rem;
}
.otp-steps__num {
width: 1.8rem;
height: 1.8rem;
border-radius: 50%;
border: 2px solid var(--background-color1, #ccc);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.78rem;
font-weight: 700;
color: var(--foreground-color3, #888);
background: var(--background-color);
transition:
border-color 0.25s ease,
color 0.25s ease,
background 0.25s ease;
}
.otp-steps__label {
font-size: 0.7rem;
color: var(--foreground-color3, #888);
white-space: nowrap;
transition: color 0.25s ease;
}
/* Active step */
.otp-steps__item--active .otp-steps__num {
border-color: var(--accent-color);
color: var(--accent-color);
}
.otp-steps__item--active .otp-steps__label {
color: var(--accent-color);
font-weight: 600;
}
/* Completed step */
.otp-steps__item--done .otp-steps__num {
border-color: var(--accent-color);
background: var(--accent-color);
color: #fff;
}
.otp-steps__item--done .otp-steps__label {
color: var(--accent-color);
}
/* ── OTP panel ───────────────────────────────────────────────────────────── */ /* ── OTP panel ───────────────────────────────────────────────────────────── */
.otp-panel__hint { .otp-panel__hint {

File diff suppressed because one or more lines are too long

View File

@@ -5,27 +5,8 @@
<div class="auth-card"> <div class="auth-card">
<div id="auth-message" class="auth-message" aria-live="polite" style="display: none"></div> <div id="auth-message" class="auth-message" aria-live="polite" style="display: none"></div>
<!-- ── Step indicator ─────────────────────────────────────────── -->
<div class="otp-steps" id="otp-steps" aria-label="Sign-up steps">
<div class="otp-steps__item otp-steps__item--active" id="step-ind-1">
<span class="otp-steps__num">1</span>
<span class="otp-steps__label">Details</span>
</div>
<div class="otp-steps__line"></div>
<div class="otp-steps__item" id="step-ind-2">
<span class="otp-steps__num">2</span>
<span class="otp-steps__label">Verify email</span>
</div>
<div class="otp-steps__line"></div>
<div class="otp-steps__item" id="step-ind-3">
<span class="otp-steps__num">3</span>
<span class="otp-steps__label">Done</span>
</div>
</div>
<!-- ── STEP 1 — Account details ───────────────────────────────── --> <!-- ── STEP 1 — Account details ───────────────────────────────── -->
<form id="signup-form" class="auth-form" novalidate> <form id="signup-form" class="auth-form" novalidate>
<div class="auth-form__input-wrap">
<div class="auth-form__group"> <div class="auth-form__group">
<label class="auth-form__label" for="signup-username">Username</label> <label class="auth-form__label" for="signup-username">Username</label>
<input <input
@@ -58,6 +39,7 @@
<div class="auth-form__group"> <div class="auth-form__group">
<label class="auth-form__label" for="signup-password">Password</label> <label class="auth-form__label" for="signup-password">Password</label>
<div class="auth-form__input-wrap">
<input <input
class="auth-form__input" class="auth-form__input"
type="password" type="password"
@@ -99,6 +81,7 @@
/> />
</svg> </svg>
</button> </button>
</div>
<span class="auth-form__hint">(at least 8 characters)</span> <span class="auth-form__hint">(at least 8 characters)</span>
</div> </div>
@@ -113,7 +96,6 @@
required required
/> />
</div> </div>
</div>
<button type="submit" class="auth-form__submit" id="signup-submit"> <button type="submit" class="auth-form__submit" id="signup-submit">
<span class="auth-form__submit-text">Send verification code</span> <span class="auth-form__submit-text">Send verification code</span>
@@ -194,15 +176,8 @@
(function () { (function () {
var BACKEND = 'https://backend.freedoms4.org/auth.php'; var BACKEND = 'https://backend.freedoms4.org/auth.php';
// ── State ────────────────────────────────────────────────────── var state = { username: '', email: '', password: '' };
var state = {
username: '',
email: '',
password: '',
otpToken: null,
};
// ── DOM refs ───────────────────────────────────────────────────
var msgBox = document.getElementById('auth-message'); var msgBox = document.getElementById('auth-message');
var signupForm = document.getElementById('signup-form'); var signupForm = document.getElementById('signup-form');
var signupSubmit = document.getElementById('signup-submit'); var signupSubmit = document.getElementById('signup-submit');
@@ -215,13 +190,6 @@
var pwdInput = document.getElementById('signup-password'); var pwdInput = document.getElementById('signup-password');
var eyeBtn = signupForm.querySelector('.auth-form__eye'); var eyeBtn = signupForm.querySelector('.auth-form__eye');
var stepInds = [
document.getElementById('step-ind-1'),
document.getElementById('step-ind-2'),
document.getElementById('step-ind-3'),
];
// ── Helpers ────────────────────────────────────────────────────
function showMsg(text, type) { function showMsg(text, type) {
msgBox.textContent = text; msgBox.textContent = text;
msgBox.className = 'auth-message auth-message--' + type; msgBox.className = 'auth-message auth-message--' + type;
@@ -231,22 +199,15 @@
msgBox.style.display = 'none'; msgBox.style.display = 'none';
} }
function setLoading(btn, loading) { function setLoading(btn, on) {
btn.disabled = loading; btn.disabled = on;
btn.querySelector('.auth-form__submit-text').style.display = loading ? 'none' : ''; btn.querySelector('.auth-form__submit-text').style.display = on ? 'none' : '';
btn.querySelector('.auth-form__submit-loader').style.display = loading btn.querySelector('.auth-form__submit-loader').style.display = on
? 'inline-flex' ? 'inline-flex'
: 'none'; : 'none';
} }
function setStep(n) { // Password eye toggle
stepInds.forEach(function (el, i) {
el.classList.toggle('otp-steps__item--active', i + 1 === n);
el.classList.toggle('otp-steps__item--done', i + 1 < n);
});
}
// ── Password eye toggle ────────────────────────────────────────
eyeBtn.addEventListener('click', function () { eyeBtn.addEventListener('click', function () {
var isText = pwdInput.type === 'text'; var isText = pwdInput.type === 'text';
pwdInput.type = isText ? 'password' : 'text'; pwdInput.type = isText ? 'password' : 'text';
@@ -254,26 +215,27 @@
eyeBtn.querySelector('.eye-hide').style.display = isText ? 'none' : ''; eyeBtn.querySelector('.eye-hide').style.display = isText ? 'none' : '';
}); });
// ── Resend cooldown ──────────────────────────────────────────── // Resend cooldown
var resendTimeout = null; var resendInterval = null;
function startResendCooldown(seconds) { function startResendCooldown(seconds) {
otpResendBtn.style.display = 'none'; otpResendBtn.style.display = 'none';
otpResendTimer.style.display = ''; otpResendTimer.style.display = '';
var remaining = seconds; var remaining = seconds;
otpResendTimer.textContent = 'Resend in ' + remaining + 's'; otpResendTimer.textContent = 'Resend in ' + remaining + 's';
resendTimeout = setInterval(function () { resendInterval = setInterval(function () {
remaining--; remaining--;
if (remaining <= 0) { if (remaining <= 0) {
clearInterval(resendTimeout); clearInterval(resendInterval);
otpResendTimer.style.display = 'none'; otpResendTimer.style.display = 'none';
otpResendBtn.style.display = ''; otpResendBtn.style.display = '';
otpResendBtn.disabled = false;
} else { } else {
otpResendTimer.textContent = 'Resend in ' + remaining + 's'; otpResendTimer.textContent = 'Resend in ' + remaining + 's';
} }
}, 1000); }, 1000);
} }
// ── STEP 1 submit — validate + send_otp ─────────────────────── // STEP 1 — validate fields and send OTP
signupForm.addEventListener('submit', function (e) { signupForm.addEventListener('submit', function (e) {
e.preventDefault(); e.preventDefault();
hideMsg(); hideMsg();
@@ -320,13 +282,11 @@
}) })
.then(function (data) { .then(function (data) {
if (data.success) { if (data.success) {
// Switch to OTP panel
signupForm.style.display = 'none'; signupForm.style.display = 'none';
otpEmailDisp.textContent = email; otpEmailDisp.textContent = email;
otpPanel.style.display = ''; otpPanel.style.display = '';
otpInput.value = ''; otpInput.value = '';
otpInput.focus(); otpInput.focus();
setStep(2);
startResendCooldown(60); startResendCooldown(60);
showMsg('Code sent! Check your inbox (and spam folder).', 'success'); showMsg('Code sent! Check your inbox (and spam folder).', 'success');
} else { } else {
@@ -341,7 +301,7 @@
}); });
}); });
// ── Resend button ────────────────────────────────────────────── // Resend
otpResendBtn.addEventListener('click', function () { otpResendBtn.addEventListener('click', function () {
hideMsg(); hideMsg();
otpResendBtn.disabled = true; otpResendBtn.disabled = true;
@@ -370,8 +330,8 @@
}); });
}); });
// ── STEP 2 submit — verify_otp then signup ──────────────────── // STEP 2 — verify OTP and create account in one request
otpSubmit.addEventListener('click', function () { function doSignup() {
hideMsg(); hideMsg();
var otp = otpInput.value.trim(); var otp = otpInput.value.trim();
@@ -382,26 +342,7 @@
setLoading(otpSubmit, true); setLoading(otpSubmit, true);
// 2a. Verify OTP → get token
fetch(BACKEND, { fetch(BACKEND, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ action: 'verify_otp', email: state.email, otp: otp }),
})
.then(function (r) {
return r.json();
})
.then(function (data) {
if (!data.success) {
showMsg(data.message || 'Invalid or expired code.', 'error');
setLoading(otpSubmit, false);
return;
}
state.otpToken = data.otp_token;
// 2b. Create account
return fetch(BACKEND, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
credentials: 'include', credentials: 'include',
@@ -410,35 +351,32 @@
username: state.username, username: state.username,
email: state.email, email: state.email,
password: state.password, password: state.password,
otp_token: state.otpToken, otp: otp,
}), }),
}) })
.then(function (r) { .then(function (r) {
return r.json(); return r.json();
}) })
.then(function (d) { .then(function (data) {
if (d.success) { if (data.success) {
setStep(3);
otpPanel.style.display = 'none';
showMsg('Account created! Redirecting to login\u2026', 'success'); showMsg('Account created! Redirecting to login\u2026', 'success');
setTimeout(function () { setTimeout(function () {
window.location.href = '/login/'; window.location.href = '/login/';
}, 1800); }, 1800);
} else { } else {
showMsg(d.message || 'Sign-up failed. Please try again.', 'error'); showMsg(data.message || 'Sign-up failed. Please try again.', 'error');
setLoading(otpSubmit, false); setLoading(otpSubmit, false);
} }
});
}) })
.catch(function () { .catch(function () {
showMsg('Network error. Please try again.', 'error'); showMsg('Network error. Please try again.', 'error');
setLoading(otpSubmit, false); setLoading(otpSubmit, false);
}); });
}); }
// Allow pressing Enter in OTP box to submit otpSubmit.addEventListener('click', doSignup);
otpInput.addEventListener('keydown', function (e) { otpInput.addEventListener('keydown', function (e) {
if (e.key === 'Enter') otpSubmit.click(); if (e.key === 'Enter') doSignup();
}); });
})(); })();
</script> </script>

View File

@@ -199,75 +199,6 @@
color: #fff; color: #fff;
} }
/* ── OTP step indicator ─────────────────────────────────────────────────── */
.otp-steps {
display: flex;
align-items: center;
margin-bottom: 1.6rem;
}
.otp-steps__line {
flex: 1;
height: 2px;
background: var(--background-color1, #ccc);
transition: background 0.25s ease;
}
.otp-steps__item {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.3rem;
}
.otp-steps__num {
width: 1.8rem;
height: 1.8rem;
border-radius: 50%;
border: 2px solid var(--background-color1, #ccc);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.78rem;
font-weight: 700;
color: var(--foreground-color3, #888);
background: var(--background-color);
transition:
border-color 0.25s ease,
color 0.25s ease,
background 0.25s ease;
}
.otp-steps__label {
font-size: 0.7rem;
color: var(--foreground-color3, #888);
white-space: nowrap;
transition: color 0.25s ease;
}
/* Active step */
.otp-steps__item--active .otp-steps__num {
border-color: var(--accent-color);
color: var(--accent-color);
}
.otp-steps__item--active .otp-steps__label {
color: var(--accent-color);
font-weight: 600;
}
/* Completed step */
.otp-steps__item--done .otp-steps__num {
border-color: var(--accent-color);
background: var(--accent-color);
color: #fff;
}
.otp-steps__item--done .otp-steps__label {
color: var(--accent-color);
}
/* ── OTP panel ───────────────────────────────────────────────────────────── */ /* ── OTP panel ───────────────────────────────────────────────────────────── */
.otp-panel__hint { .otp-panel__hint {