File: /home/niyknzcu/nexlancedigital.com/claude - Copy/reset-password.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Reset Password — Nexlance</title>
<link rel="stylesheet" href="login.css">
<style>
.reset-page {
min-height: 100vh;
background: #f3efff;
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
}
.reset-card {
max-width: 440px;
width: 100%;
background: #fff;
border-radius: 20px;
padding: 52px 48px;
box-shadow: 0 8px 40px rgba(108, 92, 231, 0.13);
}
.state-msg {
text-align: center;
padding: 24px 0;
}
.state-msg .icon { font-size: 2.5rem; display: block; margin-bottom: 10px; }
.state-msg strong { display: block; font-size: 1.1rem; color: #333; margin-bottom: 8px; }
.state-msg p { font-size: 0.9rem; color: #777; line-height: 1.6; }
.reset-title { font-size: 1.3rem; font-weight: 800; color: #6c5ce7; margin-bottom: 4px; }
.reset-sub { font-size: 0.87rem; color: #999; margin-bottom: 24px; line-height: 1.5; }
#loadingState, #errorState, #successState, #resetForm { display: none; }
@media (max-width: 520px) {
.reset-card { padding: 36px 24px; }
}
</style>
</head>
<body>
<div class="reset-page">
<div class="reset-card">
<!-- Brand -->
<div style="margin-bottom:28px;">
<div class="logo" style="font-size:1.4rem;font-weight:800;color:#6c5ce7;">◆ Nexlance</div>
</div>
<!-- Loading -->
<div id="loadingState" class="state-msg">
<span class="icon">⏳</span>
<strong>Verifying reset link…</strong>
<p>Please wait a moment.</p>
</div>
<!-- Invalid / expired link -->
<div id="errorState" class="state-msg">
<span class="icon">⚠️</span>
<strong>Link expired or invalid</strong>
<p>This password reset link has expired or already been used.<br><br>
<a href="login.html" class="link-muted" style="color:#6c5ce7;font-weight:600;">Request a new reset link →</a>
</p>
</div>
<!-- Success -->
<div id="successState" class="state-msg">
<span class="icon">✅</span>
<strong>Password updated!</strong>
<p>Your password has been changed successfully.<br><br>
<a href="login.html" class="link-muted" style="color:#6c5ce7;font-weight:600;">Sign in with your new password →</a>
</p>
</div>
<!-- Reset Form (shown after oobCode verified) -->
<div id="resetForm">
<div style="font-size:2rem;margin-bottom:10px;">🔑</div>
<div class="reset-title">Set New Password</div>
<p class="reset-sub">Choose a strong password for your account.</p>
<div style="display:flex;flex-direction:column;gap:20px;">
<label style="display:flex;flex-direction:column;gap:7px;font-size:.85rem;font-weight:600;color:#555;">
New Password
<div class="password-row">
<input id="newPassword" type="password" placeholder="Min 8 characters" autocomplete="new-password">
<button type="button" id="toggleNew" class="small-btn">Show</button>
</div>
<span style="font-size:.76rem;color:#aaa;line-height:1.4;">
Must contain: uppercase · lowercase · number · special character
</span>
<span class="field-error" id="newPasswordError"></span>
</label>
<label style="display:flex;flex-direction:column;gap:7px;font-size:.85rem;font-weight:600;color:#555;">
Confirm Password
<div class="password-row">
<input id="confirmPassword" type="password" placeholder="Re-enter password" autocomplete="new-password">
<button type="button" id="toggleConfirm" class="small-btn">Show</button>
</div>
<span class="field-error" id="confirmPasswordError"></span>
</label>
<button class="btn-primary btn-block" type="button" id="updatePasswordBtn">
Update Password
</button>
<div id="resetMessage" class="form-message" aria-live="polite"></div>
</div>
</div>
</div>
</div>
<!-- Firebase SDK -->
<script src="https://www.gstatic.com/firebasejs/10.14.1/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.14.1/firebase-auth-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.14.1/firebase-firestore-compat.js"></script>
<script src="supabase-config.js"></script>
<script>
document.addEventListener('DOMContentLoaded', async () => {
const loadingEl = document.getElementById('loadingState');
const errorEl = document.getElementById('errorState');
const successEl = document.getElementById('successState');
const resetForm = document.getElementById('resetForm');
function showState(state) {
loadingEl.style.display = state === 'loading' ? 'block' : 'none';
errorEl.style.display = state === 'error' ? 'block' : 'none';
successEl.style.display = state === 'success' ? 'block' : 'none';
resetForm.style.display = state === 'form' ? 'block' : 'none';
}
/* ── Firebase not configured ── */
if (!isFirebaseConfigured) {
showState('error');
errorEl.innerHTML = `
<span class="icon">⚙️</span>
<strong>Firebase not configured</strong>
<p>Add your Firebase credentials in <code>supabase-config.js</code> to enable password reset.</p>`;
return;
}
showState('loading');
/* ── Read oobCode from URL (Firebase appends ?mode=resetPassword&oobCode=xxxx) ── */
const params = new URLSearchParams(window.location.search);
const mode = params.get('mode');
const oobCode = params.get('oobCode');
if (mode !== 'resetPassword' || !oobCode) {
showState('error');
return;
}
/* ── Verify the code is still valid ── */
let accountEmail = '';
try {
accountEmail = await auth.verifyPasswordResetCode(oobCode);
showState('form');
} catch (e) {
showState('error');
return;
}
/* ── Password validation ── */
function validatePassword(p) {
if (!p) return 'Password is required.';
if (p.length < 8) return 'Password must be at least 8 characters.';
if (!/[A-Z]/.test(p)) return 'Must contain at least one uppercase letter.';
if (!/[a-z]/.test(p)) return 'Must contain at least one lowercase letter.';
if (!/[0-9]/.test(p)) return 'Must contain at least one number.';
if (!/[!@#$%^&*()\-_=+\[\]{}|;:'",.<>?/`~\\]/.test(p))
return 'Must contain at least one special character (e.g. @, #, $, !).';
return null;
}
function showErr(id, msg) {
const el = document.getElementById(id);
if (el) { el.textContent = msg; el.style.display = 'block'; }
const inp = document.getElementById(id.replace('Error', ''));
if (inp) inp.classList.add('input-error');
}
function clearErr(id) {
const el = document.getElementById(id);
if (el) { el.textContent = ''; el.style.display = 'none'; }
const inp = document.getElementById(id.replace('Error', ''));
if (inp) inp.classList.remove('input-error');
}
/* ── Toggle show/hide ── */
function setupToggle(btnId, inputId) {
const btn = document.getElementById(btnId);
const inp = document.getElementById(inputId);
if (!btn || !inp) return;
btn.addEventListener('click', () => {
inp.type = inp.type === 'password' ? 'text' : 'password';
btn.textContent = inp.type === 'password' ? 'Show' : 'Hide';
});
}
setupToggle('toggleNew', 'newPassword');
setupToggle('toggleConfirm', 'confirmPassword');
/* ── Live confirm match ── */
document.getElementById('confirmPassword').addEventListener('input', () => {
const np = document.getElementById('newPassword').value;
const cp = document.getElementById('confirmPassword').value;
if (!cp) { clearErr('confirmPasswordError'); return; }
np !== cp ? showErr('confirmPasswordError', 'Passwords do not match.') : clearErr('confirmPasswordError');
});
/* ── Submit new password ── */
document.getElementById('updatePasswordBtn').addEventListener('click', async () => {
clearErr('newPasswordError');
clearErr('confirmPasswordError');
const msg = document.getElementById('resetMessage');
msg.textContent = ''; msg.className = 'form-message';
const np = document.getElementById('newPassword').value;
const cp = document.getElementById('confirmPassword').value;
const pwErr = validatePassword(np);
if (pwErr) { showErr('newPasswordError', pwErr); return; }
if (np !== cp) { showErr('confirmPasswordError', 'Passwords do not match.'); return; }
const btn = document.getElementById('updatePasswordBtn');
btn.disabled = true; btn.textContent = 'Updating…';
try {
// Firebase confirms the password reset using the oobCode
await auth.confirmPasswordReset(oobCode, np);
showState('success');
// Auto-redirect to login after 3 s
setTimeout(() => { window.location.href = 'login.html'; }, 3000);
} catch (err) {
btn.disabled = false; btn.textContent = 'Update Password';
if (err.code === 'auth/expired-action-code' || err.code === 'auth/invalid-action-code') {
showState('error');
} else {
msg.textContent = 'Something went wrong. Please request a new reset link.';
msg.className = 'form-message error';
}
}
});
});
</script>
</body>
</html>