Ashok75/base2
013
1<!DOCTYPE html>2<html lang="en" data-bs-theme="dark">3<head>4 <meta charset="UTF-8">5 <meta name="viewport" content="width=device-width, initial-scale=1.0">6 <title>GAKR AI - Login/Signup</title>7 <link rel="stylesheet" href="https://cdn.replit.com/agent/bootstrap-agent-dark-theme.min.css">8 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">9 <style>10 :root {11 --gakr-blue: #4285F4;12 --gakr-blue-dark: #1a73e8;13 --gakr-blue-light: #e8f0fe;14 --gakr-grey-text: #5f6368;15 --gakr-grey-hover-bg: rgba(95, 99, 104, 0.1);16 }17 18 :root[data-bs-theme="dark"] {19 --gakr-blue: #8ab4f8;20 --gakr-blue-dark: #669df6;21 --gakr-blue-light: rgba(138, 180, 248, 0.1);22 --gakr-grey-text: #bdc1c6;23 --gakr-grey-hover-bg: rgba(189, 193, 198, 0.1);24 }25 26 @import url('https://fonts.googleapis.com/css?family=Poppins:400,500,600,700&display=swap');27 28 * {29 margin: 0;30 padding: 0;31 box-sizing: border-box;32 font-family: 'Poppins', sans-serif;33 }34 35 html, body {36 display: flex;37 flex-direction: column;38 align-items: center;39 height: 100%;40 width: 100%;41 background: -webkit-linear-gradient(left, #003366,#004080,#0059b3 , #0073e6);42 color: var(--bs-body-color);43 padding-top: calc(64px + 5vh);44 padding-bottom: 5vh;45 min-height: 100vh;46 /* Allow space for modal overlay */47 position: relative;48 }49 50 ::selection {51 background: var(--gakr-blue);52 color: #fff;53 }54 55 .wrapper {56 overflow: hidden;57 max-width: 440px;58 width: 90%;59 background: var(--bs-body-bg);60 box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.5);61 padding: 30px;62 border-radius: 15px;63 margin-top: 10px;64 margin-bottom: 10px;65 z-index: 10; /* Ensure wrapper is below modal but above background */66 }67 68 .wrapper .title-text {69 display: flex;70 width: 200%;71 }72 73 .wrapper .title {74 width: 50%;75 font-size: 35px;76 font-weight: 600;77 text-align: center;78 transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);79 color: var(--bs-body-color);80 }81 82 .wrapper .slide-controls {83 position: relative;84 display: flex;85 height: 50px;86 width: 100%;87 overflow: hidden;88 margin: 30px 0 10px 0;89 justify-content: space-between;90 border: 1px solid var(--bs-border-color);91 border-radius: 15px;92 }93 94 .slide-controls .slide {95 height: 100%;96 width: 100%;97 color: var(--bs-body-color);98 font-size: 18px;99 font-weight: 500;100 text-align: center;101 line-height: 48px;102 cursor: pointer;103 z-index: 1;104 transition: all 0.6s ease;105 }106 107 .slide-controls label.signup{108 color: var(--bs-body-color);109 }110 111 .slide-controls .slider-tab {112 position: absolute;113 height: 100%;114 width: 50%;115 left: 0;116 z-index: 0;117 border-radius: 15px;118 background: var(--gakr-blue);119 transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);120 }121 122 input[type="radio"]{123 display: none;124 }125 126 #signup:checked ~ .slider-tab{127 left: 50%;128 }129 #signup:checked ~ label.signup{130 color: #fff;131 cursor: default;132 user-select: none;133 }134 #signup:checked ~ label.login{135 color: var(--bs-body-color);136 }137 #login:checked ~ label.signup{138 color: var(--bs-body-color);139 }140 #login:checked ~ label.login{141 color: #fff;142 cursor: default;143 user-select: none;144 }145 146 .wrapper .form-container {147 width: 100%;148 overflow: hidden;149 }150 151 .form-container .form-inner {152 display: flex;153 width: 200%;154 }155 156 .form-container .form-inner form {157 width: 50%;158 transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);159 }160 161 .form-inner form .field {162 height: 50px;163 width: 100%;164 margin-top: 20px;165 position: relative;166 }167 168 .form-inner form .field:has(+ .field.btn) {169 margin-bottom: 25px;170 }171 172 .form-inner form .field input{173 height: 100%;174 width: 100%;175 outline: none;176 padding-left: 15px;177 border-radius: 15px;178 border: 1px solid var(--bs-border-color);179 border-bottom-width: 2px;180 font-size: 17px;181 transition: all 0.3s ease;182 color: var(--bs-body-color);183 background-color: var(--bs-body-bg);184 }185 186 .form-inner form .field input.error {187 border-color: red;188 }189 190 .form-inner form .field input::placeholder{191 color: var(--bs-secondary-color);192 transition: all 0.3s ease;193 }194 195 form .field input:focus::placeholder{196 color: var(--gakr-blue);197 }198 199 .form-inner form .pass-link{200 margin-top: 5px;201 font-size: 0.9rem;202 text-align: right; /* Align to right */203 }204 205 .form-inner form .signup-link{206 text-align: center;207 margin-top: 30px;208 font-size: 0.9rem;209 }210 211 .form-inner form .pass-link a,212 .form-inner form .signup-link a{213 color: var(--gakr-blue);214 text-decoration: none;215 transition: color 0.2s ease-in-out;216 }217 218 .form-inner form .pass-link a:hover,219 form-inner form .signup-link a:hover{220 text-decoration: underline;221 color: var(--gakr-blue-dark);222 }223 224 form .btn {225 height: 50px;226 width: 100%;227 margin-top: 20px;228 border-radius: 15px;229 position: relative;230 overflow: hidden;231 background: none;232 transition: none;233 }234 235 form .btn input[type="submit"] {236 height: 100%;237 width: 100%;238 z-index: 1;239 position: relative;240 background: var(--gakr-blue);241 border: none;242 color: #fff;243 padding: 0;244 border-radius: 15px;245 font-size: 20px;246 font-weight: 500;247 cursor: pointer;248 transition: background-color 0.2s ease-in-out, transform 0.1s ease-in-out;249 display: flex;250 align-items: center;251 justify-content: center;252 }253 254 form .btn input[type="submit"]:hover {255 background-color: var(--gakr-blue-dark);256 }257 258 form .btn input[type="submit"]:active {259 transform: scale(0.98);260 transition: background-color 0s, transform 0.1s;261 }262 263 .error-message {264 color: red;265 font-size: 0.8rem;266 margin-top: 4px;267 position: absolute;268 bottom: -18px;269 left: 15px;270 white-space: nowrap;271 }272 273 .form-message {274 text-align: center;275 margin-top: 20px;276 font-size: 0.9rem;277 min-height: 1.2em;278 }279 280 .form-message.success {281 color: green;282 }283 284 .form-message.error {285 color: red;286 }287 288 .loading-spinner {289 display: inline-block;290 width: 18px;291 height: 18px;292 border: 3px solid rgba(255, 255, 255, .3);293 border-radius: 50%;294 border-top-color: #fff;295 animation: spin 1s ease-in-out infinite;296 margin-left: 10px;297 vertical-align: middle;298 }299 300 @keyframes spin {301 to { -webkit-transform: rotate(360deg); }302 }303 304 .form-inner form .btn input[type="submit"]:disabled {305 opacity: 0.7;306 cursor: not-allowed;307 position: relative;308 }309 310 /* --- Modal Styles --- */311 .modal-overlay {312 position: fixed;313 top: 0;314 left: 0;315 width: 100%;316 height: 100%;317 background-color: rgba(0, 0, 0, 0.6); /* Semi-transparent black overlay */318 display: flex;319 justify-content: center;320 align-items: center;321 z-index: 100; /* On top of everything */322 visibility: hidden; /* Hidden by default */323 opacity: 0;324 transition: visibility 0s, opacity 0.3s ease-in-out;325 }326 327 .modal-overlay.show {328 visibility: visible;329 opacity: 1;330 }331 332 .modal-content {333 background-color: var(--bs-body-bg);334 padding: 30px;335 border-radius: 15px;336 box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.7);337 text-align: center;338 max-width: 350px;339 width: 80%;340 transform: translateY(-20px); /* Slight animation on show */341 transition: transform 0.3s ease-in-out;342 }343 344 .modal-overlay.show .modal-content {345 transform: translateY(0);346 }347 348 .modal-message {349 font-size: 1.1rem;350 margin-bottom: 25px;351 color: var(--bs-body-color);352 }353 .modal-message p {354 margin-bottom: 10px; /* Spacing for paragraphs in modal message */355 line-height: 1.4;356 }357 .modal-message strong {358 font-size: 1.2rem; /* Heading for attempts message */359 display: block;360 margin-bottom: 15px;361 }362 .modal-message ul {363 list-style: none;364 padding: 0;365 margin-bottom: 20px;366 }367 .modal-message ul li {368 margin-bottom: 8px;369 font-size: 1rem;370 }371 372 373 .modal-button {374 background-color: var(--gakr-blue);375 color: #fff;376 border: none;377 padding: 10px 25px;378 border-radius: 10px;379 font-size: 1rem;380 cursor: pointer;381 transition: background-color 0.2s ease-in-out, transform 0.1s ease-in-out;382 }383 384 .modal-button:hover {385 background-color: var(--gakr-blue-dark);386 }387 388 .modal-button:active {389 transform: scale(0.98);390 }391 392 .modal-buttons-stacked {393 display: flex;394 flex-direction: column; /* Stack buttons vertically */395 gap: 10px;396 margin-top: 20px;397 }398 399 .modal-buttons-stacked .modal-button {400 width: 100%; /* Make buttons full width */401 }402 403 .modal-buttons-inline {404 display: flex;405 justify-content: space-between; /* Space them out */406 gap: 15px;407 margin-top: 20px;408 }409 .modal-buttons-inline .modal-button {410 flex-grow: 1; /* Allow buttons to grow to fill space */411 max-width: none; /* Override max-width from .modal-buttons */412 }413 414 415 /* Eye icon styles */416 .field .toggle-password {417 position: absolute;418 right: 15px;419 top: 50%;420 transform: translateY(-50%);421 cursor: pointer;422 color: var(--bs-secondary-color);423 font-size: 0.9rem;424 }425 426 .field .toggle-password:hover {427 color: var(--gakr-blue);428 }429 430 /* Added styles for Forgot/Reset Modals */431 .modal-form {432 text-align: left;433 margin-top: 20px;434 }435 .modal-form .field {436 margin-bottom: 15px;437 height: auto; /* Override default field height */438 }439 .modal-form label {440 display: block;441 margin-bottom: 5px;442 font-size: 0.9rem;443 color: var(--bs-body-color);444 }445 .otp-input-container {446 display: flex;447 gap: 5px;448 justify-content: center;449 margin-bottom: 10px;450 }451 .otp-digit {452 width: 40px;453 height: 40px;454 text-align: center;455 border: 1px solid var(--bs-border-color);456 border-radius: 5px;457 font-size: 1.2rem;458 background-color: var(--bs-body-bg);459 color: var(--bs-body-color);460 }461 .otp-digit:focus {462 border-color: var(--gakr-blue);463 outline: none;464 }465 .modal-form .error-message {466 position: static; /* Remove absolute positioning */467 margin-top: 5px;468 margin-left: 0;469 white-space: normal; /* Allow message to wrap */470 }471 .modal-buttons {472 display: flex;473 justify-content: center;474 gap: 15px;475 margin-top: 20px;476 }477 .modal-buttons button {478 width: 100%;479 max-width: 150px;480 }481 482 /* Header styles */483 .gakr-chat-header {484 padding: 0.75rem 1.5rem;485 display: flex;486 justify-content: space-between;487 align-items: center;488 border-bottom: 1px solid var(--bs-border-color);489 height: 64px;490 position: fixed;491 top: 0;492 left: 0;493 right: 0;494 width: 100%;495 background-color: var(--bs-body-bg);496 z-index: 10;497 }498 499 .gakr-logo-area {500 display: flex;501 align-items: center;502 gap: 0.75rem;503 }504 505 .gakr-brand-logo {506 width: 24px;507 height: 24px;508 display: flex;509 align-items: center;510 justify-content: center;511 }512 513 .gakr-brand-text {514 font-size: 1.25rem;515 font-weight: 500;516 color: var(--gakr-blue);517 }518 519 .gakr-nav-controls {520 display: flex;521 align-items: center;522 }523 524 .gakr-login-button {525 color: var(--gakr-blue);526 text-decoration: none;527 background: transparent;528 border: 1px solid var(--gakr-blue);529 padding: 0.5rem 1rem;530 border-radius: 50px;531 font-size: 0.9rem;532 transition: background-color 0.2s;533 }534 535 .gakr-login-button:hover {536 background-color: rgba(66, 133, 244, 0.1);537 }538 539 .gakr-profile-button {540 color: var(--gakr-blue);541 text-decoration: none;542 background: transparent;543 border: none;544 padding: 0.5rem 1rem;545 border-radius: 50px;546 font-size: 0.9rem;547 transition: background-color 0.2s;548 display: flex;549 align-items: center;550 gap: 0.5rem;551 }552 553 .gakr-profile-button:hover {554 background-color: rgba(66, 133, 244, 0.1);555 }556 </style>557</head>558<body>559 <header class="gakr-chat-header">560 <div class="gakr-logo-area">561 <a href="/" class="gakr-brand-logo">562 <i class="fas fa-robot" style="color: var(--gakr-blue);"></i>563 </a>564 <a href="/" class="gakr-brand-text" style="text-decoration: none;">GAKR AI</a>565 </div>566 567 <div class="gakr-nav-controls" id="authButtonContainer">568 <a href="/auth" class="gakr-login-button">Sign in / Register</a>569 </div>570 </header>571 572 <div class="wrapper">573 <div class="title-text">574 <div class="title login">Login Form</div>575 <div class="title signup">Signup Form</div>576 </div>577 <div class="form-container">578 <div class="slide-controls">579 <input type="radio" name="slide" id="login" checked>580 <input type="radio" name="slide" id="signup">581 <label for="login" class="slide login">Login</label>582 <label for="signup" class="slide signup">Signup</label>583 <div class="slider-tab"></div>584 </div>585 <div class="form-inner">586 <form action="#" class="login" id="loginForm">587 <div class="field">588 <input type="text" placeholder="Username or Email" required id="loginUsernameOrEmail" name="username_or_email">589 <div class="error-message" id="loginUsernameOrEmailError"></div>590 </div>591 <div class="field">592 <input type="password" placeholder="Password" required id="loginPassword" name="password">593 <span class="toggle-password fa-solid fa-eye-slash"></span>594 <div class="error-message" id="loginPasswordError"></div>595 </div>596 <div class="pass-link"><a href="#" id="forgotPasswordLink">Forgot password?</a></div>597 <div class="field btn">598 <input type="submit" value="Login">599 </div>600 <div class="signup-link">Not a member? <a href="">Signup now</a></div>601 </form>602 <form action="#" class="signup" id="signupForm">603 <div class="field">604 <input type="text" placeholder="Name" required id="signupName" name="name">605 <div class="error-message" id="signupNameError"></div>606 </div>607 <div class="field">608 <input type="text" placeholder="Email Address" required id="signupEmail" name="email">609 <div class="error-message" id="signupEmailError"></div>610 </div>611 <div class="field">612 <input type="password" placeholder="Password" required id="signupPassword" name="password">613 <span class="toggle-password fa-solid fa-eye-slash"></span>614 <div class="error-message" id="signupPasswordError"></div>615 </div>616 <div class="field">617 <input type="password" placeholder="Confirm password" required id="signupConfirmPassword" name="confirm_password">618 <span class="toggle-password fa-solid fa-eye-slash"></span>619 <div class="error-message" id="signupConfirmPasswordError"></div>620 </div>621 <div class="field btn">622 <input type="submit" value="Signup">623 </div>624 </form>625 </div>626 </div>627 <div class="form-message" id="generalFormMessage"></div>628 </div>629 630 <div class="modal-overlay" id="customModal">631 <div class="modal-content">632 <div class="modal-message" id="modalMessage"></div>633 <div id="modalButtonsContainer" class="modal-buttons">634 </div>635 </div>636 </div>637 638 <div class="modal-overlay" id="forgotPasswordRequestModal">639 <div class="modal-content" style="max-width: 450px;">640 <h3 style="text-align: center; margin-bottom: 20px; color: var(--gakr-blue);">Reset Password</h3>641 <p class="modal-message" id="forgotModalMessage" style="text-align: center; margin-bottom: 25px;">Enter your registered email address to receive a password reset code.</p>642 643 <form id="forgotPasswordRequestForm" class="modal-form">644 <div class="field" style="margin-bottom: 30px;">645 <label for="forgotEmail" style="display: block; margin-bottom: 8px; font-weight: 500; color: var(--bs-body-color);">Email Address:</label>646 <input type="email" id="forgotEmail" name="email" required style="width: 100%; padding: 12px; border: 2px solid var(--bs-border-color); border-radius: 8px; font-size: 16px; background-color: var(--bs-body-bg); color: var(--bs-body-color);" placeholder="Enter your email">647 <div class="error-message" id="forgotEmailError" style="margin-top: 8px;"></div>648 </div>649 <div class="modal-buttons-stacked">650 <button type="submit" class="modal-button" id="forgotSubmitButton" style="width: 100%; padding: 12px; font-size: 16px;">Send Reset Code</button>651 <button type="button" class="modal-button" id="forgotCancelButton" style="width: 100%; padding: 12px; font-size: 16px; background-color: var(--bs-secondary-color); margin-top: 10px;">Cancel</button>652 </div>653 </form>654 655 <div id="otpSection" style="display: none;">656 <h3 style="text-align: center; margin-bottom: 20px; color: var(--gakr-blue);">Enter Verification Code</h3>657 <p style="text-align: center; margin-bottom: 25px; color: var(--bs-secondary-color);">We've sent a 8-digit code to your email. Enter it below to continue.</p>658 <form id="verifyOtpForm" class="modal-form">659 <div class="field" style="margin-bottom: 30px;">660 <label style="display: block; margin-bottom: 15px; font-weight: 500; color: var(--bs-body-color); text-align: center;">Verification Code:</label>661 <div class="otp-input-container" style="display: flex; justify-content: center; gap: 8px; margin-bottom: 10px;">662 <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">663 <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">664 <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">665 <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">666 <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">667 <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">668 <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">669 <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">670 </div>671 <input type="hidden" id="otpInput" name="otp">672 <div class="error-message" id="otpError" style="text-align: center; margin-top: 10px;"></div>673 </div>674 <div class="modal-buttons-stacked">675 <button type="submit" class="modal-button" id="verifyOtpButton" style="width: 100%; padding: 12px; font-size: 16px;">Verify Code</button>676 <button type="button" class="modal-button" id="resendOtpButton" style="width: 100%; padding: 12px; font-size: 16px; margin-top: 10px;">Send Code Again</button>677 <button type="button" class="modal-button" id="otpCancelButton" style="width: 100%; padding: 12px; font-size: 16px; background-color: var(--bs-secondary-color); margin-top: 10px;">Cancel</button>678 </div>679 </form>680 </div>681 </div>682 </div>683 684 <div class="modal-overlay" id="resetPasswordModal">685 <div class="modal-content">686 <p class="modal-message">Enter your new password.</p>687 <form id="resetPasswordForm" class="modal-form">688 <div class="field">689 <label for="resetEmail">Email:</label>690 <input type="email" id="resetEmail" name="email" required readonly style="background-color: var(--bs-secondary-bg); cursor: not-allowed;">691 <div class="error-message" id="resetEmailError"></div>692 </div>693 <div class="field">694 <label for="newPassword">New Password:</label>695 <input type="password" id="newPassword" name="new_password" required>696 <span class="toggle-password fa-solid fa-eye-slash"></span>697 <div class="error-message" id="newPasswordError"></div>698 </div>699 <div class="field">700 <label for="confirmNewPassword">Confirm New Password:</label>701 <input type="password" id="confirmNewPassword" name="confirm_new_password" required>702 <span class="toggle-password fa-solid fa-eye-slash"></span>703 <div class="error-message" id="confirmNewPasswordError"></div>704 </div>705 <div class="modal-buttons-stacked">706 <button type="submit" class="modal-button" id="resetSubmitButton">Reset Password</button>707 <button type="button" class="modal-button" id="resetCancelButton" style="background-color: var(--bs-secondary-color);">Cancel</button>708 </div>709 </form>710 </div>711 </div>712 713<script>714 document.addEventListener('DOMContentLoaded', function() {715 // --- Authentication Status Check ---716 function updateAuthButton() {717 const authButtonContainer = document.getElementById('authButtonContainer');718 const userIsLoggedIn = localStorage.getItem('isLoggedIn') === 'true';719 720 if (userIsLoggedIn) {721 authButtonContainer.innerHTML = `722 <a href="/profile" class="gakr-profile-button">723 <i class="fas fa-user-circle me-2"></i>Profile724 </a>725 `;726 const profileButton = authButtonContainer.querySelector('.gakr-profile-button');727 if (profileButton) {728 profileButton.addEventListener('click', function(event) {729 console.log("Profile button clicked!");730 });731 }732 } else {733 authButtonContainer.innerHTML = `734 <a href="/auth" class="gakr-login-button">Sign in / Register</a>735 `;736 }737 }738 739 updateAuthButton();740 window.addEventListener('focus', updateAuthButton);741 window.addEventListener('storage', updateAuthButton);742 743 const loginText = document.querySelector(".title-text .login");744 const loginFormDiv = document.querySelector("form.login");745 const signupLink = document.querySelector("form .signup-link a");746 const loginRadio = document.getElementById('login');747 const signupRadio = document.getElementById('signup');748 749 const loginForm = document.getElementById('loginForm');750 const signupForm = document.getElementById('signupForm');751 752 // Existing form inputs and error displays753 const loginUsernameOrEmailInput = document.getElementById('loginUsernameOrEmail');754 const loginPasswordInput = document.getElementById('loginPassword');755 const loginSubmitButton = loginForm ? loginForm.querySelector('input[type="submit"]') : null;756 757 const signupNameInput = document.getElementById('signupName');758 const signupEmailInput = document.getElementById('signupEmail');759 const signupPasswordInput = document.getElementById('signupPassword');760 const signupConfirmPasswordInput = document.getElementById('signupConfirmPassword');761 const signupSubmitButton = signupForm ? signupForm.querySelector('input[type="submit"]') : null;762 763 const loginUsernameOrEmailError = document.getElementById('loginUsernameOrEmailError');764 const loginPasswordError = document.getElementById('loginPasswordError');765 766 const signupNameError = document.getElementById('signupNameError');767 const signupEmailError = document.getElementById('signupEmailError');768 const signupPasswordError = document.getElementById('signupPasswordError');769 const signupConfirmPasswordError = document.getElementById('signupConfirmPasswordError');770 771 const generalFormMessage = document.getElementById('generalFormMessage');772 773 // Modal elements (main alert modal)774 const customModal = document.getElementById('customModal');775 const modalMessageDiv = document.getElementById('modalMessage');776 const modalButtonsContainer = document.getElementById('modalButtonsContainer');777 778 // OTP digit inputs779 const otpDigits = document.querySelectorAll('.otp-digit');780 otpDigits.forEach((input, index) => {781 input.addEventListener('input', function() {782 if (this.value.length === 1 && index < otpDigits.length - 1) {783 otpDigits[index + 1].focus();784 }785 });786 input.addEventListener('keydown', function(e) {787 if (e.key === 'Backspace' && this.value === '' && index > 0) {788 otpDigits[index - 1].focus();789 }790 });791 });792 const forgotPasswordLink = document.getElementById('forgotPasswordLink');793 const forgotPasswordRequestModal = document.getElementById('forgotPasswordRequestModal'); // The main overlay for forgot/otp794 const forgotModalMessage = document.getElementById('forgotModalMessage'); // Message within the forgot/otp modal795 796 // Elements for Step 1: Request Email797 const forgotPasswordRequestForm = document.getElementById('forgotPasswordRequestForm');798 const forgotEmailInput = document.getElementById('forgotEmail');799 const forgotEmailError = document.getElementById('forgotEmailError');800 const forgotSubmitButton = document.getElementById('forgotSubmitButton');801 const forgotCancelButton = document.getElementById('forgotCancelButton');802 803 // Elements for Step 2: Enter OTP804 const otpSection = document.getElementById('otpSection'); // The container for OTP input and buttons805 const verifyOtpForm = document.getElementById('verifyOtpForm');806 const otpInput = document.getElementById('otpInput');807 const otpError = document.getElementById('otpError');808 const verifyOtpButton = document.getElementById('verifyOtpButton');809 const resendOtpButton = document.getElementById('resendOtpButton');810 const otpCancelButton = document.getElementById('otpCancelButton');811 812 // Reset Password Modal elements (Step 3: New Password)813 const resetPasswordModal = document.getElementById('resetPasswordModal');814 const resetPasswordForm = document.getElementById('resetPasswordForm');815 const resetEmailInput = document.getElementById('resetEmail'); // Hidden but read-only email field816 const newPasswordInput = document.getElementById('newPassword');817 const confirmNewPasswordInput = document.getElementById('confirmNewPassword');818 const resetEmailError = document.getElementById('resetEmailError'); // Error for reset email (should be rare)819 const newPasswordError = document.getElementById('newPasswordError');820 const confirmNewPasswordError = document.getElementById('confirmNewPasswordError');821 const resetSubmitButton = document.getElementById('resetSubmitButton');822 const resetCancelButton = document.getElementById('resetCancelButton');823 824 // New: Login Attempt Counter825 let loginAttempts = 0;826 const MAX_LOGIN_ATTEMPTS = 3;827 828 // Stores the email across the forgot password/OTP/reset flow829 let currentForgotEmail = '';830 831 // Resend OTP cooldown variables832 let resendCooldown = 0;833 let resendInterval = null;834 835 // Function to start resend cooldown timer836 function startResendCooldown() {837 resendCooldown = 60; // 60 seconds838 resendOtpButton.disabled = true;839 resendOtpButton.textContent = `Send Again (60s)`;840 resendOtpButton.style.backgroundColor = 'var(--bs-secondary-color)'; // Gray during cooldown841 842 resendInterval = setInterval(() => {843 resendCooldown--;844 if (resendCooldown > 0) {845 resendOtpButton.textContent = `Send Again (${resendCooldown}s)`;846 } else {847 clearInterval(resendInterval);848 resendInterval = null;849 resendOtpButton.disabled = false;850 resendOtpButton.textContent = 'Send Code Again';851 resendOtpButton.style.backgroundColor = ''; // Reset to default (primary blue)852 }853 }, 1000);854 }855 856 // Function to clear all error messages and input error classes857 function clearErrors(formType = 'all') {858 if (formType === 'all' || formType === 'main') {859 document.querySelectorAll('.error-message').forEach(el => el.textContent = '');860 document.querySelectorAll('.form-inner form .field input').forEach(el => el.classList.remove('error'));861 if (generalFormMessage) {862 generalFormMessage.textContent = '';863 generalFormMessage.className = 'form-message';864 }865 }866 if (formType === 'all' || formType === 'forgot') {867 if (forgotEmailError) forgotEmailError.textContent = '';868 if (forgotEmailInput) forgotEmailInput.classList.remove('error');869 // Also clear the general forgotModalMessage when resetting for new flow870 if (forgotModalMessage) forgotModalMessage.innerHTML = 'Enter your registered email to request a password reset.';871 }872 if (formType === 'all' || formType === 'otp') {873 if (otpError) otpError.textContent = '';874 if (otpInput) otpInput.classList.remove('error');875 }876 if (formType === 'all' || formType === 'reset') {877 if (resetEmailError) resetEmailError.textContent = '';878 if (newPasswordError) newPasswordError.textContent = '';879 if (confirmNewPasswordError) confirmNewPasswordError.textContent = '';880 if (resetEmailInput) resetEmailInput.classList.remove('error');881 if (newPasswordInput) newPasswordInput.classList.remove('error');882 if (confirmNewPasswordInput) confirmNewPasswordInput.classList.remove('error');883 }884 }885 886 // Function to display general form-level message (below the forms)887 function displayFormMessage(message, type = 'info') {888 if (generalFormMessage) {889 generalFormMessage.textContent = message;890 generalFormMessage.className = 'form-message ' + type;891 }892 }893 894 // Universal modal display function895 function showCustomModal(modalElement) {896 modalElement.classList.add('show');897 }898 899 // Universal modal hide function900 function hideCustomModal(modalElement) {901 modalElement.classList.remove('show');902 clearErrors(); // Clear all errors when any modal is hidden903 }904 905 // Function to show a flexible custom modal (for general alerts/info)906 function showFlexibleModal(messageHTML, buttonsConfig, callback = null) {907 modalMessageDiv.innerHTML = messageHTML;908 modalButtonsContainer.innerHTML = ''; // Clear previous buttons909 910 if (buttonsConfig && buttonsConfig.length > 0) {911 const isInline = buttonsConfig.every(btn => !btn.newline);912 modalButtonsContainer.className = isInline ? 'modal-buttons-inline' : 'modal-buttons-stacked';913 914 buttonsConfig.forEach(btnConf => {915 const button = document.createElement('button');916 button.className = 'modal-button';917 button.textContent = btnConf.text;918 if (btnConf.style) {919 button.style = btnConf.style;920 }921 button.onclick = function() {922 hideCustomModal(customModal);923 if (btnConf.action && typeof btnConf.action === 'function') {924 btnConf.action();925 }926 if (callback && typeof callback === 'function') {927 callback();928 }929 };930 modalButtonsContainer.appendChild(button);931 });932 } else {933 const okButton = document.createElement('button');934 okButton.className = 'modal-button';935 okButton.textContent = 'OK';936 okButton.onclick = function() {937 hideCustomModal(customModal);938 if (callback && typeof callback === 'function') {939 callback();940 }941 };942 modalButtonsContainer.classList.remove('modal-buttons-inline', 'modal-buttons-stacked');943 modalButtonsContainer.classList.add('modal-buttons');944 modalButtonsContainer.appendChild(okButton);945 }946 showCustomModal(customModal);947 }948 949 // Slide logic950 function slideToSignup() {951 if (loginFormDiv && loginText) {952 loginFormDiv.style.marginLeft = "-50%";953 loginText.style.marginLeft = "-50%";954 clearErrors('main');955 }956 }957 958 function slideToLogin() {959 if (loginFormDiv && loginText) {960 loginFormDiv.style.marginLeft = "0%";961 loginText.style.marginLeft = "0%";962 clearErrors('main');963 }964 }965 966 // Event listeners for radio buttons and signup link967 if (signupRadio) {968 signupRadio.addEventListener('change', function() {969 if (this.checked) {970 slideToSignup();971 }972 });973 }974 975 if (loginRadio) {976 loginRadio.addEventListener('change', function() {977 if (this.checked) {978 slideToLogin();979 }980 });981 }982 983 if (signupLink && signupRadio) {984 signupLink.onclick = ((e) => {985 e.preventDefault();986 signupRadio.checked = true;987 slideToSignup();988 });989 }990 991 // Handle URL parameter for initial tab992 const urlParams = new URLSearchParams(window.location.search);993 const showSignup = urlParams.get('signup');994 995 if (showSignup === 'true') {996 if (signupRadio) {997 signupRadio.checked = true;998 slideToSignup();999 }1000 } else {1001 if (loginRadio) {1002 loginRadio.checked = true;1003 slideToLogin();1004 }1005 }1006 1007 // Function to toggle password visibility1008 function setupPasswordToggle(passwordInput, toggleIcon) {1009 if (passwordInput && toggleIcon) {1010 toggleIcon.addEventListener('click', function() {1011 const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password';1012 passwordInput.setAttribute('type', type);1013 this.classList.toggle('fa-eye');1014 this.classList.toggle('fa-eye-slash');1015 });1016 }1017 }1018 1019 // Setup password toggles for all password fields1020 setupPasswordToggle(loginPasswordInput, document.querySelector('#loginForm .toggle-password'));1021 setupPasswordToggle(signupPasswordInput, document.querySelector('#signupForm #signupPassword + .toggle-password'));1022 setupPasswordToggle(signupConfirmPasswordInput, document.querySelector('#signupForm #signupConfirmPassword + .toggle-password'));1023 setupPasswordToggle(newPasswordInput, document.querySelector('#resetPasswordForm #newPassword + .toggle-password'));1024 setupPasswordToggle(confirmNewPasswordInput, document.querySelector('#resetPasswordForm #confirmNewPassword + .toggle-password'));1025 1026 1027 // Login Form Submission Handler1028 if (loginForm) {1029 loginForm.addEventListener('submit', async function(event) {1030 event.preventDefault();1031 clearErrors('main'); // Clear main form errors1032 1033 // Only proceed if login attempts are below limit1034 if (loginAttempts >= MAX_LOGIN_ATTEMPTS) {1035 showLoginAttemptsLimitModal();1036 return; // Stop submission1037 }1038 1039 let isValid = true;1040 1041 const usernameOrEmail = loginUsernameOrEmailInput.value.trim();1042 const password = loginPasswordInput.value.trim();1043 1044 if (usernameOrEmail === '') {1045 isValid = false;1046 loginUsernameOrEmailError.textContent = 'Username or Email is required';1047 loginUsernameOrEmailInput.classList.add('error');1048 }1049 if (password === '') {1050 isValid = false;1051 loginPasswordError.textContent = 'Password is required';1052 loginPasswordInput.classList.add('error');1053 }1054 1055 if (isValid) {1056 // Check if user exists1057 try {1058 const checkResponse = await fetch('/api/check_user', {1059 method: 'POST',1060 headers: { 'Content-Type': 'application/json' },1061 body: JSON.stringify({ identifier: usernameOrEmail })1062 });1063 const checkData = await checkResponse.json();1064 if (!checkData.exists) {1065 loginUsernameOrEmailError.textContent = 'User does not exist.';1066 loginUsernameOrEmailInput.classList.add('error');1067 return;1068 }1069 } catch (error) {1070 console.error('Error checking user:', error);1071 // Proceed anyway1072 }1073 submitFormData(event.target, loginSubmitButton, '/api/login', 'main');1074 }1075 });1076 }1077 1078 // Signup Form Submission Handler1079 if (signupForm) {1080 signupForm.addEventListener('submit', async function(event) {1081 event.preventDefault();1082 clearErrors('main');1083 1084 let isValid = true;1085 1086 const name = signupNameInput.value.trim();1087 const email = signupEmailInput.value.trim();1088 const password = signupPasswordInput.value.trim();1089 const confirmPassword = signupConfirmPasswordInput.value.trim();1090 1091 if (name === '') {1092 isValid = false;1093 signupNameError.textContent = 'Name is required';1094 signupNameInput.classList.add('error');1095 }1096 1097 if (email === '') {1098 isValid = false;1099 signupEmailError.textContent = 'Email is required';1100 signupEmailInput.classList.add('error');1101 } else if (!isValidEmail(email)) {1102 isValid = false;1103 signupEmailError.textContent = 'Enter a valid email address';1104 signupEmailInput.classList.add('error');1105 }1106 1107 if (password === '') {1108 isValid = false;1109 signupPasswordError.textContent = 'Password is required';1110 signupPasswordInput.classList.add('error');1111 } else if (password.length < 6) {1112 isValid = false;1113 signupPasswordError.textContent = 'Password must be at least 6 characters';1114 signupPasswordInput.classList.add('error');1115 }1116 1117 if (confirmPassword === '') {1118 isValid = false;1119 signupConfirmPasswordError.textContent = 'Confirm password is required';1120 signupConfirmPasswordInput.classList.add('error');1121 } else if (password !== confirmPassword) {1122 isValid = false;1123 signupConfirmPasswordError.textContent = 'Passwords do not match';1124 signupConfirmPasswordInput.classList.add('error');1125 signupPasswordInput.classList.add('error');1126 }1127 1128 if (isValid) {1129 // Check if user already exists1130 try {1131 const checkResponse = await fetch('/api/check_user', {1132 method: 'POST',1133 headers: { 'Content-Type': 'application/json' },1134 body: JSON.stringify({ identifier: email })1135 });1136 const checkData = await checkResponse.json();1137 if (checkData.exists) {1138 signupEmailError.textContent = 'Email already registered.';1139 signupEmailInput.classList.add('error');1140 return;1141 }1142 } catch (error) {1143 console.error('Error checking user:', error);1144 // Proceed anyway1145 }1146 submitFormData(event.target, signupSubmitButton, '/api/signup', 'main');1147 }1148 });1149 }1150 1151 // Forgot Password Link Handler (Initial Click)1152 if (forgotPasswordLink) {1153 forgotPasswordLink.addEventListener('click', function(event) {1154 event.preventDefault();1155 clearErrors('forgot'); // Clear errors for email request1156 clearErrors('otp'); // Clear errors for OTP section1157 1158 forgotPasswordRequestForm.reset(); // Clear email input1159 otpInput.value = ''; // Clear OTP input1160 // Reset the general message for the forgot password modal1161 forgotModalMessage.innerHTML = 'Enter your registered email to request a password reset.';1162 1163 // Show the email request form and hide the OTP section1164 forgotPasswordRequestForm.style.display = 'block';1165 if (otpSection) otpSection.style.display = 'none'; // Ensure OTP section is hidden initially1166 1167 // Reset modal size for email input1168 const modalContent = forgotPasswordRequestModal.querySelector('.modal-content');1169 if (modalContent) {1170 modalContent.style.maxWidth = '450px';1171 }1172 1173 showCustomModal(forgotPasswordRequestModal);1174 });1175 }1176 1177 // Step 1: Forgot Password Request Form Submission Handler (Requests OTP)1178 if (forgotPasswordRequestForm) {1179 forgotPasswordRequestForm.addEventListener('submit', async function(event) {1180 event.preventDefault();1181 clearErrors('forgot'); // Clear errors specific to the email request form1182 1183 let isValid = true;1184 const email = forgotEmailInput.value.trim();1185 1186 if (email === '') {1187 isValid = false;1188 forgotEmailError.textContent = 'Email is required.';1189 forgotEmailInput.classList.add('error');1190 } else if (!isValidEmail(email)) {1191 isValid = false;1192 forgotEmailError.textContent = 'Enter a valid email address.';1193 forgotEmailInput.classList.add('error');1194 }1195 1196 if (isValid) {1197 forgotSubmitButton.disabled = true;1198 forgotSubmitButton.innerHTML = '<span class="loading-spinner"></span> Sending OTP...';1199 1200 try {