GhostManSec
Server: LiteSpeed
System: Linux premium256.web-hosting.com 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13 UTC 2025 x86_64
User: niyknzcu (1843)
PHP: 8.0.30
Disabled: NONE
Upload Files
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>