You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1276 lines
34 KiB

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments/user-head :: head}"></head>
<style>
:root {
--bg: #f6f3ef;
--accent: #12363b;
--dark: #2f1f1b;
--muted: #7b7773;
--panel-w: 660px;
--radius: 14px;
}
/* Reset */
* {
box-sizing: border-box;
margin: 0;
padding: 0
}
html,
body {
height: 100%
}
body {
font-family: "Inter", "Helvetica Neue", Arial, sans-serif;
background:
linear-gradient(rgba(255, 255, 255, 0.70), rgba(255, 255, 255, 0.82)),
url('/victoriapublichall/images/vph-background_v1.jpg') top center / cover no-repeat,
url('/victoriapublichall/images/vph-background_v1.jpg') bottom center / cover no-repeat;
min-height: 100vh;
background-attachment: fixed;
/* keeps both images static */
background-repeat: no-repeat;
background-size: cover;
-webkit-font-smoothing: antialiased;
min-height: 100vh;
display: flex;
flex-direction: column;
gap: 12px;
padding: 18px 24px;
}
/* Mobile view image should be FULL COVER, NO REPEAT */
@media (max-width: 520px) {
body {
background:
linear-gradient(rgba(255, 255, 255, 0.75), rgba(255, 255, 255, 0.90)),
url('/victoriapublichall/images/vph-background_v1.jpg');
background-size: 100em;
/* full cover */
background-repeat: no-repeat;
background-position: center center;
background-attachment: scroll;
/* prevents weird zooming on mobile */
/* remove second image on mobile */
}
}
/* container */
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
}
/******************************
NAVBAR (desktop + mobile)
******************************/
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 10px 0;
position: relative;
z-index: 1300;
}
.leftbar {
display: flex;
align-items: center;
gap: 20px;
}
/* small logo + layout helpers */
.nav-left {
display: flex;
align-items: center;
gap: 12px;
}
.logo {
width: 72px;
height: 72px;
object-fit: contain;
border-radius: 50%;
}
/* default desktop nav */
nav.primary {
display: flex;
gap: 18px;
align-items: center;
}
/* Desktop Navigation Links (Figma Accurate) */
nav.primary a {
font-family: "Playfair Display", serif !important;
font-weight: 400 !important;
/* Regular */
font-style: normal !important;
font-size: 15px !important;
/* Figma size */
line-height: 100% !important;
letter-spacing: 0 !important;
color: #3C1E12;
/* matches Book a Visit color */
text-decoration: none;
padding: 6px 8px;
/* keep your layout spacing */
border-radius: 6px;
transition: background 0.2s ease;
}
/* Hover state (optional subtle effect like Figma preview) */
nav.primary a:hover {
background: rgba(0, 0, 0, 0.06);
color: #3C1E12;
/* no change in color */
}
/* Mobile menu links */
.mobile-nav a {
font-family: "Playfair Display", serif !important;
font-weight: 400 !important;
font-size: 15px !important;
letter-spacing: 0 !important;
color: #3C1E12;
}
.rightbar {
display: flex;
align-items: center;
gap: 12px;
}
/* desktop CTA (Book a Visit) */
/* Desktop CTA (Book a Visit) — Updated with Figma specs */
/* Desktop CTA (Book a Visit) — With Figma Border */
#desktopBookBtn {
width: 174px;
/* Figma button width */
height: 57px;
/* Figma button height */
display: inline-flex;
align-items: center;
justify-content: center;
background: transparent;
border-radius: 8px;
/* Figma corner radius */
/* Border from Figma (thin outline) */
border: 1px solid rgba(60, 30, 18, 0.40);
/* darker visible border */
cursor: pointer;
/* Figma Typography */
font-family: "Playfair Display", serif !important;
font-weight: 700 !important;
/* Bold */
font-style: italic !important;
font-size: 24px !important;
line-height: 100% !important;
/* Letter spacing -6% = -0.06em */
letter-spacing: -0.06em !important;
color: #3C1E12 !important;
}
/* Hover effect (Figma-like subtle shadow) */
#desktopBookBtn:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.10);
}
/* Keep existing responsive behavior */
@media (max-width: 900px) {
nav.primary {
display: none !important;
}
#desktopBookBtn {
display: none !important;
}
.hamburger {
display: flex !important;
}
/* Mobile logo size */
.logo {
width: 44px;
height: 44px;
}
}
/* hamburger (hidden on desktop) */
.hamburger {
display: none;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
border-radius: 8px;
border: 1px solid rgba(0, 0, 0, 0.06);
background: transparent;
cursor: pointer;
padding: 8px;
}
.hamburger .bar {
display: block;
width: 18px;
height: 2px;
background: var(--dark);
margin: 3px 0;
transition: transform .2s, opacity .2s;
}
/* mobile floating menu + overlay (initial hidden) */
.mobile-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.22);
z-index: 1350;
opacity: 0;
pointer-events: none;
transition: opacity .22s ease;
}
.mobile-overlay.show {
opacity: 1;
pointer-events: auto;
}
.mobile-nav {
position: fixed;
left: 12px;
right: 12px;
top: 68px;
max-width: 420px;
margin: 0 auto;
background: #fff;
border-radius: 12px;
padding: 14px;
box-shadow: 0 22px 60px rgba(0, 0, 0, 0.16);
border: 1px solid rgba(0, 0, 0, 0.06);
z-index: 1400;
transform: translateY(-8px) scale(.995);
opacity: 0;
pointer-events: none;
transition: opacity .22s ease, transform .22s ease;
}
.mobile-nav.open {
opacity: 1;
transform: translateY(0) scale(1);
pointer-events: auto;
}
.mobile-nav a {
display: block;
padding: 10px 12px;
color: var(--dark);
text-decoration: none;
border-radius: 8px;
font-weight: 600;
}
.mobile-nav a+a {
margin-top: 8px;
}
.mobile-nav .book-cta {
background: #f7f9f9;
text-align: center;
padding: 12px;
color: var(--accent);
font-weight: 700;
}
/* small screens tweak */
@media (max-width:520px) {
.logo {
width: 61px !important;
/* Figma width */
height: 61px !important;
/* Figma height */
object-fit: contain;
}
.mobile-nav {
left: 12px;
right: 12px;
top: 68px;
max-width: 420px;
margin: 0 auto;
background: #fff;
border-radius: 12px;
padding: 14px;
box-shadow: 0 22px 60px rgba(0, 0, 0, 0.16);
border: 1px solid rgba(0, 0, 0, 0.06);
z-index: 1400;
transform: translateY(-8px) scale(.995);
opacity: 0;
pointer-events: none;
transition: opacity .22s ease, transform .22s ease;
}
}
/******************************
PAGE / HERO / PANEL
******************************/
.page {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
margin-top: 20px;
}
.panel-wrap {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
margin-top: 18px;
}
.hero {
width: 100%;
max-width: 920px;
text-align: center;
margin-bottom: 12px;
}
/* Desktop style */
.page h1 {
font-family: "Inter", sans-serif;
font-weight: 700;
font-style: normal;
font-size: 48px;
line-height: 100%;
letter-spacing: 0;
text-align: center;
width: 100%;
max-width: 444px;
/* Figma width */
margin: 0 auto 10px;
/* Center + bottom spacing */
color: #3B1E14 !important;
}
/* Mobile view */
@media (max-width:520px) {
.page h1 {
font-family: "Inter", sans-serif;
font-weight: 700;
font-style: normal;
font-size: 32px !important;
/* Figma size */
line-height: 100% !important;
/* Figma line height */
letter-spacing: 0;
width: 296px;
/* Figma width */
max-width: 90%;
/* Responsive safety */
margin: 0 auto;
/* Center horizontally */
text-align: center;
/* Figma alignment */
color: #3B1E14 !important;
/* Requested color */
}
}
/* Desktop version */
.hero .lead {
font-family: "Inter", sans-serif;
font-weight: 400;
font-style: normal;
font-size: 20px;
line-height: 100%;
letter-spacing: 0;
text-align: center;
width: 100%;
max-width: 417px;
/* From Figma */
margin: 0 auto 20px;
/* Center + spacing */
color: #000000;
/* BLACK COLOR as requested */
}
/* Mobile version */
@media (max-width:520px) {
.hero .lead {
font-family: "Inter", sans-serif;
font-weight: 400;
font-style: normal;
font-size: 14px !important;
/* Figma size */
line-height: 100% !important;
/* Figma line height */
letter-spacing: 0;
width: 275px;
/* Figma width */
max-width: 90%;
/* keep responsive */
margin: 0 auto 20px;
/* center align */
text-align: center;
/* Figma alignment */
color: #000000 !important;
/* Text color */
}
}
/* Desktop version */
.panel h3 {
font-family: "Inter", sans-serif;
font-weight: 700;
/* bold */
font-style: normal;
font-size: 20px;
line-height: 100%;
letter-spacing: 0;
text-align: center;
color: #000000;
/* BLACK TEXT */
width: 100%;
max-width: 336px;
/* Figma width */
margin: 0 auto 14px;
/* center + bottom spacing */
}
/* Mobile version */
@media (max-width:520px) {
.panel h3 {
font-size: 24px;
/* slightly smaller */
max-width: 120%;
line-height: 140%;
}
}
/* Desktop version */
.muted-label {
font-family: "Inter", sans-serif;
font-weight: 700;
font-style: normal;
font-size: 16px;
line-height: 100%;
letter-spacing: 0;
color: #3B1E14;
width: 100%;
max-width: 100%;
margin-bottom: 8px;
text-align: left;
display: block;
margin-left: 40px !important;
/* ⬅ FIXES CENTER ISSUE */
}
/* Mobile version */
@media (max-width:520px) {
.muted-label {
font-size: 16px;
max-width: 1400%;
line-height: 140%;
text-align: left;
margin-left: 0 !important;
/* keep left */
}
}
/* Desktop version */
.input {
width: 100%;
max-width: 536px;
/* Figma width */
padding: 16px 18px;
/* creates approx 65px height */
border-radius: 14px;
/* Figma radius */
border: 1px solid rgba(0, 0, 0, 0.06);
background: #ffffff;
font-family: "Inter", sans-serif;
font-size: 16px;
outline: none;
box-sizing: border-box;
}
/* Focus glow */
.input:focus {
box-shadow: 0 8px 26px rgba(20, 30, 30, 0.04);
border-color: rgba(0, 0, 0, 0.14);
}
/* Mobile version */
@media (max-width:520px) {
.input {
max-width: 140%;
line-height: 140%;
padding: 16px 18px;
/* better fit on mobile */
border-radius: 12px;
}
}
/* Desktop version */
.btn {
width: 100%;
max-width: 536px;
/* Figma width */
padding: 18px 20px;
/* visual height approx 66px */
border-radius: 11px;
/* Figma radius */
background: var(--accent);
/* your theme greenish color */
color: #ffffff;
font-family: "Inter", sans-serif;
font-weight: 700;
font-size: 16px;
border: none;
cursor: pointer;
box-shadow: 0 8px 26px rgba(18, 54, 59, 0.12);
transition: none !important;
/* Animate: Instant */
}
/* Disabled version */
.btn[disabled] {
opacity: .6;
cursor: not-allowed;
box-shadow: none;
}
/* Mobile version */
@media (max-width:520px) {
.btn {
max-width: 140%;
line-height: 140%;
padding: 16px 18px;
/* smaller height on mobile */
border-radius: 12px;
font-size: 18px;
}
}
.links-row {
width: 100%;
max-width: 536px;
display: flex;
justify-content: space-between;
/* pushes them to opposite sides */
align-items: center;
margin: 8px auto 0;
/* center align inside panel */
padding: 0 4px;
/* small spacing for balance */
}
/* Change phone/email */
.changeLinkrow a {
font-family: "Inter", sans-serif;
font-size: 16px;
font-weight: 400;
line-height: 100%;
color: #0478D0;
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: 2px;
cursor: pointer;
transition: none !important;
}
.changeLinkrow a.disabled {
color: rgb(0, 128, 192) !important;
pointer-events: none;
}
/* Resend OTP */
.resendLinkrow {
text-align: right;
}
.resendLinkrow a {
font-family: "Inter", sans-serif;
font-size: 16px;
font-weight: 400;
line-height: 100%;
color: #0478D0;
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: 2px;
cursor: pointer;
transition: none !important;
}
.resendLinkrow a.disabled {
color: #0478D0;
pointer-events: none;
}
.otp-input {
width: 536px;
/* height: 65px; */
max-width: 100%;
/* keeps it responsive on mobile */
padding: 16px 18px;
border-radius: 14px;
/* Figma: Radius 14px */
background: #ffffff;
border: 1px solid rgba(0, 0, 0, 0.06);
font-family: "Inter", sans-serif;
font-size: 20px;
/* OTP input usually bigger for readability */
font-weight: 400;
line-height: 100%;
outline: none;
box-sizing: border-box;
}
/* Focus effect (same style as your other inputs) */
.otp-input:focus {
border-color: rgba(0, 0, 0, 0.12);
box-shadow: 0 8px 26px rgba(20, 30, 30, 0.04);
}
/* Mobile View – make it fit screen */
@media (max-width: 520px) {
.otp-input {
max-width: 140%;
line-height: 140%;
padding: 16px 18px;
width: 100%;
height: 55px;
font-size: 18px;
border-radius: 12px;
}
}
.panel {
width: 100%;
max-width: var(--panel-w);
/* GLASS EFFECT PANEL */
background: rgba(255, 255, 255, 0);
border-radius: var(--radius);
padding: 26px;
border: 1px solid rgba(255, 255, 255, 0);
}
</style>
<body>
<div class="container">
<!-- ============================
NAVBAR: REPLACE/EDIT HERE
(Brand + desktop nav + CTA; mobile hamburger + menu below)
============================ -->
<!-- NAVBAR (replace your existing header / mobile nav markup with this) -->
<header class="topbar" role="banner">
<div class="leftbar">
<div class="nav-left">
<!-- logo image — use same img src you already have -->
<img th:src="@{images/VPH-logo_main-EN 1.png}" alt="VPH logo" class="logo">
</div>
<!-- desktop nav (hidden on mobile) -->
<nav class="primary" aria-label="Primary navigation">
<a href="/victoriapublichall/home">Home</a>
<a href="#">About VPH</a>
<a href="/victoriapublichall/visit">Visit</a>
<a href="#">Contact Us</a>
</nav>
</div>
<!-- right side: hamburger (mobile) + desktop CTA -->
<div class="rightbar">
<button id="hambtn" class="hamburger" aria-label="Open menu" aria-expanded="false" aria-controls="mobileNav">
<span class="bar bar1" aria-hidden="true"></span>
<span class="bar bar2" aria-hidden="true"></span>
<span class="bar bar3" aria-hidden="true"></span>
</button>
</div>
</header>
<!-- overlay (click to close) -->
<div id="mobileOverlay" class="mobile-overlay" aria-hidden="true"></div>
<!-- mobile nav -->
<nav id="mobileNav" class="mobile-nav" aria-hidden="true" role="dialog" aria-modal="true" aria-label="Mobile menu">
<a href="/victoriapublichall/home">Home</a>
<a href="#">About VPH</a>
<a href="/victoriapublichall/visit">Visit</a>
<a href="#">Contact Us</a>
</nav>
<!-- ============================
PAGE CONTENT (HERO + OTP PANEL)
============================ -->
<main class="page" role="main" aria-labelledby="hero-heading">
<div class="hero">
<h1 id="hero-heading">Victoria Public Hall</h1>
<div class="lead">Experience Chennai's First Town Hall</div>
<div class="panel-wrap">
<div class="panel" role="region" aria-label="OTP booking panel">
<h3>Book your visit / Manage Bookings</h3>
<br>
<span class="muted-label" for="contactInput">Enter phone number or email to proceed</span>
<!-- initial input -->
<div id="input-block">
<input id="contactInput" class="input" type="text" inputmode="text" placeholder="Phone | Email"
autocomplete="off" aria-label="Phone or Email">
<div style="height:12px"></div>
<button id="sendBtn" class="btn" type="button">Send OTP</button>
</div>
<!-- after-send block -->
<div id="sent-block" style="display:none">
<input id="contactInputDisabled" class="input" type="text" disabled>
<div style="height:8px"></div>
<button id="sendBtnDisabled" class="btn" disabled>Send OTP</button>
<div class="links-row" style="margin-top:8px">
<div class="changeLinkrow">
<a href="#" id="changeLink">Change phone/email</a>
</div>
<div class="resendLinkrow">
<a href="#" id="resendLink" class="disabled">Resend OTP</a>
</div>
</div>
<div class="otp-area" id="otpArea" aria-hidden="true">
<span class="muted-label" for="otpInput" style="margin-top:12px">Enter OTP</span>
<input id="otpInput" class="otp-input" type="text" maxlength="5" placeholder="Enter OTP"
aria-label="OTP">
<div style="height:12px"></div>
<button id="submitOtpBtn" class="btn" type="button">Submit</button>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
<div id="pageLoader"
style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255,255,255,0.8); z-index: 9999; text-align: center;">
<div style="position: relative; top: 50%; transform: translateY(-50%);">
<div class="spinner-border text-primary" style="width: 3rem; height: 3rem;"></div>
<p style="margin-top: 1rem; font-size: 1.2rem; color: #007bff;">Please wait...</p>
</div>
</div>
<script th:replace="~{fragments/user-script :: script}"></script>
<script>
$(document).ready(function () {
$('#otpInput').on('input', function () {
// Allow only digits
this.value = this.value.replace(/\D/g, '');
// Max 10 digits
if (this.value.length > 5) {
this.value = this.value.slice(0, 5);
}
});
});
/* ========================
MOBILE NAV (improved)
======================== */
(function () {
const hambtn = document.getElementById('hambtn');
const mobileNav = document.getElementById('mobileNav');
const mobileOverlay = document.getElementById('mobileOverlay');
const desktopBook = document.getElementById('desktopBookBtn');
const mobileBook = document.getElementById('mobileBook');
if (!hambtn || !mobileNav || !mobileOverlay) {
// nothing to do if elements missing
} else {
function openMenu() {
hambtn.setAttribute('aria-expanded', 'true');
mobileNav.classList.add('open'); mobileNav.setAttribute('aria-hidden', 'false');
mobileOverlay.classList.add('show'); mobileOverlay.setAttribute('aria-hidden', 'false');
document.body.style.overflow = 'hidden';
const first = mobileNav.querySelector('a'); if (first) first.focus();
}
function closeMenu() {
hambtn.setAttribute('aria-expanded', 'false');
mobileNav.classList.remove('open'); mobileNav.setAttribute('aria-hidden', 'true');
mobileOverlay.classList.remove('show'); mobileOverlay.setAttribute('aria-hidden', 'true');
document.body.style.overflow = '';
hambtn.focus();
}
hambtn.addEventListener('click', () => {
const isOpen = hambtn.getAttribute('aria-expanded') === 'true';
if (isOpen) closeMenu(); else openMenu();
});
mobileOverlay.addEventListener('click', closeMenu);
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && mobileNav.classList.contains('open')) closeMenu();
});
mobileNav.querySelectorAll('a').forEach(a => {
a.addEventListener('click', () => {
// close after clicking any mobile link
closeMenu();
});
});
// ensure menu closes when resizing to desktop
window.addEventListener('resize', () => {
if (window.innerWidth > 900 && mobileNav.classList.contains('open')) closeMenu();
});
}
// Optional: wire desktop Book a Visit to the same booking action as mobile
if (desktopBook && mobileBook) {
desktopBook.addEventListener('click', (e) => {
e.preventDefault();
// mimic mobile behavior for demo: show alert (replace with real action)
alert('Book a Visit clicked — open booking flow here.');
});
mobileBook.addEventListener('click', (e) => {
// mobile link closes menu via handler above and then can navigate or open booking flow
// if you want to override, uncomment below
// e.preventDefault(); alert('Mobile Book clicked (replace with action).');
});
}
})();
/* ========================
OTP PANEL (demo logic) - start timer only on explicit Resend click
======================== */
(function () {
const contactInput = document.getElementById('contactInput');
const sendBtn = document.getElementById('sendBtn');
const sentBlock = document.getElementById('sent-block');
const inputBlock = document.getElementById('input-block');
const contactInputDisabled = document.getElementById('contactInputDisabled');
const sendBtnDisabled = document.getElementById('sendBtnDisabled');
const changeLink = document.getElementById('changeLink');
const resendLink = document.getElementById('resendLink');
const otpArea = document.getElementById('otpArea');
const otpInput = document.getElementById('otpInput');
const submitOtpBtn = document.getElementById('submitOtpBtn');
let resendTimer = null;
let secondsLeft = 0;
const RESEND_DELAY = 30; // seconds
function isEmail(text) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(text); }
function isPhone(text) {
const digits = text.replace(/\D/g, '');
return digits.length === 10; // EXACT 10 digits only
}
function isValidContact(text) { if (!text) return false; const trimmed = text.trim(); return isEmail(trimmed) || isPhone(trimmed); }
function setResendDisabled(state) {
if (!resendLink) return;
if (state) {
resendLink.classList.add('disabled');
resendLink.setAttribute('aria-disabled', 'true');
resendLink.style.pointerEvents = 'none';
} else {
resendLink.classList.remove('disabled');
resendLink.removeAttribute('aria-disabled');
resendLink.style.pointerEvents = '';
resendLink.textContent = 'Resend OTP';
}
}
function startResendCountdown(sec = RESEND_DELAY) {
if (!resendLink) return;
if (resendTimer) { clearInterval(resendTimer); resendTimer = null; }
secondsLeft = sec;
// lock the resend link and show countdown
setResendDisabled(true);
resendLink.textContent = `Resend OTP (${secondsLeft}s)`;
resendTimer = setInterval(() => {
secondsLeft--;
if (secondsLeft <= 0) {
clearInterval(resendTimer);
resendTimer = null;
setResendDisabled(false);
} else {
resendLink.textContent = `Resend OTP (${secondsLeft}s)`;
}
}, 1000);
}
function demoSendOtp(contact) {
$.ajax({
type: "GET",
url: '/victoriapublichall/otpapi/send-otp',
data: { 'userInput': contact },
success: function (response) {
if (response.status === "success") {
if (otpArea) { otpArea.style.display = 'block'; otpArea.setAttribute('aria-hidden', 'false'); }
if (otpInput) { otpInput.value = ''; setTimeout(() => otpInput.focus(), 120); } startResendCountdown();
} else if (response === "error") {
Swal.fire({
icon: 'warning',
title: 'Error',
text: 'Invalid Mobile Number or Email ID.',
});
return;
}
else {
Swal.fire({
icon: 'warning',
title: 'Error',
text: 'Error in OTP',
});
return;
}
},
error: function () {
alert("Error While Sending OTP.");
}
});
}
function showSentState(contact) {
inputBlock.style.display = 'none'; inputBlock.setAttribute('aria-hidden', 'true');
sentBlock.style.display = 'block'; sentBlock.setAttribute('aria-hidden', 'false');
if (contactInputDisabled) contactInputDisabled.value = contact;
if (sendBtnDisabled) sendBtnDisabled.disabled = true;
// ensure resend link is enabled for the user to click (timer will only start on explicit click)
setResendDisabled(false);
}
// Send OTP (initial send)
if (sendBtn) {
sendBtn.addEventListener('click', () => {
const raw = (contactInput && contactInput.value) ? contactInput.value.trim() : '';
// If only digits -> treat as phone
if (/^\d+$/.test(raw) && raw.length !== 10) {
// alert('Mobile number must be exactly 10 digits.');
Swal.fire({
icon: 'warning',
title: 'Validation Error',
text: 'Mobile number must be exactly 10 digits.',
});
contactInput.focus();
return;
}
if (!isValidContact(raw)) {
// alert('Enter a valid 10-digit phone number or email.');
Swal.fire({
icon: 'warning',
title: 'Validation Error',
text: 'Enter a valid 10-digit phone number or email.',
});
contactInput.focus();
return;
}
showSentState(raw);
// demo send OTP - do NOT start countdown here per requirement
demoSendOtp(raw);
document.getElementById('otpArea').scrollIntoView({
behavior: 'smooth',
block: 'start'
});
});
}
// Resend OTP - only starts countdown when user explicitly clicks Resend
if (resendLink) {
resendLink.addEventListener('click', (e) => {
e.preventDefault();
if (resendLink.classList.contains('disabled')) return; // safety
const to = (contactInputDisabled && contactInputDisabled.value) ? contactInputDisabled.value : (contactInput && contactInput.value ? contactInput.value : '');
if (!to) return;
if (/^\d+$/.test(to) && to.length !== 10) {
// alert('Mobile number must be exactly 10 digits.');
Swal.fire({
icon: 'warning',
title: 'Validation Error',
text: 'Mobile number must be exactly 10 digits.',
});
return;
}
if (!isValidContact(to)) {
// alert('Enter a valid 10-digit phone number or email.');
Swal.fire({
icon: 'warning',
title: 'Validation Error',
text: 'Enter a valid 10-digit phone number or email.',
});
return;
}
// send new OTP and start the countdown now
demoSendOtp(to);
startResendCountdown();
});
}
// Change link: go back to input state and reset
if (changeLink) {
changeLink.addEventListener('click', (e) => {
e.preventDefault();
if (resendTimer) { clearInterval(resendTimer); resendTimer = null; }
setResendDisabled(false);
sentBlock.style.display = 'none'; sentBlock.setAttribute('aria-hidden', 'true');
inputBlock.style.display = 'block'; inputBlock.setAttribute('aria-hidden', 'false');
if (contactInput) contactInput.focus();
if (otpArea) { otpArea.style.display = 'none'; otpArea.setAttribute('aria-hidden', 'true'); }
if (otpInput) otpInput.value = '';
if (contactInput) contactInput.value = '';
});
}
// Submit OTP
if (submitOtpBtn) {
submitOtpBtn.addEventListener('click', () => {
const otp = otpInput ? otpInput.value.trim() : '';
const userInput = (contactInputDisabled && contactInputDisabled.value) ? contactInputDisabled.value : (contactInput && contactInput.value ? contactInput.value : '');
if (!otp) {
Swal.fire({
icon: 'warning',
title: 'Missing OTP',
text: 'Please Enter OTP',
});
otpInput.focus(); return;
}
if (!userInput) {
Swal.fire({
icon: 'warning',
title: 'Missing Phone Number Or Email',
text: 'Please Enter Phone Number Or Email',
});
otpInput.focus(); return;
}
/* if(otp === currentOtp){
// demo success behavior: you can replace this with a redirect or server call
setTimeout(()=> {
alert('OTP verified (demo). You can now proceed to bookings.');
// optional: automatically switch back to input or navigate
// here we simulate closing the OTP flow (reset)
changeLink.click();
}, 700);
} else {
alert('Invalid OTP. Please try again.');
otpInput.focus();
} */
$.ajax({
type: "GET",
url: '/victoriapublichall/otpapi/verifyOTP',
data: { 'otp': otp, 'userInput': userInput },
success: function (response) {
console.log("API Response:", response);
// Success case
if (response.verifiedotp === "success") {
Swal.fire({
icon: 'success',
title: response.message,
//text: 'Click OK to continue',
showConfirmButton: false,
timer: 2000,
allowOutsideClick: false,
allowEscapeKey: false
}).then(() => {
console.log("Booking Details: ", response.regDetails);
let hasBooking = false;
let hasuser = response.userInput;
// regDetails is an array → check if contains items
if (response.regDetails && typeof response.regDetails === "object" && !Array.isArray(response.regDetails)) {
hasBooking = true;
console.log("step1");
}
// regDetails is a string (No booking found) → treat as NO booking
else if (typeof response.regDetails === "string") {
hasBooking = false;
console.log("step2");
}
sessionStorage.setItem("hasBooking", hasBooking);
sessionStorage.setItem("hasuser", hasuser);
sessionStorage.setItem("bookingdetails", JSON.stringify(response.regDetails));
//sessionStorage.setItem("bookingdetails", response.regDetails);
// Redirect with the correctly determined flag
//window.location.href = "/victoriapublichall/registerbook?active=" + hasBooking+"user="+hasuser;
window.location.href = "/victoriapublichall/registerbook";
$('#pageLoader').show();
});
}
// Error case returned as 200 OK (badRequest still JSON)
else if (response.verifiedotp === "error") {
Swal.fire({
icon: 'error',
title: 'Invalid OTP',
text: response.message
});
}
},
error: function (xhr) {
console.log("Error Response:", xhr);
let msg = "Something went wrong";
// Extract JSON error response
try {
let err = JSON.parse(xhr.responseText);
msg = err.message;
} catch (e) {
msg = "Server error. Try again.";
}
Swal.fire({
icon: 'error',
title: 'Error',
text: msg
});
}
});
});
}
// keyboard shortcuts
if (contactInput) contactInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') sendBtn.click(); });
if (otpInput) otpInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') submitOtpBtn.click(); });
// input smoothing: if user types digits only, limit to 10 digits (for phone)
if (contactInput) {
contactInput.addEventListener('input', () => {
if (/^\d*$/.test(contactInput.value)) {
contactInput.value = contactInput.value.replace(/\D/g, '').slice(0, 10);
}
});
}
})();
</script>
</body>
</html>