| | <!DOCTYPE html> |
| | <html lang="en" data-bs-theme="dark"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>GAKR AI - Login/Signup</title> |
| | <link rel="stylesheet" href="https://cdn.replit.com/agent/bootstrap-agent-dark-theme.min.css"> |
| | <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> |
| | <style> |
| | :root { |
| | --gakr-blue: #4285F4; |
| | --gakr-blue-dark: #1a73e8; |
| | --gakr-blue-light: #e8f0fe; |
| | --gakr-grey-text: #5f6368; |
| | --gakr-grey-hover-bg: rgba(95, 99, 104, 0.1); |
| | } |
| | |
| | :root[data-bs-theme="dark"] { |
| | --gakr-blue: #8ab4f8; |
| | --gakr-blue-dark: #669df6; |
| | --gakr-blue-light: rgba(138, 180, 248, 0.1); |
| | --gakr-grey-text: #bdc1c6; |
| | --gakr-grey-hover-bg: rgba(189, 193, 198, 0.1); |
| | } |
| | |
| | @import url('https://fonts.googleapis.com/css?family=Poppins:400,500,600,700&display=swap'); |
| | |
| | * { |
| | margin: 0; |
| | padding: 0; |
| | box-sizing: border-box; |
| | font-family: 'Poppins', sans-serif; |
| | } |
| | |
| | html, body { |
| | display: flex; |
| | flex-direction: column; |
| | align-items: center; |
| | height: 100%; |
| | width: 100%; |
| | background: -webkit-linear-gradient(left, #003366,#004080,#0059b3 , #0073e6); |
| | color: var(--bs-body-color); |
| | padding-top: 5vh; |
| | padding-bottom: 5vh; |
| | min-height: 100vh; |
| | |
| | position: relative; |
| | } |
| | |
| | ::selection { |
| | background: var(--gakr-blue); |
| | color: #fff; |
| | } |
| | |
| | .wrapper { |
| | overflow: hidden; |
| | max-width: 440px; |
| | width: 90%; |
| | background: var(--bs-body-bg); |
| | box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.5); |
| | padding: 30px; |
| | border-radius: 15px; |
| | margin-top: 10px; |
| | margin-bottom: 10px; |
| | z-index: 10; |
| | } |
| | |
| | .wrapper .title-text { |
| | display: flex; |
| | width: 200%; |
| | } |
| | |
| | .wrapper .title { |
| | width: 50%; |
| | font-size: 35px; |
| | font-weight: 600; |
| | text-align: center; |
| | transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); |
| | color: var(--bs-body-color); |
| | } |
| | |
| | .wrapper .slide-controls { |
| | position: relative; |
| | display: flex; |
| | height: 50px; |
| | width: 100%; |
| | overflow: hidden; |
| | margin: 30px 0 10px 0; |
| | justify-content: space-between; |
| | border: 1px solid var(--bs-border-color); |
| | border-radius: 15px; |
| | } |
| | |
| | .slide-controls .slide { |
| | height: 100%; |
| | width: 100%; |
| | color: var(--bs-body-color); |
| | font-size: 18px; |
| | font-weight: 500; |
| | text-align: center; |
| | line-height: 48px; |
| | cursor: pointer; |
| | z-index: 1; |
| | transition: all 0.6s ease; |
| | } |
| | |
| | .slide-controls label.signup{ |
| | color: var(--bs-body-color); |
| | } |
| | |
| | .slide-controls .slider-tab { |
| | position: absolute; |
| | height: 100%; |
| | width: 50%; |
| | left: 0; |
| | z-index: 0; |
| | border-radius: 15px; |
| | background: var(--gakr-blue); |
| | transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); |
| | } |
| | |
| | input[type="radio"]{ |
| | display: none; |
| | } |
| | |
| | #signup:checked ~ .slider-tab{ |
| | left: 50%; |
| | } |
| | #signup:checked ~ label.signup{ |
| | color: #fff; |
| | cursor: default; |
| | user-select: none; |
| | } |
| | #signup:checked ~ label.login{ |
| | color: var(--bs-body-color); |
| | } |
| | #login:checked ~ label.signup{ |
| | color: var(--bs-body-color); |
| | } |
| | #login:checked ~ label.login{ |
| | color: #fff; |
| | cursor: default; |
| | user-select: none; |
| | } |
| | |
| | .wrapper .form-container { |
| | width: 100%; |
| | overflow: hidden; |
| | } |
| | |
| | .form-container .form-inner { |
| | display: flex; |
| | width: 200%; |
| | } |
| | |
| | .form-container .form-inner form { |
| | width: 50%; |
| | transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); |
| | } |
| | |
| | .form-inner form .field { |
| | height: 50px; |
| | width: 100%; |
| | margin-top: 20px; |
| | position: relative; |
| | } |
| | |
| | .form-inner form .field:has(+ .field.btn) { |
| | margin-bottom: 25px; |
| | } |
| | |
| | .form-inner form .field input{ |
| | height: 100%; |
| | width: 100%; |
| | outline: none; |
| | padding-left: 15px; |
| | border-radius: 15px; |
| | border: 1px solid var(--bs-border-color); |
| | border-bottom-width: 2px; |
| | font-size: 17px; |
| | transition: all 0.3s ease; |
| | color: var(--bs-body-color); |
| | background-color: var(--bs-body-bg); |
| | } |
| | |
| | .form-inner form .field input.error { |
| | border-color: red; |
| | } |
| | |
| | .form-inner form .field input::placeholder{ |
| | color: var(--bs-secondary-color); |
| | transition: all 0.3s ease; |
| | } |
| | |
| | form .field input:focus::placeholder{ |
| | color: var(--gakr-blue); |
| | } |
| | |
| | .form-inner form .pass-link{ |
| | margin-top: 5px; |
| | font-size: 0.9rem; |
| | text-align: right; |
| | } |
| | |
| | .form-inner form .signup-link{ |
| | text-align: center; |
| | margin-top: 30px; |
| | font-size: 0.9rem; |
| | } |
| | |
| | .form-inner form .pass-link a, |
| | .form-inner form .signup-link a{ |
| | color: var(--gakr-blue); |
| | text-decoration: none; |
| | transition: color 0.2s ease-in-out; |
| | } |
| | |
| | .form-inner form .pass-link a:hover, |
| | form-inner form .signup-link a:hover{ |
| | text-decoration: underline; |
| | color: var(--gakr-blue-dark); |
| | } |
| | |
| | form .btn { |
| | height: 50px; |
| | width: 100%; |
| | margin-top: 20px; |
| | border-radius: 15px; |
| | position: relative; |
| | overflow: hidden; |
| | background: none; |
| | transition: none; |
| | } |
| | |
| | form .btn input[type="submit"] { |
| | height: 100%; |
| | width: 100%; |
| | z-index: 1; |
| | position: relative; |
| | background: var(--gakr-blue); |
| | border: none; |
| | color: #fff; |
| | padding: 0; |
| | border-radius: 15px; |
| | font-size: 20px; |
| | font-weight: 500; |
| | cursor: pointer; |
| | transition: background-color 0.2s ease-in-out, transform 0.1s ease-in-out; |
| | display: flex; |
| | align-items: center; |
| | justify-content: center; |
| | } |
| | |
| | form .btn input[type="submit"]:hover { |
| | background-color: var(--gakr-blue-dark); |
| | } |
| | |
| | form .btn input[type="submit"]:active { |
| | transform: scale(0.98); |
| | transition: background-color 0s, transform 0.1s; |
| | } |
| | |
| | .error-message { |
| | color: red; |
| | font-size: 0.8rem; |
| | margin-top: 4px; |
| | position: absolute; |
| | bottom: -18px; |
| | left: 15px; |
| | white-space: nowrap; |
| | } |
| | |
| | .form-message { |
| | text-align: center; |
| | margin-top: 20px; |
| | font-size: 0.9rem; |
| | min-height: 1.2em; |
| | } |
| | |
| | .form-message.success { |
| | color: green; |
| | } |
| | |
| | .form-message.error { |
| | color: red; |
| | } |
| | |
| | .loading-spinner { |
| | display: inline-block; |
| | width: 18px; |
| | height: 18px; |
| | border: 3px solid rgba(255, 255, 255, .3); |
| | border-radius: 50%; |
| | border-top-color: #fff; |
| | animation: spin 1s ease-in-out infinite; |
| | margin-left: 10px; |
| | vertical-align: middle; |
| | } |
| | |
| | @keyframes spin { |
| | to { -webkit-transform: rotate(360deg); } |
| | } |
| | |
| | .form-inner form .btn input[type="submit"]:disabled { |
| | opacity: 0.7; |
| | cursor: not-allowed; |
| | position: relative; |
| | } |
| | |
| | |
| | .modal-overlay { |
| | position: fixed; |
| | top: 0; |
| | left: 0; |
| | width: 100%; |
| | height: 100%; |
| | background-color: rgba(0, 0, 0, 0.6); |
| | display: flex; |
| | justify-content: center; |
| | align-items: center; |
| | z-index: 100; |
| | visibility: hidden; |
| | opacity: 0; |
| | transition: visibility 0s, opacity 0.3s ease-in-out; |
| | } |
| | |
| | .modal-overlay.show { |
| | visibility: visible; |
| | opacity: 1; |
| | } |
| | |
| | .modal-content { |
| | background-color: var(--bs-body-bg); |
| | padding: 30px; |
| | border-radius: 15px; |
| | box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.7); |
| | text-align: center; |
| | max-width: 350px; |
| | width: 80%; |
| | transform: translateY(-20px); |
| | transition: transform 0.3s ease-in-out; |
| | } |
| | |
| | .modal-overlay.show .modal-content { |
| | transform: translateY(0); |
| | } |
| | |
| | .modal-message { |
| | font-size: 1.1rem; |
| | margin-bottom: 25px; |
| | color: var(--bs-body-color); |
| | } |
| | .modal-message p { |
| | margin-bottom: 10px; |
| | line-height: 1.4; |
| | } |
| | .modal-message strong { |
| | font-size: 1.2rem; |
| | display: block; |
| | margin-bottom: 15px; |
| | } |
| | .modal-message ul { |
| | list-style: none; |
| | padding: 0; |
| | margin-bottom: 20px; |
| | } |
| | .modal-message ul li { |
| | margin-bottom: 8px; |
| | font-size: 1rem; |
| | } |
| | |
| | |
| | .modal-button { |
| | background-color: var(--gakr-blue); |
| | color: #fff; |
| | border: none; |
| | padding: 10px 25px; |
| | border-radius: 10px; |
| | font-size: 1rem; |
| | cursor: pointer; |
| | transition: background-color 0.2s ease-in-out, transform 0.1s ease-in-out; |
| | } |
| | |
| | .modal-button:hover { |
| | background-color: var(--gakr-blue-dark); |
| | } |
| | |
| | .modal-button:active { |
| | transform: scale(0.98); |
| | } |
| | |
| | .modal-buttons-stacked { |
| | display: flex; |
| | flex-direction: column; |
| | gap: 10px; |
| | margin-top: 20px; |
| | } |
| | |
| | .modal-buttons-stacked .modal-button { |
| | width: 100%; |
| | } |
| | |
| | .modal-buttons-inline { |
| | display: flex; |
| | justify-content: space-between; |
| | gap: 15px; |
| | margin-top: 20px; |
| | } |
| | .modal-buttons-inline .modal-button { |
| | flex-grow: 1; |
| | max-width: none; |
| | } |
| | |
| | |
| | |
| | .field .toggle-password { |
| | position: absolute; |
| | right: 15px; |
| | top: 50%; |
| | transform: translateY(-50%); |
| | cursor: pointer; |
| | color: var(--bs-secondary-color); |
| | font-size: 0.9rem; |
| | } |
| | |
| | .field .toggle-password:hover { |
| | color: var(--gakr-blue); |
| | } |
| | |
| | |
| | .modal-form { |
| | text-align: left; |
| | margin-top: 20px; |
| | } |
| | .modal-form .field { |
| | margin-bottom: 15px; |
| | height: auto; |
| | } |
| | .modal-form label { |
| | display: block; |
| | margin-bottom: 5px; |
| | font-size: 0.9rem; |
| | color: var(--bs-body-color); |
| | } |
| | .modal-form input[type="text"], |
| | .modal-form input[type="email"], |
| | .modal-form input[type="password"] { |
| | width: 100%; |
| | padding: 10px 15px; |
| | border: 1px solid var(--bs-border-color); |
| | border-radius: 10px; |
| | background-color: var(--bs-body-bg); |
| | color: var(--bs-body-color); |
| | box-sizing: border-box; |
| | } |
| | .modal-form .error-message { |
| | position: static; |
| | margin-top: 5px; |
| | margin-left: 0; |
| | white-space: normal; |
| | } |
| | .modal-buttons { |
| | display: flex; |
| | justify-content: center; |
| | gap: 15px; |
| | margin-top: 20px; |
| | } |
| | .modal-buttons button { |
| | width: 100%; |
| | max-width: 150px; |
| | } |
| | </style> |
| | </head> |
| | <body> |
| |
|
| | <div class="wrapper"> |
| | <div class="title-text"> |
| | <div class="title login">Login Form</div> |
| | <div class="title signup">Signup Form</div> |
| | </div> |
| | <div class="form-container"> |
| | <div class="slide-controls"> |
| | <input type="radio" name="slide" id="login" checked> |
| | <input type="radio" name="slide" id="signup"> |
| | <label for="login" class="slide login">Login</label> |
| | <label for="signup" class="slide signup">Signup</label> |
| | <div class="slider-tab"></div> |
| | </div> |
| | <div class="form-inner"> |
| | <form action="#" class="login" id="loginForm"> |
| | <div class="field"> |
| | <input type="text" placeholder="Username or Email" required id="loginUsernameOrEmail" name="username_or_email"> |
| | <div class="error-message" id="loginUsernameOrEmailError"></div> |
| | </div> |
| | <div class="field"> |
| | <input type="password" placeholder="Password" required id="loginPassword" name="password"> |
| | <span class="toggle-password fa-solid fa-eye-slash"></span> |
| | <div class="error-message" id="loginPasswordError"></div> |
| | </div> |
| | <div class="pass-link"><a href="#" id="forgotPasswordLink">Forgot password?</a></div> |
| | <div class="field btn"> |
| | <input type="submit" value="Login"> |
| | </div> |
| | <div class="signup-link">Not a member? <a href="">Signup now</a></div> |
| | </form> |
| | <form action="#" class="signup" id="signupForm"> |
| | <div class="field"> |
| | <input type="text" placeholder="Name" required id="signupName" name="name"> |
| | <div class="error-message" id="signupNameError"></div> |
| | </div> |
| | <div class="field"> |
| | <input type="text" placeholder="Email Address" required id="signupEmail" name="email"> |
| | <div class="error-message" id="signupEmailError"></div> |
| | </div> |
| | <div class="field"> |
| | <input type="password" placeholder="Password" required id="signupPassword" name="password"> |
| | <span class="toggle-password fa-solid fa-eye-slash"></span> |
| | <div class="error-message" id="signupPasswordError"></div> |
| | </div> |
| | <div class="field"> |
| | <input type="password" placeholder="Confirm password" required id="signupConfirmPassword" name="confirm_password"> |
| | <span class="toggle-password fa-solid fa-eye-slash"></span> |
| | <div class="error-message" id="signupConfirmPasswordError"></div> |
| | </div> |
| | <div class="field btn"> |
| | <input type="submit" value="Signup"> |
| | </div> |
| | </form> |
| | </div> |
| | </div> |
| | <div class="form-message" id="generalFormMessage"></div> |
| | </div> |
| |
|
| | <div class="modal-overlay" id="customModal"> |
| | <div class="modal-content"> |
| | <div class="modal-message" id="modalMessage"></div> |
| | <div id="modalButtonsContainer" class="modal-buttons"> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | <div class="modal-overlay" id="forgotPasswordRequestModal"> |
| | <div class="modal-content"> |
| | <p class="modal-message" id="forgotModalMessage">Enter your registered email to request a password reset.</p> |
| | |
| | <form id="forgotPasswordRequestForm" class="modal-form"> |
| | <div class="field"> |
| | <label for="forgotEmail">Email:</label> |
| | <input type="email" id="forgotEmail" name="email" required> |
| | <div class="error-message" id="forgotEmailError"></div> |
| | </div> |
| | <div class="modal-buttons-stacked"> |
| | <button type="submit" class="modal-button" id="forgotSubmitButton">Request OTP</button> |
| | <button type="button" class="modal-button" id="forgotCancelButton" style="background-color: var(--bs-secondary-color);">Cancel</button> |
| | </div> |
| | </form> |
| |
|
| | <div id="otpSection" style="display: none;"> |
| | <form id="verifyOtpForm" class="modal-form"> |
| | <div class="field"> |
| | <label for="otpInput">Enter OTP:</label> |
| | <input type="text" id="otpInput" name="otp" required maxlength="6" pattern="\d{6}" title="Please enter a 6-digit OTP"> |
| | <div class="error-message" id="otpError"></div> |
| | </div> |
| | <div class="modal-buttons-stacked"> |
| | <button type="submit" class="modal-button" id="verifyOtpButton">Verify OTP</button> |
| | <button type="button" class="modal-button" id="resendOtpButton" style="background-color: var(--bs-secondary-color);">Send Again</button> |
| | <button type="button" class="modal-button" id="otpCancelButton" style="background-color: var(--bs-secondary-color);">Cancel</button> |
| | </div> |
| | </form> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | <div class="modal-overlay" id="resetPasswordModal"> |
| | <div class="modal-content"> |
| | <p class="modal-message">Enter your new password.</p> |
| | <form id="resetPasswordForm" class="modal-form"> |
| | <div class="field"> |
| | <label for="resetEmail">Email:</label> |
| | <input type="email" id="resetEmail" name="email" required readonly style="background-color: var(--bs-secondary-bg); cursor: not-allowed;"> |
| | <div class="error-message" id="resetEmailError"></div> |
| | </div> |
| | <div class="field"> |
| | <label for="newPassword">New Password:</label> |
| | <input type="password" id="newPassword" name="new_password" required> |
| | <span class="toggle-password fa-solid fa-eye-slash"></span> |
| | <div class="error-message" id="newPasswordError"></div> |
| | </div> |
| | <div class="field"> |
| | <label for="confirmNewPassword">Confirm New Password:</label> |
| | <input type="password" id="confirmNewPassword" name="confirm_new_password" required> |
| | <span class="toggle-password fa-solid fa-eye-slash"></span> |
| | <div class="error-message" id="confirmNewPasswordError"></div> |
| | </div> |
| | <div class="modal-buttons-stacked"> |
| | <button type="submit" class="modal-button" id="resetSubmitButton">Reset Password</button> |
| | <button type="button" class="modal-button" id="resetCancelButton" style="background-color: var(--bs-secondary-color);">Cancel</button> |
| | </div> |
| | </form> |
| | </div> |
| | </div> |
| |
|
| | <script> |
| | document.addEventListener('DOMContentLoaded', function() { |
| | const loginText = document.querySelector(".title-text .login"); |
| | const loginFormDiv = document.querySelector("form.login"); |
| | const signupLink = document.querySelector("form .signup-link a"); |
| | const loginRadio = document.getElementById('login'); |
| | const signupRadio = document.getElementById('signup'); |
| | |
| | const loginForm = document.getElementById('loginForm'); |
| | const signupForm = document.getElementById('signupForm'); |
| | |
| | |
| | const loginUsernameOrEmailInput = document.getElementById('loginUsernameOrEmail'); |
| | const loginPasswordInput = document.getElementById('loginPassword'); |
| | const loginSubmitButton = loginForm ? loginForm.querySelector('input[type="submit"]') : null; |
| | |
| | const signupNameInput = document.getElementById('signupName'); |
| | const signupEmailInput = document.getElementById('signupEmail'); |
| | const signupPasswordInput = document.getElementById('signupPassword'); |
| | const signupConfirmPasswordInput = document.getElementById('signupConfirmPassword'); |
| | const signupSubmitButton = signupForm ? signupForm.querySelector('input[type="submit"]') : null; |
| | |
| | const loginUsernameOrEmailError = document.getElementById('loginUsernameOrEmailError'); |
| | const loginPasswordError = document.getElementById('loginPasswordError'); |
| | |
| | const signupNameError = document.getElementById('signupNameError'); |
| | const signupEmailError = document.getElementById('signupEmailError'); |
| | const signupPasswordError = document.getElementById('signupPasswordError'); |
| | const signupConfirmPasswordError = document.getElementById('signupConfirmPasswordError'); |
| | |
| | const generalFormMessage = document.getElementById('generalFormMessage'); |
| | |
| | |
| | const customModal = document.getElementById('customModal'); |
| | const modalMessageDiv = document.getElementById('modalMessage'); |
| | const modalButtonsContainer = document.getElementById('modalButtonsContainer'); |
| | |
| | |
| | const forgotPasswordLink = document.getElementById('forgotPasswordLink'); |
| | const forgotPasswordRequestModal = document.getElementById('forgotPasswordRequestModal'); |
| | const forgotModalMessage = document.getElementById('forgotModalMessage'); |
| | |
| | |
| | const forgotPasswordRequestForm = document.getElementById('forgotPasswordRequestForm'); |
| | const forgotEmailInput = document.getElementById('forgotEmail'); |
| | const forgotEmailError = document.getElementById('forgotEmailError'); |
| | const forgotSubmitButton = document.getElementById('forgotSubmitButton'); |
| | const forgotCancelButton = document.getElementById('forgotCancelButton'); |
| | |
| | |
| | const otpSection = document.getElementById('otpSection'); |
| | const verifyOtpForm = document.getElementById('verifyOtpForm'); |
| | const otpInput = document.getElementById('otpInput'); |
| | const otpError = document.getElementById('otpError'); |
| | const verifyOtpButton = document.getElementById('verifyOtpButton'); |
| | const resendOtpButton = document.getElementById('resendOtpButton'); |
| | const otpCancelButton = document.getElementById('otpCancelButton'); |
| | |
| | |
| | const resetPasswordModal = document.getElementById('resetPasswordModal'); |
| | const resetPasswordForm = document.getElementById('resetPasswordForm'); |
| | const resetEmailInput = document.getElementById('resetEmail'); |
| | const newPasswordInput = document.getElementById('newPassword'); |
| | const confirmNewPasswordInput = document.getElementById('confirmNewPassword'); |
| | const resetEmailError = document.getElementById('resetEmailError'); |
| | const newPasswordError = document.getElementById('newPasswordError'); |
| | const confirmNewPasswordError = document.getElementById('confirmNewPasswordError'); |
| | const resetSubmitButton = document.getElementById('resetSubmitButton'); |
| | const resetCancelButton = document.getElementById('resetCancelButton'); |
| | |
| | |
| | let loginAttempts = 0; |
| | const MAX_LOGIN_ATTEMPTS = 3; |
| | |
| | |
| | let currentForgotEmail = ''; |
| | |
| | |
| | function clearErrors(formType = 'all') { |
| | if (formType === 'all' || formType === 'main') { |
| | document.querySelectorAll('.error-message').forEach(el => el.textContent = ''); |
| | document.querySelectorAll('.form-inner form .field input').forEach(el => el.classList.remove('error')); |
| | if (generalFormMessage) { |
| | generalFormMessage.textContent = ''; |
| | generalFormMessage.className = 'form-message'; |
| | } |
| | } |
| | if (formType === 'all' || formType === 'forgot') { |
| | if (forgotEmailError) forgotEmailError.textContent = ''; |
| | if (forgotEmailInput) forgotEmailInput.classList.remove('error'); |
| | |
| | if (forgotModalMessage) forgotModalMessage.innerHTML = 'Enter your registered email to request a password reset.'; |
| | } |
| | if (formType === 'all' || formType === 'otp') { |
| | if (otpError) otpError.textContent = ''; |
| | if (otpInput) otpInput.classList.remove('error'); |
| | } |
| | if (formType === 'all' || formType === 'reset') { |
| | if (resetEmailError) resetEmailError.textContent = ''; |
| | if (newPasswordError) newPasswordError.textContent = ''; |
| | if (confirmNewPasswordError) confirmNewPasswordError.textContent = ''; |
| | if (resetEmailInput) resetEmailInput.classList.remove('error'); |
| | if (newPasswordInput) newPasswordInput.classList.remove('error'); |
| | if (confirmNewPasswordInput) confirmNewPasswordInput.classList.remove('error'); |
| | } |
| | } |
| | |
| | |
| | function displayFormMessage(message, type = 'info') { |
| | if (generalFormMessage) { |
| | generalFormMessage.textContent = message; |
| | generalFormMessage.className = 'form-message ' + type; |
| | } |
| | } |
| | |
| | |
| | function showCustomModal(modalElement) { |
| | modalElement.classList.add('show'); |
| | } |
| | |
| | |
| | function hideCustomModal(modalElement) { |
| | modalElement.classList.remove('show'); |
| | clearErrors(); |
| | } |
| | |
| | |
| | function showFlexibleModal(messageHTML, buttonsConfig, callback = null) { |
| | modalMessageDiv.innerHTML = messageHTML; |
| | modalButtonsContainer.innerHTML = ''; |
| | |
| | if (buttonsConfig && buttonsConfig.length > 0) { |
| | const isInline = buttonsConfig.every(btn => !btn.newline); |
| | modalButtonsContainer.className = isInline ? 'modal-buttons-inline' : 'modal-buttons-stacked'; |
| | |
| | buttonsConfig.forEach(btnConf => { |
| | const button = document.createElement('button'); |
| | button.className = 'modal-button'; |
| | button.textContent = btnConf.text; |
| | if (btnConf.style) { |
| | button.style = btnConf.style; |
| | } |
| | button.onclick = function() { |
| | hideCustomModal(customModal); |
| | if (btnConf.action && typeof btnConf.action === 'function') { |
| | btnConf.action(); |
| | } |
| | if (callback && typeof callback === 'function') { |
| | callback(); |
| | } |
| | }; |
| | modalButtonsContainer.appendChild(button); |
| | }); |
| | } else { |
| | const okButton = document.createElement('button'); |
| | okButton.className = 'modal-button'; |
| | okButton.textContent = 'OK'; |
| | okButton.onclick = function() { |
| | hideCustomModal(customModal); |
| | if (callback && typeof callback === 'function') { |
| | callback(); |
| | } |
| | }; |
| | modalButtonsContainer.classList.remove('modal-buttons-inline', 'modal-buttons-stacked'); |
| | modalButtonsContainer.classList.add('modal-buttons'); |
| | modalButtonsContainer.appendChild(okButton); |
| | } |
| | showCustomModal(customModal); |
| | } |
| | |
| | |
| | function slideToSignup() { |
| | if (loginFormDiv && loginText) { |
| | loginFormDiv.style.marginLeft = "-50%"; |
| | loginText.style.marginLeft = "-50%"; |
| | clearErrors('main'); |
| | } |
| | } |
| | |
| | function slideToLogin() { |
| | if (loginFormDiv && loginText) { |
| | loginFormDiv.style.marginLeft = "0%"; |
| | loginText.style.marginLeft = "0%"; |
| | clearErrors('main'); |
| | } |
| | } |
| | |
| | |
| | if (signupRadio) { |
| | signupRadio.addEventListener('change', function() { |
| | if (this.checked) { |
| | slideToSignup(); |
| | } |
| | }); |
| | } |
| | |
| | if (loginRadio) { |
| | loginRadio.addEventListener('change', function() { |
| | if (this.checked) { |
| | slideToLogin(); |
| | } |
| | }); |
| | } |
| | |
| | if (signupLink && signupRadio) { |
| | signupLink.onclick = ((e) => { |
| | e.preventDefault(); |
| | signupRadio.checked = true; |
| | slideToSignup(); |
| | }); |
| | } |
| | |
| | |
| | const urlParams = new URLSearchParams(window.location.search); |
| | const showSignup = urlParams.get('signup'); |
| | |
| | if (showSignup === 'true') { |
| | if (signupRadio) { |
| | signupRadio.checked = true; |
| | slideToSignup(); |
| | } |
| | } else { |
| | if (loginRadio) { |
| | loginRadio.checked = true; |
| | slideToLogin(); |
| | } |
| | } |
| | |
| | |
| | function setupPasswordToggle(passwordInput, toggleIcon) { |
| | if (passwordInput && toggleIcon) { |
| | toggleIcon.addEventListener('click', function() { |
| | const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password'; |
| | passwordInput.setAttribute('type', type); |
| | this.classList.toggle('fa-eye'); |
| | this.classList.toggle('fa-eye-slash'); |
| | }); |
| | } |
| | } |
| | |
| | |
| | setupPasswordToggle(loginPasswordInput, document.querySelector('#loginForm .toggle-password')); |
| | setupPasswordToggle(signupPasswordInput, document.querySelector('#signupForm #signupPassword + .toggle-password')); |
| | setupPasswordToggle(signupConfirmPasswordInput, document.querySelector('#signupForm #signupConfirmPassword + .toggle-password')); |
| | setupPasswordToggle(newPasswordInput, document.querySelector('#resetPasswordForm #newPassword + .toggle-password')); |
| | setupPasswordToggle(confirmNewPasswordInput, document.querySelector('#resetPasswordForm #confirmNewPassword + .toggle-password')); |
| | |
| | |
| | |
| | if (loginForm) { |
| | loginForm.addEventListener('submit', function(event) { |
| | event.preventDefault(); |
| | clearErrors('main'); |
| | |
| | |
| | if (loginAttempts >= MAX_LOGIN_ATTEMPTS) { |
| | showLoginAttemptsLimitModal(); |
| | return; |
| | } |
| | |
| | let isValid = true; |
| | |
| | const usernameOrEmail = loginUsernameOrEmailInput.value.trim(); |
| | const password = loginPasswordInput.value.trim(); |
| | |
| | if (usernameOrEmail === '') { |
| | isValid = false; |
| | loginUsernameOrEmailError.textContent = 'Username or Email is required'; |
| | loginUsernameOrEmailInput.classList.add('error'); |
| | } |
| | if (password === '') { |
| | isValid = false; |
| | loginPasswordError.textContent = 'Password is required'; |
| | loginPasswordInput.classList.add('error'); |
| | } |
| | |
| | if (isValid) { |
| | submitFormData(event.target, loginSubmitButton, '/api/login', 'main'); |
| | } |
| | }); |
| | } |
| | |
| | |
| | if (signupForm) { |
| | signupForm.addEventListener('submit', function(event) { |
| | event.preventDefault(); |
| | clearErrors('main'); |
| | |
| | let isValid = true; |
| | |
| | const name = signupNameInput.value.trim(); |
| | const email = signupEmailInput.value.trim(); |
| | const password = signupPasswordInput.value.trim(); |
| | const confirmPassword = signupConfirmPasswordInput.value.trim(); |
| | |
| | if (name === '') { |
| | isValid = false; |
| | signupNameError.textContent = 'Name is required'; |
| | signupNameInput.classList.add('error'); |
| | } |
| | |
| | if (email === '') { |
| | isValid = false; |
| | signupEmailError.textContent = 'Email is required'; |
| | signupEmailInput.classList.add('error'); |
| | } else if (!isValidEmail(email)) { |
| | isValid = false; |
| | signupEmailError.textContent = 'Enter a valid email address'; |
| | signupEmailInput.classList.add('error'); |
| | } |
| | |
| | if (password === '') { |
| | isValid = false; |
| | signupPasswordError.textContent = 'Password is required'; |
| | signupPasswordInput.classList.add('error'); |
| | } else if (password.length < 6) { |
| | isValid = false; |
| | signupPasswordError.textContent = 'Password must be at least 6 characters'; |
| | signupPasswordInput.classList.add('error'); |
| | } |
| | |
| | if (confirmPassword === '') { |
| | isValid = false; |
| | signupConfirmPasswordError.textContent = 'Confirm password is required'; |
| | signupConfirmPasswordInput.classList.add('error'); |
| | } else if (password !== confirmPassword) { |
| | isValid = false; |
| | signupConfirmPasswordError.textContent = 'Passwords do not match'; |
| | signupConfirmPasswordInput.classList.add('error'); |
| | signupPasswordInput.classList.add('error'); |
| | } |
| | |
| | if (isValid) { |
| | submitFormData(event.target, signupSubmitButton, '/api/signup', 'main'); |
| | } |
| | }); |
| | } |
| | |
| | |
| | if (forgotPasswordLink) { |
| | forgotPasswordLink.addEventListener('click', function(event) { |
| | event.preventDefault(); |
| | clearErrors('forgot'); |
| | clearErrors('otp'); |
| | |
| | forgotPasswordRequestForm.reset(); |
| | otpInput.value = ''; |
| | |
| | forgotModalMessage.innerHTML = 'Enter your registered email to request a password reset.'; |
| | |
| | |
| | forgotPasswordRequestForm.style.display = 'block'; |
| | if (otpSection) otpSection.style.display = 'none'; |
| | |
| | showCustomModal(forgotPasswordRequestModal); |
| | }); |
| | } |
| | |
| | |
| | if (forgotPasswordRequestForm) { |
| | forgotPasswordRequestForm.addEventListener('submit', async function(event) { |
| | event.preventDefault(); |
| | clearErrors('forgot'); |
| | |
| | let isValid = true; |
| | const email = forgotEmailInput.value.trim(); |
| | |
| | if (email === '') { |
| | isValid = false; |
| | forgotEmailError.textContent = 'Email is required.'; |
| | forgotEmailInput.classList.add('error'); |
| | } else if (!isValidEmail(email)) { |
| | isValid = false; |
| | forgotEmailError.textContent = 'Enter a valid email address.'; |
| | forgotEmailInput.classList.add('error'); |
| | } |
| | |
| | if (isValid) { |
| | forgotSubmitButton.disabled = true; |
| | forgotSubmitButton.innerHTML = '<span class="loading-spinner"></span> Sending OTP...'; |
| | |
| | try { |
| | const response = await fetch('/api/forgot_password_request', { |
| | method: 'POST', |
| | headers: { 'Content-Type': 'application/json' }, |
| | body: JSON.stringify({ email: email }) |
| | }); |
| | |
| | const result = await response.json(); |
| | |
| | if (response.ok) { |
| | currentForgotEmail = email; |
| | |
| | |
| | forgotPasswordRequestForm.style.display = 'none'; |
| | if (otpSection) otpSection.style.display = 'block'; |
| | otpInput.value = ''; |
| | |
| | |
| | let messageText = `<p>An OTP has been sent to <strong>${email}</strong>. Please enter it below.</p>`; |
| | |
| | |
| | |
| | |
| | forgotModalMessage.innerHTML = messageText; |
| | |
| | } else { |
| | displayModalFormErrors(result, forgotPasswordRequestForm, 'forgot'); |
| | } |
| | } catch (error) { |
| | console.error('Fetch error:', error); |
| | showFlexibleModal('<p>Network error. Could not request OTP. Please try again.</p>', [{ text: 'OK' }]); |
| | } finally { |
| | forgotSubmitButton.disabled = false; |
| | forgotSubmitButton.innerHTML = 'Request OTP'; |
| | } |
| | } |
| | }); |
| | } |
| | |
| | |
| | if (verifyOtpForm) { |
| | verifyOtpForm.addEventListener('submit', async function(event) { |
| | event.preventDefault(); |
| | clearErrors('otp'); |
| | |
| | let isValid = true; |
| | const otp = otpInput.value.trim(); |
| | |
| | if (otp === '') { |
| | isValid = false; |
| | otpError.textContent = 'OTP is required.'; |
| | otpInput.classList.add('error'); |
| | } else if (!/^\d{6}$/.test(otp)) { |
| | isValid = false; |
| | otpError.textContent = 'OTP must be a 6-digit number.'; |
| | otpInput.classList.add('error'); |
| | } |
| | |
| | if (isValid) { |
| | verifyOtpButton.disabled = true; |
| | verifyOtpButton.innerHTML = '<span class="loading-spinner"></span> Verifying...'; |
| | |
| | try { |
| | const response = await fetch('/api/verify_otp', { |
| | method: 'POST', |
| | headers: { 'Content-Type': 'application/json' }, |
| | body: JSON.stringify({ email: currentForgotEmail, otp: otp }) |
| | }); |
| | |
| | const result = await response.json(); |
| | |
| | if (response.ok) { |
| | hideCustomModal(forgotPasswordRequestModal); |
| | resetEmailInput.value = currentForgotEmail; |
| | newPasswordInput.value = ''; |
| | confirmNewPasswordInput.value = ''; |
| | showCustomModal(resetPasswordModal); |
| | |
| | } else { |
| | otpError.textContent = result.message || 'OTP verification failed.'; |
| | otpInput.classList.add('error'); |
| | otpInput.classList.add('error'); |
| | |
| | if (result.message) { |
| | forgotModalMessage.innerHTML = `<p>${result.message}</p>`; |
| | } |
| | } |
| | } catch (error) { |
| | console.error('Fetch error:', error); |
| | showFlexibleModal('<p>Network error. Could not verify OTP. Please try again.</p>', [{ text: 'OK' }]); |
| | } finally { |
| | verifyOtpButton.disabled = false; |
| | verifyOtpButton.innerHTML = 'Verify OTP'; |
| | } |
| | } |
| | }); |
| | } |
| | |
| | |
| | if (resendOtpButton) { |
| | resendOtpButton.addEventListener('click', async function(event) { |
| | event.preventDefault(); |
| | clearErrors('otp'); |
| | resendOtpButton.disabled = true; |
| | resendOtpButton.innerHTML = '<span class="loading-spinner"></span> Resending...'; |
| | |
| | try { |
| | const response = await fetch('/api/forgot_password_request', { |
| | method: 'POST', |
| | headers: { 'Content-Type': 'application/json' }, |
| | body: JSON.stringify({ email: currentForgotEmail }) |
| | }); |
| | |
| | const result = await response.json(); |
| | |
| | if (response.ok) { |
| | let messageText = `<p>New OTP sent to <strong>${currentForgotEmail}</strong>. Please enter it below.</p>`; |
| | |
| | |
| | |
| | forgotModalMessage.innerHTML = messageText; |
| | otpInput.value = ''; |
| | otpInput.classList.remove('error'); |
| | otpError.textContent = ''; |
| | } else { |
| | |
| | otpError.textContent = result.message || 'Failed to resend OTP.'; |
| | otpInput.classList.add('error'); |
| | if (result.message) { |
| | forgotModalMessage.innerHTML = `<p>${result.message}</p>`; |
| | } |
| | } |
| | } catch (error) { |
| | console.error('Fetch error:', error); |
| | showFlexibleModal('<p>Network error. Could not resend OTP. Please try again.</p>', [{ text: 'OK' }]); |
| | } finally { |
| | resendOtpButton.disabled = false; |
| | resendOtpButton.innerHTML = 'Send Again'; |
| | } |
| | }); |
| | } |
| | |
| | |
| | if (forgotCancelButton) { |
| | forgotCancelButton.addEventListener('click', function() { |
| | hideCustomModal(forgotPasswordRequestModal); |
| | forgotPasswordRequestForm.reset(); |
| | currentForgotEmail = ''; |
| | clearErrors('forgot'); |
| | }); |
| | } |
| | |
| | if (otpCancelButton) { |
| | otpCancelButton.addEventListener('click', function() { |
| | hideCustomModal(forgotPasswordRequestModal); |
| | verifyOtpForm.reset(); |
| | currentForgotEmail = ''; |
| | clearErrors('otp'); |
| | }); |
| | } |
| | |
| | if (resetCancelButton) { |
| | resetCancelButton.addEventListener('click', function() { |
| | hideCustomModal(resetPasswordModal); |
| | resetPasswordForm.reset(); |
| | currentForgotEmail = ''; |
| | clearErrors('reset'); |
| | }); |
| | } |
| | |
| | |
| | if (resetPasswordForm) { |
| | resetPasswordForm.addEventListener('submit', async function(event) { |
| | event.preventDefault(); |
| | clearErrors('reset'); |
| | |
| | let isValid = true; |
| | const email = resetEmailInput.value.trim(); |
| | const newPassword = newPasswordInput.value.trim(); |
| | const confirmNewPassword = confirmNewPasswordInput.value.trim(); |
| | |
| | |
| | if (email === '') { |
| | isValid = false; |
| | resetEmailError.textContent = 'Email is required.'; |
| | resetEmailInput.classList.add('error'); |
| | } else if (!isValidEmail(email)) { |
| | isValid = false; |
| | resetEmailError.textContent = 'Invalid email format.'; |
| | resetEmailInput.classList.add('error'); |
| | } |
| | |
| | if (newPassword === '') { |
| | isValid = false; |
| | newPasswordError.textContent = 'New password is required.'; |
| | newPasswordInput.classList.add('error'); |
| | } else if (newPassword.length < 6) { |
| | isValid = false; |
| | newPasswordError.textContent = 'New password must be at least 6 characters.'; |
| | newPasswordInput.classList.add('error'); |
| | } |
| | |
| | if (confirmNewPassword === '') { |
| | isValid = false; |
| | confirmNewPasswordError.textContent = 'Confirm new password is required.'; |
| | confirmNewPasswordInput.classList.add('error'); |
| | } else if (newPassword !== confirmNewPassword) { |
| | isValid = false; |
| | confirmNewPasswordError.textContent = 'Passwords do not match.'; |
| | confirmNewPasswordInput.classList.add('error'); |
| | newPasswordInput.classList.add('error'); |
| | } |
| | |
| | |
| | if (isValid) { |
| | resetSubmitButton.disabled = true; |
| | resetSubmitButton.innerHTML = '<span class="loading-spinner"></span> Resetting...'; |
| | |
| | try { |
| | const response = await fetch('/api/reset_password', { |
| | method: 'POST', |
| | headers: { 'Content-Type': 'application/json' }, |
| | body: JSON.stringify({ email: email, new_password: newPassword }) |
| | }); |
| | |
| | const result = await response.json(); |
| | |
| | if (response.ok) { |
| | hideCustomModal(resetPasswordModal); |
| | resetPasswordForm.reset(); |
| | currentForgotEmail = ''; |
| | showFlexibleModal( |
| | '<p>Password reset successful. You can now login with your new password.</p>', |
| | [{ text: 'OK', action: () => { |
| | loginRadio.checked = true; |
| | slideToLogin(); |
| | }}] |
| | ); |
| | } else { |
| | |
| | displayModalFormErrors(result, resetPasswordForm, 'reset'); |
| | if (result.message && (result.message.includes('OTP not verified') || result.message.includes('not initiated'))) { |
| | showFlexibleModal( |
| | `<p>${result.message}</p><p>Please start the password reset process again.</p>`, |
| | [{ text: 'Start Over', action: () => { |
| | hideCustomModal(resetPasswordModal); |
| | forgotPasswordLink.click(); |
| | }}], |
| | null |
| | ); |
| | } |
| | } |
| | } catch (error) { |
| | console.error('Fetch error:', error); |
| | showFlexibleModal('<p>Network error. Could not reset password. Please try again.</p>', [{ text: 'OK' }]); |
| | } finally { |
| | resetSubmitButton.disabled = false; |
| | resetSubmitButton.innerHTML = 'Reset Password'; |
| | } |
| | } |
| | }); |
| | } |
| | |
| | |
| | function handleMainFormErrors(result, formElement, endpoint) { |
| | if (endpoint === '/api/login') { |
| | if (result.message && (result.message.includes('Account not found') || result.message.includes('Invalid credentials'))) { |
| | showFlexibleModal( |
| | `<p>${result.message}</p><p>If you are not registered, please sign up.</p>`, |
| | [{ text: 'Sign Up', action: () => { |
| | signupRadio.checked = true; |
| | slideToSignup(); |
| | }}, { text: 'OK', style: 'background-color: var(--bs-secondary-color);' }] |
| | ); |
| | } |
| | } else if (endpoint === '/api/signup') { |
| | if (result.message && (result.message.includes('Email already registered') || result.message.includes('Username already taken'))) { |
| | showFlexibleModal( |
| | `<p>${result.message}</p><p>If you are already registered, please log in.</p>`, |
| | [{ text: 'Login', action: () => { |
| | loginRadio.checked = true; |
| | slideToLogin(); |
| | }}, { text: 'OK', style: 'background-color: var(--bs-secondary-color);' }] |
| | ); |
| | } |
| | } |
| | if (result.errors) { |
| | for (const field in result.errors) { |
| | let inputElement, errorElement; |
| | |
| | if (formElement.id === 'loginForm' && field === 'username_or_email') { |
| | inputElement = loginUsernameOrEmailInput; |
| | errorElement = loginUsernameOrEmailError; |
| | } else if (formElement.id === 'signupForm' && field === 'name') { |
| | inputElement = signupNameInput; |
| | errorElement = signupNameError; |
| | } |
| | else { |
| | |
| | |
| | inputElement = formElement.querySelector(`[name="${field}"]`); |
| | |
| | const inputId = inputElement ? inputElement.id : null; |
| | errorElement = inputId ? document.getElementById(`${inputId}Error`) : null; |
| | } |
| | |
| | if (errorElement) errorElement.textContent = result.errors[field]; |
| | if (inputElement) inputElement.classList.add('error'); |
| | } |
| | } |
| | } |
| | |
| | |
| | function displayModalFormErrors(result, formElement, formContext) { |
| | |
| | clearErrors(formContext); |
| | |
| | if (result.errors) { |
| | for (const field in result.errors) { |
| | |
| | const inputElement = formElement.querySelector(`[name="${field}"]`); |
| | |
| | const errorElement = inputElement ? document.getElementById(`${inputElement.id}Error`) : null; |
| | |
| | if (errorElement) errorElement.textContent = result.errors[field]; |
| | if (inputElement) inputElement.classList.add('error'); |
| | } |
| | } |
| | |
| | const targetModalMessage = formElement.closest('.modal-content').querySelector('.modal-message'); |
| | if (targetModalMessage && result.message) { |
| | targetModalMessage.innerHTML = `<p>${result.message}</p>`; |
| | } |
| | } |
| | |
| | |
| | async function submitFormData(formElement, submitButton, endpoint, formContext) { |
| | if (!formElement || !submitButton || !endpoint) { |
| | console.error("submitFormData called with missing arguments."); |
| | return; |
| | } |
| | |
| | const formData = new FormData(formElement); |
| | const data = {}; |
| | for (let [key, value] of formData.entries()) { |
| | data[key] = value; |
| | } |
| | |
| | submitButton.disabled = true; |
| | if (formContext === 'main') { |
| | displayFormMessage("Submitting...", 'info'); |
| | } else { |
| | |
| | submitButton.innerHTML = '<span class="loading-spinner"></span> Submitting...'; |
| | } |
| | |
| | try { |
| | const response = await fetch(endpoint, { |
| | method: 'POST', |
| | headers: { |
| | 'Content-Type': 'application/json', |
| | }, |
| | body: JSON.stringify(data), |
| | }); |
| | |
| | const result = await response.json(); |
| | |
| | if (response.ok) { |
| | if (formContext === 'main') { |
| | clearErrors('main'); |
| | formElement.reset(); |
| | displayFormMessage('', 'info'); |
| | loginAttempts = 0; |
| | |
| | if (endpoint === '/api/signup') { |
| | showFlexibleModal( |
| | '<p>Registration successful! Please log in.</p>', |
| | [{ text: 'OK', action: () => { |
| | loginRadio.checked = true; |
| | slideToLogin(); |
| | const url = new URL(window.location); |
| | url.searchParams.delete('signup'); |
| | window.history.replaceState({}, '', url.toString()); |
| | }}] |
| | ); |
| | } else if (endpoint === '/api/login') { |
| | showFlexibleModal( |
| | '<p>Login successful!</p>', |
| | [{ |
| | text: 'OK', |
| | action: () => { |
| | |
| | localStorage.setItem('gakr_is_logged_in', 'true'); |
| | alert('Welcome! You are logged in.'); |
| | |
| | |
| | |
| | |
| | window.location.href = 'chat.html'; |
| | } |
| | }] |
| | ); |
| | } |
| | |
| | } |
| | |
| | |
| | } else { |
| | if (formContext === 'main') { |
| | if (endpoint === '/api/login' && result.message && result.message.includes('Invalid credentials')) { |
| | loginAttempts++; |
| | loginPasswordInput.classList.add('error'); |
| | loginPasswordError.textContent = result.message; |
| | |
| | if (loginAttempts >= MAX_LOGIN_ATTEMPTS) { |
| | showLoginAttemptsLimitModal(); |
| | loginPasswordInput.value = ''; |
| | } else { |
| | displayFormMessage(`Incorrect password. You have ${MAX_LOGIN_ATTEMPTS - loginAttempts} attempts left.`, 'error'); |
| | } |
| | } else { |
| | displayFormMessage(result.message || 'An error occurred.', 'error'); |
| | handleMainFormErrors(result, formElement, endpoint); |
| | } |
| | } else if (formContext === 'forgot') { |
| | displayModalFormErrors(result, formElement, 'forgot'); |
| | } else if (formContext === 'otp') { |
| | displayModalFormErrors(result, formElement, 'otp'); |
| | } else if (formContext === 'reset') { |
| | displayModalFormErrors(result, formElement, 'reset'); |
| | } |
| | } |
| | } catch (error) { |
| | console.error('Fetch error:', error); |
| | if (formContext === 'main') { |
| | displayFormMessage('Network error. Please try again.', 'error'); |
| | } else { |
| | showFlexibleModal( |
| | '<p>Network error. Please check your internet connection and try again.</p>', |
| | [{ text: 'OK' }] |
| | ); |
| | } |
| | } finally { |
| | submitButton.disabled = false; |
| | |
| | if (formContext === 'forgot') { |
| | submitButton.innerHTML = 'Request OTP'; |
| | } else if (formContext === 'otp') { |
| | submitButton.innerHTML = 'Verify OTP'; |
| | } else if (formContext === 'reset') { |
| | submitButton.innerHTML = 'Reset Password'; |
| | } |
| | } |
| | } |
| | |
| | |
| | function showLoginAttemptsLimitModal() { |
| | const messageHtml = ` |
| | <strong>You have made three incorrect login attempts.</strong> |
| | <ul> |
| | <li>1. If you are not registered, please <a href="#" id="modalSignupLink">sign up now</a>.</li> |
| | <li>2. If you are already registered, please <a href="#" id="modalLoginLink">login now</a> with correct credentials.</li> |
| | <li>3. If you have forgotten your password, click <a href="#" id="modalForgotPasswordLink">forgot password</a> to reset it.</li> |
| | </ul> |
| | `; |
| | |
| | const buttons = [ |
| | { text: 'Login', action: () => { |
| | loginRadio.checked = true; |
| | slideToLogin(); |
| | loginAttempts = 0; |
| | loginPasswordInput.value = ''; |
| | }}, |
| | { text: 'Signup', action: () => { |
| | signupRadio.checked = true; |
| | slideToSignup(); |
| | loginAttempts = 0; |
| | loginPasswordInput.value = ''; |
| | }} |
| | ]; |
| | |
| | showFlexibleModal(messageHtml, buttons, () => { |
| | const modalSignupLink = document.getElementById('modalSignupLink'); |
| | const modalLoginLink = document.getElementById('modalLoginLink'); |
| | const modalForgotPasswordLink = document.getElementById('modalForgotPasswordLink'); |
| | |
| | if (modalSignupLink) { |
| | modalSignupLink.onclick = (e) => { |
| | e.preventDefault(); |
| | hideCustomModal(customModal); |
| | signupRadio.checked = true; |
| | slideToSignup(); |
| | loginAttempts = 0; |
| | loginPasswordInput.value = ''; |
| | }; |
| | } |
| | if (modalLoginLink) { |
| | modalLoginLink.onclick = (e) => { |
| | e.preventDefault(); |
| | hideCustomModal(customModal); |
| | loginRadio.checked = true; |
| | slideToLogin(); |
| | loginAttempts = 0; |
| | loginPasswordInput.value = ''; |
| | }; |
| | } |
| | if (modalForgotPasswordLink) { |
| | modalForgotPasswordLink.onclick = (e) => { |
| | e.preventDefault(); |
| | hideCustomModal(customModal); |
| | forgotPasswordLink.click(); |
| | loginAttempts = 0; |
| | loginPasswordInput.value = ''; |
| | }; |
| | } |
| | }); |
| | } |
| | |
| | function isValidEmail(email) { |
| | const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; |
| | return emailRegex.test(email); |
| | } |
| | }); |
| | </script> |
| | </body> |
| | </html> |
| |
|