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.
1479 lines
43 KiB
1479 lines
43 KiB
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>VPH - Registration</title>
|
|
<link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet">
|
|
<!-- jQuery -->
|
|
<script src="https://code.jquery.com/jquery-3.6.4.min.js"
|
|
integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8="
|
|
crossorigin="anonymous"></script>
|
|
|
|
|
|
</head>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Montserrat';
|
|
background: linear-gradient(135deg, #eff6ff 0%, #f0fdfa 100%);
|
|
min-height: 100vh;
|
|
padding: 2rem 1rem;
|
|
}
|
|
|
|
.container {
|
|
max-width: 64rem;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.header {
|
|
text-align: center;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 2.5rem;
|
|
font-weight: bold;
|
|
color: #1f2937;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.header p {
|
|
color: #6b7280;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.progress-bar {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.progress-steps {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.step {
|
|
width: 2rem;
|
|
height: 2rem;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
background-color: #e5e7eb;
|
|
color: #6b7280;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.step.active {
|
|
background-color: #3b82f6;
|
|
color: white;
|
|
}
|
|
|
|
.step.completed {
|
|
background-color: #10b981;
|
|
color: white;
|
|
}
|
|
|
|
.step-line {
|
|
width: 3rem;
|
|
height: 0.25rem;
|
|
background-color: #e5e7eb;
|
|
margin: 0 0.5rem;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.step-line.completed {
|
|
background-color: #3b82f6;
|
|
}
|
|
|
|
.step-label {
|
|
text-align: center;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.step-label p {
|
|
font-size: 0.875rem;
|
|
color: #6b7280;
|
|
}
|
|
|
|
.form-container {
|
|
background: white;
|
|
border-radius: 0.5rem;
|
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
|
padding: 2rem;
|
|
}
|
|
|
|
.step-content {
|
|
display: none;
|
|
}
|
|
|
|
.step-content.active {
|
|
display: block;
|
|
}
|
|
|
|
.step-header {
|
|
text-align: center;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.step-header h2 {
|
|
font-size: 1.5rem;
|
|
font-weight: bold;
|
|
color: #1f2937;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.step-header p {
|
|
color: #6b7280;
|
|
}
|
|
|
|
.booking-type-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
gap: 1rem;
|
|
margin-bottom: -24px;
|
|
}
|
|
|
|
.booking-type-btn {
|
|
padding: 0.5rem;
|
|
border: 2px solid #e5e7eb;
|
|
border-radius: 0.5rem;
|
|
background: white;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
text-align: center;
|
|
}
|
|
|
|
.booking-type-btn:hover {
|
|
transform: scale(1.02);
|
|
border-color: #d1d5db;
|
|
}
|
|
|
|
.booking-type-btn.selected {
|
|
border-color: #3b82f6;
|
|
background-color: #eff6ff;
|
|
color: #1d4ed8;
|
|
}
|
|
|
|
.booking-type-btn .icon {
|
|
font-size: 2rem;
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.booking-type-btn h3 {
|
|
font-size: 1.125rem;
|
|
font-weight: 600;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.booking-type-btn p {
|
|
font-size: 0.875rem;
|
|
opacity: 0.75;
|
|
}
|
|
|
|
.form-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
gap: 1.5rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.form-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.form-group label {
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
color: #374151;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.form-input {
|
|
padding: 0.75rem;
|
|
border: 1px solid #d1d5db;
|
|
border-radius: 0.5rem;
|
|
font-size: 1rem;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.form-input:focus {
|
|
outline: none;
|
|
border-color: #3b82f6;
|
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
}
|
|
|
|
.help-text {
|
|
font-size: 0.875rem;
|
|
color: #6b7280;
|
|
margin-top: 0.25rem;
|
|
}
|
|
|
|
.time-slots-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 1rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.time-slot-btn {
|
|
padding: 1rem;
|
|
border: 2px solid #e5e7eb;
|
|
border-radius: 0.5rem;
|
|
background: white;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
text-align: center;
|
|
}
|
|
|
|
.time-slot-btn:hover {
|
|
transform: scale(1.02);
|
|
border-color: #d1d5db;
|
|
}
|
|
|
|
.time-slot-btn.selected {
|
|
border-color: #3b82f6;
|
|
background-color: #eff6ff;
|
|
color: #1d4ed8;
|
|
}
|
|
|
|
.time-slot-btn .icon {
|
|
font-size: 1.5rem;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.time-slot-btn h3 {
|
|
font-weight: 600;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.time-slot-btn p {
|
|
font-size: 0.875rem;
|
|
opacity: 0.75;
|
|
}
|
|
|
|
.slot-selection {
|
|
margin-top: 2rem;
|
|
}
|
|
|
|
.slot-header {
|
|
background-color: #eff6ff;
|
|
padding: 1rem;
|
|
border-radius: 0.5rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.slot-header h3 {
|
|
font-size: 1.125rem;
|
|
font-weight: 600;
|
|
color: #1e40af;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.selection-info {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-size: 0.875rem;
|
|
color: #1d4ed8;
|
|
}
|
|
|
|
.selection-count {
|
|
background-color: #dbeafe;
|
|
padding: 0.25rem 0.75rem;
|
|
border-radius: 9999px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.selection-complete {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
margin-top: 0.5rem;
|
|
color: #059669;
|
|
font-weight: 500;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.slots-container {
|
|
display: grid;
|
|
grid-template-columns: repeat(5, 1fr);
|
|
gap: 1rem;
|
|
margin: 0 auto;
|
|
padding-bottom: 0;
|
|
overflow: visible; /* no more horizontal scroll */
|
|
}
|
|
|
|
.slot-group {
|
|
background-color: #f9fafb;
|
|
padding: 1rem;
|
|
border-radius: 0.5rem;
|
|
flex: 1 1 140px; /* grow, shrink, base 140px */
|
|
min-width: 120px; /* never smaller than this */
|
|
max-width: 180px;
|
|
}
|
|
|
|
.slot-group h4 {
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
color: #374151;
|
|
margin-bottom: 0.75rem;
|
|
text-align: center;
|
|
padding-bottom: 0.25rem;
|
|
}
|
|
.slot-group h4::after {
|
|
width: 20px;
|
|
height: 2px;
|
|
}
|
|
.slot-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.slot-btn {
|
|
aspect-ratio: 1;
|
|
border: 2px solid #d1d5db;
|
|
border-radius: 0.5rem;
|
|
background: white;
|
|
cursor: pointer;
|
|
font-size: 0.75rem;
|
|
font-weight: 500;
|
|
transition: all 0.2s ease;
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 0.4rem 0;
|
|
}
|
|
|
|
.slot-btn:hover:not(:disabled) {
|
|
transform: scale(1.05);
|
|
border-color: #3b82f6;
|
|
background-color: #eff6ff;
|
|
}
|
|
|
|
.slot-btn.selected {
|
|
border-color: #10b981;
|
|
background-color: #d1fae5;
|
|
color: #065f46;
|
|
}
|
|
|
|
.slot-btn.occupied {
|
|
border-color: #fca5a5;
|
|
background-color: #fee2e2;
|
|
color: #dc2626;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.slot-btn.disabled {
|
|
border-color: #e5e7eb;
|
|
background-color: #f9fafb;
|
|
color: #9ca3af;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.slot-btn .check-icon {
|
|
position: absolute;
|
|
top: -0.25rem;
|
|
right: -0.25rem;
|
|
width: 0.75rem;
|
|
height: 0.75rem;
|
|
background-color: #10b981;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 0.5rem;
|
|
color: white;
|
|
}
|
|
|
|
.slot-legend {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 1.5rem;
|
|
margin-top: 1.5rem;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.legend-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.legend-box {
|
|
width: 1rem;
|
|
height: 1rem;
|
|
border: 2px solid;
|
|
border-radius: 0.25rem;
|
|
}
|
|
|
|
.legend-box.available {
|
|
background: white;
|
|
border-color: #d1d5db;
|
|
}
|
|
|
|
.legend-box.selected {
|
|
background: #d1fae5;
|
|
border-color: #10b981;
|
|
}
|
|
|
|
.legend-box.occupied {
|
|
background: #fee2e2;
|
|
border-color: #fca5a5;
|
|
}
|
|
|
|
.legend-box.disabled {
|
|
background: #f9fafb;
|
|
border-color: #e5e7eb;
|
|
}
|
|
|
|
.confirmation-header {
|
|
text-align: center;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.success-icon {
|
|
font-size: 4rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.confirmation-header h2 {
|
|
font-size: 1.5rem;
|
|
font-weight: bold;
|
|
color: #1f2937;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.confirmation-header p {
|
|
color: #6b7280;
|
|
}
|
|
|
|
.booking-summary {
|
|
background-color: #f9fafb;
|
|
padding: 1.5rem;
|
|
border-radius: 0.5rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.booking-summary h3 {
|
|
font-weight: 600;
|
|
font-size: 1.125rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.summary-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 0.75rem 0;
|
|
border-bottom: 1px solid #e5e7eb;
|
|
}
|
|
|
|
.summary-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.summary-item span:first-child {
|
|
color: #6b7280;
|
|
}
|
|
|
|
.summary-item span:last-child {
|
|
font-weight: 500;
|
|
text-transform: capitalize;
|
|
}
|
|
|
|
.important-note {
|
|
background-color: #eff6ff;
|
|
padding: 1rem;
|
|
border-radius: 0.5rem;
|
|
font-size: 0.875rem;
|
|
color: #1e40af;
|
|
}
|
|
|
|
.navigation-buttons {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-top: 2rem;
|
|
}
|
|
|
|
.btn {
|
|
padding: 0.75rem 1.5rem;
|
|
border-radius: 0.5rem;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
border: none;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.btn:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.btn-primary {
|
|
background-color: #3b82f6;
|
|
color: white;
|
|
}
|
|
|
|
.btn-primary:hover:not(:disabled) {
|
|
background-color: #2563eb;
|
|
transform: scale(1.02);
|
|
}
|
|
|
|
.btn-secondary {
|
|
background-color: #e5e7eb;
|
|
color: #374151;
|
|
}
|
|
|
|
.btn-secondary:hover:not(:disabled) {
|
|
background-color: #d1d5db;
|
|
}
|
|
|
|
.error-message {
|
|
color: #dc2626;
|
|
font-size: 0.875rem;
|
|
margin-top: 1.5rem;
|
|
display: none;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.error-message.show {
|
|
display: flex;
|
|
}
|
|
|
|
.error-message::before {
|
|
content: "⚠️";
|
|
font-size: 1rem;
|
|
}
|
|
|
|
/* batch-card status colors */
|
|
.time-slot-btn.status-ok {
|
|
border-color: #10b981; /* green */
|
|
background: #ecfdf5;
|
|
}
|
|
|
|
.time-slot-btn.status-low {
|
|
border-color: #f59e0b; /* amber */
|
|
background: #fffbeb;
|
|
}
|
|
|
|
.time-slot-btn.status-critical {
|
|
border-color: #ef4444; /* red */
|
|
background: #fee2e2;
|
|
}
|
|
|
|
.time-slot-btn.status-full {
|
|
border-color: #9ca3af; /* gray */
|
|
background: #f3f4f6;
|
|
color: #9ca3af;
|
|
}
|
|
|
|
.time-slot-btn.status-full .available-count { display: none; }
|
|
.time-slot-btn:disabled { cursor: not-allowed; opacity: .65; }
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
.container {
|
|
padding: 1rem;
|
|
}
|
|
.booking-type-btn h3 {
|
|
font-size: 1.125rem;
|
|
font-weight: 600;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
.slots-container {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, 1fr);
|
|
gap: 1rem;
|
|
margin: 0 auto;
|
|
padding-bottom: 0;
|
|
overflow: visible;
|
|
}
|
|
.form-container {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.booking-type-grid,
|
|
.time-slots-grid {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
height: 225px;
|
|
}
|
|
|
|
.form-grid {
|
|
display: grid !important;
|
|
grid-template-columns: 1fr !important;
|
|
gap: 1rem !important;
|
|
}
|
|
|
|
.form-group.full-width {
|
|
grid-column: auto !important;
|
|
}
|
|
|
|
.slot-legend {
|
|
flex-wrap: wrap;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.navigation-buttons {
|
|
flex-direction: row !important;
|
|
justify-content: space-between;
|
|
gap: 0;
|
|
margin-top: 5rem;
|
|
}
|
|
|
|
.navigation-buttons .btn {
|
|
flex: 1; /* let them grow equally */
|
|
max-width: 30%; /* so there’s a little gutter */
|
|
}
|
|
.slot-selection {
|
|
margin-top: 6rem;
|
|
}
|
|
|
|
}
|
|
</style>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>Victoria Public Hall <br> Experiential Museum - Registration</h1>
|
|
</div>
|
|
|
|
<div class="progress-bar" id="progressBar">
|
|
<div class="progress-steps">
|
|
<div class="step active" data-step="1">1</div>
|
|
<div class="step-line"></div>
|
|
<div class="step" data-step="2">2</div>
|
|
<div class="step-line"></div>
|
|
<div class="step" data-step="3">3</div>
|
|
<div class="step-line"></div>
|
|
<div class="step" data-step="4">4</div>
|
|
</div>
|
|
<div class="step-label">
|
|
<p>Step <span id="currentStepNum">1</span> of 4: <span id="stepName">Booking Type</span></p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-container">
|
|
<!-- Step 1: Booking Type Selection -->
|
|
<div class="step-content active" id="step1">
|
|
<div class="step-header">
|
|
<h2>Book your Visit to VPH</h2>
|
|
</div>
|
|
|
|
<div class="booking-type-grid">
|
|
<button class="booking-type-btn" data-type="individual">
|
|
<div class="icon">👤</div>
|
|
<h3>Individual Booking</h3>
|
|
<p>Max 10</p>
|
|
</button>
|
|
|
|
<button class="booking-type-btn" data-type="group">
|
|
<div class="icon">👥</div>
|
|
<h3>Bulk Booking</h3>
|
|
<p>Max 50</p>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="error-message" id="typeError"></div>
|
|
</div>
|
|
|
|
<!-- Step 2: Visit Details -->
|
|
<div class="step-content" id="step2">
|
|
<div class="step-header">
|
|
<h2>Visit Details</h2>
|
|
</div>
|
|
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label>📅 Select Date</label>
|
|
<input type="date" id="visitDate" class="form-input">
|
|
<div class="error-message" id="dateError"></div>
|
|
</div>
|
|
|
|
<div class="form-group" id="membersGroup">
|
|
<label>👥 Number of Persons</label>
|
|
<input type="number" id="memberCount" class="form-input" min="1" max="10" step="1" oninput="this.value = this.value.replace(/\D+/g, '')"/>
|
|
<p class="help-text">Maximum 50 members allowed</p>
|
|
<div class="error-message" id="membersError"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 3: Time Slot Selection -->
|
|
<div class="step-content" id="step3">
|
|
<div class="step-header">
|
|
<h2>Choose Your batch and number of tickets</h2>
|
|
</div>
|
|
|
|
<div class="time-slots-grid">
|
|
<button class="time-slot-btn" data-batch="1">
|
|
<div class="icon">🕐</div>
|
|
<h3>8:00-10:00 AM</h3>
|
|
<p><span class="available-count">100</span> available slots</p>
|
|
</button>
|
|
<button class="time-slot-btn" data-batch="2">
|
|
<div class="icon">🕐</div>
|
|
<h3>10:30 AM-12:30 PM</h3>
|
|
<p><span class="available-count">100</span> available slots</p>
|
|
</button>
|
|
<button class="time-slot-btn" data-batch="3">
|
|
<div class="icon">🕐</div>
|
|
<h3>1:00-3:00 PM</h3>
|
|
<p><span class="available-count">100</span> available slots</p>
|
|
</button>
|
|
<button class="time-slot-btn" data-batch="4">
|
|
<div class="icon">🕐</div>
|
|
<h3>3:30-5:30 PM</h3>
|
|
<p><span class="available-count">100</span> available slots</p>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="slot-selection" id="slotSelection" style="display: none;">
|
|
<div class="slot-header">
|
|
<!--
|
|
<h3>Select Slots for <span id="selectedTimeSlot"></span></h3>
|
|
-->
|
|
<h3>Select group-wise to experience Chennaiyin Cinema and Chennaiyin Olichithram</h3>
|
|
<div class="selection-info">
|
|
<span id="selectionText">Select 1 slot for individual booking</span>
|
|
<span class="selection-count">
|
|
<span id="selectedCount">0</span> / <span id="requiredCount">1</span> selected
|
|
</span>
|
|
</div>
|
|
<div class="selection-complete" id="selectionComplete" style="display: none;">
|
|
✅ Selection complete!
|
|
</div>
|
|
</div>
|
|
<div class="slot-legend">
|
|
<div class="legend-item">
|
|
<div class="legend-box available"></div>
|
|
<span>Available</span>
|
|
</div>
|
|
<div class="legend-item">
|
|
<div class="legend-box selected"></div>
|
|
<span>Selected</span>
|
|
</div>
|
|
<div class="legend-item">
|
|
<div class="legend-box occupied"></div>
|
|
<span>Occupied</span>
|
|
</div>
|
|
<div class="legend-item">
|
|
<div class="legend-box disabled"></div>
|
|
<span>Disabled</span>
|
|
</div>
|
|
</div>
|
|
<br>
|
|
<div class="slots-container" id="slotsContainer">
|
|
|
|
<!-- Slots will be generated by JavaScript -->
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="error-message" id="batchError"></div>
|
|
<div class="error-message" id="slotError"></div>
|
|
</div>
|
|
|
|
<!-- Step 4: Personal Details -->
|
|
<div class="step-content" id="step4">
|
|
<div class="step-header">
|
|
<h2>Please provide your details</h2>
|
|
</div>
|
|
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label>Name</label>
|
|
<input type="text" id="fullName" class="form-input" placeholder="Enter your full name">
|
|
<div class="error-message" id="nameError"></div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>Phone Number</label>
|
|
<input type="tel" id="phoneNumber" class="form-input" placeholder="Enter 10-digit phone number" maxlength="10">
|
|
<div class="error-message" id="phoneError"></div>
|
|
</div>
|
|
|
|
<div class="form-group full-width">
|
|
<label>Address</label>
|
|
<textarea id="address" class="form-input" placeholder="Enter your complete address" rows="3"></textarea>
|
|
<div class="error-message" id="addressError"></div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>Aadhaar Number</label>
|
|
<input type="text" id="aadharNumber" class="form-input" placeholder="Enter 12-digit Aadhaar number" maxlength="12">
|
|
<div class="error-message" id="aadharError"></div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>Age</label>
|
|
<input type="number" id="age" class="form-input" placeholder="Enter your age" min="1" max="120">
|
|
<div class="error-message" id="ageError"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="navigation-buttons" id="navigationButtons">
|
|
<button class="btn btn-secondary" id="backBtn" disabled>Back</button>
|
|
<button class="btn btn-primary" id="nextBtn">Next</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<script>
|
|
|
|
// --- Batch start/end times (in minutes from midnight) ---
|
|
const SLOT_WINDOWS = {
|
|
'1': { startM: 8 * 60, endM: 10 * 60 }, // 8:00–10:00
|
|
'2': { startM: 10 * 60 + 30, endM: 12 * 60 + 30 }, // 10:30–12:30
|
|
'3': { startM: 13 * 60, endM: 15 * 60 }, // 1:00–3:00
|
|
'4': { startM: 15 * 60 + 30, endM: 17 * 60 + 30 } // 3:30–5:30
|
|
};
|
|
|
|
function todayYMD() {
|
|
return new Date().toISOString().split('T')[0];
|
|
}
|
|
|
|
// Return true if the batch should be time-blocked for the current selection
|
|
function isSlotTimeBlocked(batchId) {
|
|
if (!bookingData.date) return false;
|
|
|
|
const chosen = bookingData.date;
|
|
const today = todayYMD();
|
|
|
|
if (chosen < today) return true; // past date ⇒ block all
|
|
if (chosen > today) return false; // future date ⇒ allow all
|
|
|
|
// Same day: block if the batch has started already
|
|
const nowMins = new Date().getHours() * 60 + new Date().getMinutes();
|
|
const win = SLOT_WINDOWS[batchId];
|
|
if (!win) return false;
|
|
|
|
// Block as soon as start time has passed
|
|
return nowMins >= win.startM;
|
|
}
|
|
|
|
// Apply/refresh disabled state on the four batch buttons
|
|
function applyTemporalDisabling() {
|
|
document.querySelectorAll('.time-slot-btn').forEach(btn => {
|
|
const b = btn.dataset.batch;
|
|
const blocked = isSlotTimeBlocked(b);
|
|
|
|
// keep the visual "full" gray when blocked by time
|
|
btn.classList.toggle('status-full', blocked);
|
|
btn.disabled = blocked;
|
|
|
|
// If currently selected and it just became blocked, clear selection & UI
|
|
if (blocked && bookingData.batch === b) {
|
|
bookingData.batch = '';
|
|
bookingData.subSlot = [];
|
|
document.getElementById('slotSelection').style.display = 'none';
|
|
clearError('batchError');
|
|
}
|
|
});
|
|
}
|
|
|
|
const GROUP_SIZE = 10;
|
|
|
|
// Booking form state
|
|
let bookingData = {
|
|
type: '',
|
|
date: '',
|
|
members: 1,
|
|
batch: '',
|
|
subSlot: [],
|
|
personalDetails: {
|
|
fullName: '',
|
|
phone: '',
|
|
address: '',
|
|
aadhar: '',
|
|
age: ''
|
|
}
|
|
};
|
|
|
|
let currentStep = 1;
|
|
const totalSteps = 4;
|
|
|
|
// Time slots data
|
|
const timeSlots = {
|
|
'1': { time: '8:00-10:00 AM', capacity: 0, slots: [] },
|
|
'2': { time: '10:30 AM-12:30 PM', capacity: 0, slots: [] },
|
|
'3': { time: '1:00-3:00 PM', capacity: 0, slots: [] },
|
|
'4': { time: '3:30-5:30 PM', capacity: 0, slots: [] }
|
|
};
|
|
|
|
// --- Helper to build slots array ---
|
|
function buildSlots(capacity, occupiedSet) {
|
|
const arr = [];
|
|
for (let i = 1; i <= capacity; i++) {
|
|
arr.push({ id: i, occupied: occupiedSet.has(i) });
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
function fetchOccupiedSeats(slotId) {
|
|
return $.ajax({
|
|
url: '/victoriapublichall/api/occupied-seats',
|
|
method: 'GET',
|
|
dataType: 'json',
|
|
data: { bookingDate: bookingData.date, slotId }
|
|
});
|
|
}
|
|
|
|
function setBatchVisual(btn, available, capacity) {
|
|
const pct = capacity ? (available / capacity) : 0;
|
|
|
|
btn.classList.remove('status-ok','status-low','status-critical','status-full');
|
|
btn.disabled = false;
|
|
|
|
if (available <= 0) {
|
|
btn.classList.add('status-full');
|
|
btn.disabled = true; // full: cannot pick
|
|
} else if (pct <= 0.10 || available <= 5) {
|
|
btn.classList.add('status-critical'); // red: almost full
|
|
} else if (pct <= 0.25) {
|
|
btn.classList.add('status-low'); // amber: getting low
|
|
} else {
|
|
btn.classList.add('status-ok'); // greenish: plenty
|
|
}
|
|
|
|
const countEl = btn.querySelector('.available-count');
|
|
if (countEl) countEl.textContent = available;
|
|
}
|
|
|
|
|
|
// Refresh all batches for a date
|
|
function refreshAllAvailability() {
|
|
if (!bookingData.date) return;
|
|
document.querySelectorAll('.time-slot-btn').forEach(btn => {
|
|
const b = btn.dataset.batch;
|
|
fetchOccupiedSeats(b).done(data => {
|
|
const c = btn.querySelector('.available-count');
|
|
if (c) c.textContent = data.availableCount;
|
|
timeSlots[b].capacity = data.capacity;
|
|
timeSlots[b].slots = buildSlots(data.capacity, new Set(data.occupiedSeats || []));
|
|
setBatchVisual(btn, data.availableCount, data.capacity);
|
|
|
|
if (bookingData.batch === b) generateSlotGrid();
|
|
|
|
applyTemporalDisabling();
|
|
});
|
|
});
|
|
}
|
|
|
|
// Initialize form
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
initializeEventListeners();
|
|
updateProgressBar();
|
|
updateNavigationButtons();
|
|
const today = new Date().toISOString().split('T')[0];
|
|
document.getElementById('visitDate').min = today;
|
|
});
|
|
|
|
function initializeEventListeners() {
|
|
document.querySelectorAll('.booking-type-btn').forEach(btn => {
|
|
btn.addEventListener('click', function() {
|
|
selectBookingType(this.dataset.type);
|
|
clearError('typeError');
|
|
});
|
|
});
|
|
document.querySelectorAll('.time-slot-btn').forEach(btn => {
|
|
btn.addEventListener('click', function() {
|
|
selectTimeSlot(this.dataset.batch); });
|
|
});
|
|
document.getElementById('visitDate').addEventListener('change', e => {
|
|
bookingData.date = e.target.value;
|
|
clearError('dateError');
|
|
refreshAllAvailability();
|
|
applyTemporalDisabling();
|
|
});
|
|
document.getElementById('memberCount').addEventListener('input', e => {
|
|
let val = parseInt(e.target.value, 10) || 0; // parse or 0 if empty/non-numeric
|
|
|
|
if (val < 1) {
|
|
val = 1; // force minimum
|
|
showError('membersError', 'Number of persons must be at least 1');
|
|
} else {
|
|
clearError('membersError');
|
|
}
|
|
|
|
// also respect max based on booking type
|
|
const max = bookingData.type === 'individual' ? 10 : 50;
|
|
if (val > max) {
|
|
val = max;
|
|
showError('membersError', `Maximum allowed is ${max} persons`);
|
|
}
|
|
|
|
e.target.value = val;
|
|
bookingData.members = val;
|
|
|
|
// clear previously selected seats if necessary
|
|
if (bookingData.subSlot.length > val) {
|
|
bookingData.subSlot = [];
|
|
generateSlotGrid();
|
|
}
|
|
|
|
updateSlotSelection();
|
|
});
|
|
|
|
document.getElementById('fullName').addEventListener('input', e => bookingData.personalDetails.fullName = e.target.value);
|
|
document.getElementById('phoneNumber').addEventListener('input', e => {
|
|
e.target.value = e.target.value.replace(/\D/g, '');
|
|
bookingData.personalDetails.phone = e.target.value;
|
|
});
|
|
document.getElementById('address').addEventListener('input', e => bookingData.personalDetails.address = e.target.value);
|
|
document.getElementById('aadharNumber').addEventListener('input', e => {
|
|
e.target.value = e.target.value.replace(/\D/g, '');
|
|
bookingData.personalDetails.aadhar = e.target.value;
|
|
});
|
|
document.getElementById('age').addEventListener('input', e => bookingData.personalDetails.age = e.target.value);
|
|
document.getElementById('backBtn').addEventListener('click', goBack);
|
|
document.getElementById('nextBtn').addEventListener('click', goNext);
|
|
}
|
|
|
|
function selectBookingType(type) {
|
|
bookingData.type = type;
|
|
document.querySelectorAll('.booking-type-btn').forEach(b => b.classList.toggle('selected', b.dataset.type === type));
|
|
const countInput = document.getElementById('memberCount');
|
|
const helpText = document.querySelector('#membersGroup .help-text');
|
|
if (type === 'individual') {
|
|
countInput.max = 10;
|
|
helpText.textContent = 'Max 10 members for individual booking';
|
|
} else {
|
|
countInput.max = 50;
|
|
helpText.textContent = 'Max 50 members for bulk booking';
|
|
}
|
|
countInput.value = 1;
|
|
bookingData.members = 1;
|
|
clearError('membersError');
|
|
updateSlotSelection();
|
|
}
|
|
|
|
function selectTimeSlot(batch) {
|
|
if (!bookingData.date) {
|
|
showError('dateError', 'Please select a visit date first');
|
|
return;
|
|
}
|
|
|
|
if (isSlotTimeBlocked(batch)) {
|
|
showError('batchError', 'This batch is no longer available for the selected date/time.');
|
|
return;
|
|
}
|
|
|
|
bookingData.batch = batch;
|
|
bookingData.subSlot = [];
|
|
document.querySelectorAll('.time-slot-btn').forEach(b => b.classList.remove('selected'));
|
|
document.querySelector(`.time-slot-btn[data-batch="${batch}"]`).classList.add('selected');
|
|
document.getElementById('slotSelection').style.display = 'block';
|
|
updateSlotSelection();
|
|
clearError('batchError');
|
|
|
|
fetchOccupiedSeats(batch)
|
|
.done(data => {
|
|
const countEl = document.querySelector(`.time-slot-btn[data-batch="${batch}"] .available-count`);
|
|
if (countEl) countEl.textContent = data.availableCount;
|
|
const occupied = new Set(data.occupiedSeats || []);
|
|
timeSlots[batch].capacity = data.capacity;
|
|
timeSlots[batch].slots = buildSlots(data.capacity, occupied);
|
|
|
|
// update only this card’s UI + count
|
|
const btn = document.querySelector(`.time-slot-btn[data-batch="${batch}"]`);
|
|
if (btn) setBatchVisual(btn, data.availableCount, data.capacity);
|
|
|
|
generateSlotGrid();
|
|
})
|
|
.fail(xhr => {
|
|
console.error(xhr);
|
|
showError('batchError', xhr.responseJSON?.message || 'Unable to load availability');
|
|
});
|
|
}
|
|
|
|
function updateSlotSelection() {
|
|
const requiredSlots = bookingData.members;
|
|
document.getElementById('requiredCount').textContent = requiredSlots;
|
|
document.getElementById('selectedCount').textContent = bookingData.subSlot.length;
|
|
document.getElementById('selectionText').textContent = `Select ${requiredSlots} slot${requiredSlots>1?'s':''}`;
|
|
document.getElementById('selectionComplete').style.display =
|
|
(bookingData.subSlot.length === requiredSlots) ? 'block' : 'none';
|
|
}
|
|
|
|
function generateSlotGrid() {
|
|
const container = document.getElementById('slotsContainer');
|
|
container.innerHTML = '';
|
|
if (!bookingData.batch) return;
|
|
|
|
const slots = timeSlots[bookingData.batch].slots;
|
|
const capacity = timeSlots[bookingData.batch].capacity;
|
|
const groupCount = Math.ceil(capacity / GROUP_SIZE);
|
|
|
|
for (let gi = 0; gi < groupCount; gi++) {
|
|
const start = gi * GROUP_SIZE;
|
|
const end = Math.min(start + GROUP_SIZE, capacity);
|
|
const group = slots.slice(start, end);
|
|
const availableCount = group.filter(s => !s.occupied).length;
|
|
|
|
const div = document.createElement('div');
|
|
div.className = 'slot-group';
|
|
|
|
const h4 = document.createElement('h4');
|
|
h4.textContent = `Group ${gi + 1}: Slots ${start + 1}-${end} (Available: ${availableCount})`;
|
|
div.appendChild(h4);
|
|
|
|
const chkLabel = document.createElement('label');
|
|
chkLabel.style = 'display:flex;justify-content:center;font-size:.75rem;margin:.5rem 0';
|
|
const chk = document.createElement('input');
|
|
chk.type = 'checkbox';
|
|
chk.checked = group.filter(s => !s.occupied).every(s => bookingData.subSlot.includes(s.id));
|
|
chk.addEventListener('change', () => handleGroupSelect(gi, chk.checked));
|
|
chkLabel.append(chk, document.createTextNode(' Select All'));
|
|
div.appendChild(chkLabel);
|
|
|
|
const grid = document.createElement('div');
|
|
grid.className = 'slot-grid';
|
|
|
|
group.forEach(s => {
|
|
const btn = document.createElement('button');
|
|
btn.className = 'slot-btn';
|
|
btn.textContent = s.id;
|
|
if (s.occupied) {
|
|
btn.classList.add('occupied');
|
|
btn.disabled = true;
|
|
} else if (bookingData.subSlot.includes(s.id)) {
|
|
btn.classList.add('selected');
|
|
const icon = document.createElement('div');
|
|
icon.className = 'check-icon';
|
|
icon.textContent = '✓';
|
|
btn.appendChild(icon);
|
|
} else if (bookingData.subSlot.length >= bookingData.members) {
|
|
btn.classList.add('disabled');
|
|
btn.disabled = true;
|
|
}
|
|
btn.addEventListener('click', () => selectSlot(s.id));
|
|
grid.appendChild(btn);
|
|
});
|
|
div.appendChild(grid);
|
|
container.appendChild(div);
|
|
}
|
|
}
|
|
|
|
function handleGroupSelect(gi, checked) {
|
|
const slots = timeSlots[bookingData.batch].slots;
|
|
const start = gi * GROUP_SIZE;
|
|
const end = Math.min(start + GROUP_SIZE, timeSlots[bookingData.batch].capacity);
|
|
const group = slots.slice(start, end);
|
|
const cap = bookingData.members;
|
|
if (checked) {
|
|
for (let s of group) {
|
|
if (!s.occupied && bookingData.subSlot.length < cap && !bookingData.subSlot.includes(s.id)) {
|
|
bookingData.subSlot.push(s.id);
|
|
}
|
|
}
|
|
} else {
|
|
group.forEach(s => {
|
|
const idx = bookingData.subSlot.indexOf(s.id);
|
|
if (idx > -1) bookingData.subSlot.splice(idx, 1);
|
|
});
|
|
}
|
|
updateSlotSelection();
|
|
generateSlotGrid();
|
|
}
|
|
|
|
function selectSlot(id) {
|
|
const cap = bookingData.members;
|
|
const idx = bookingData.subSlot.indexOf(id);
|
|
if (idx > -1) bookingData.subSlot.splice(idx, 1);
|
|
else if (bookingData.subSlot.length < cap) bookingData.subSlot.push(id);
|
|
updateSlotSelection();
|
|
generateSlotGrid();
|
|
clearError('slotError');
|
|
}
|
|
|
|
function validateStep(step) {
|
|
clearAllErrors();
|
|
let ok = true;
|
|
if (step===1 && !bookingData.type) {
|
|
showError('typeError','Please select a booking type');
|
|
ok = false;
|
|
}
|
|
if (step===2) {
|
|
if (!bookingData.date) {
|
|
showError('dateError','Please select a visit date');
|
|
ok = false;
|
|
}
|
|
if (bookingData.members < 1) {
|
|
showError('membersError','Number of persons must be at least 1');
|
|
ok = false;
|
|
} else if (bookingData.members > (bookingData.type==='individual'?10:50)) {
|
|
showError('membersError','Number of persons exceeds maximum allowed');
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
if (step===3) {
|
|
if (!bookingData.batch) {
|
|
showError('batchError','Please select a time batch');
|
|
ok = false;
|
|
}
|
|
if (bookingData.subSlot.length !== bookingData.members) {
|
|
showError('slotError',`Please select exactly ${bookingData.members} slot(s)`);
|
|
ok = false;
|
|
}
|
|
}
|
|
if (step===4) {
|
|
if (!bookingData.personalDetails.fullName.trim()) { showError('nameError','Full name is required'); ok=false; }
|
|
if (!bookingData.personalDetails.phone.match(/^[6-9]\d{9}$/)) { showError('phoneError','Enter valid 10-digit phone'); ok=false; }
|
|
if (!bookingData.personalDetails.address.trim()) { showError('addressError','Address is required'); ok=false; }
|
|
if (!bookingData.personalDetails.aadhar.match(/^\d{12}$/)) { showError('aadharError','Aadhar must be 12 digits'); ok=false; }
|
|
if (!bookingData.personalDetails.age.match(/^\d+$/) || parseInt(bookingData.personalDetails.age)<=0) {
|
|
showError('ageError','Enter a valid age'); ok=false;
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
// Attach listeners after DOM loads
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
// Name
|
|
document.getElementById("fullName").addEventListener("input", (e) => {
|
|
if (e.target.value.trim()) {
|
|
clearError("nameError");
|
|
bookingData.personalDetails.fullName = e.target.value;
|
|
}
|
|
});
|
|
|
|
// Phone
|
|
document.getElementById("phoneNumber").addEventListener("input", (e) => {
|
|
const val = e.target.value.trim();
|
|
bookingData.personalDetails.phone = val;
|
|
if (/^[6-9]\d{9}$/.test(val)) {
|
|
clearError("phoneError");
|
|
}
|
|
});
|
|
|
|
// Address
|
|
document.getElementById("address").addEventListener("input", (e) => {
|
|
const val = e.target.value.trim();
|
|
bookingData.personalDetails.address = val;
|
|
if (val.length > 0) {
|
|
clearError("addressError");
|
|
}
|
|
});
|
|
|
|
// Aadhaar
|
|
document.getElementById("aadharNumber").addEventListener("input", (e) => {
|
|
const val = e.target.value.trim();
|
|
bookingData.personalDetails.aadhar = val;
|
|
if (/^\d{12}$/.test(val)) {
|
|
clearError("aadharError");
|
|
}
|
|
});
|
|
|
|
// Age
|
|
document.getElementById("age").addEventListener("input", (e) => {
|
|
const val = e.target.value.trim();
|
|
bookingData.personalDetails.age = val;
|
|
if (/^\d+$/.test(val) && parseInt(val) > 0) {
|
|
clearError("ageError");
|
|
}
|
|
});
|
|
});
|
|
|
|
function showError(elementId, message) {
|
|
const errorElement = document.getElementById(elementId);
|
|
errorElement.textContent = message;
|
|
errorElement.classList.add('show');
|
|
}
|
|
|
|
function clearError(elementId) {
|
|
const errorElement = document.getElementById(elementId);
|
|
errorElement.classList.remove('show');
|
|
}
|
|
|
|
function clearAllErrors() {
|
|
document.querySelectorAll('.error-message').forEach(error => {
|
|
error.classList.remove('show');
|
|
});
|
|
}
|
|
|
|
function goNext() {
|
|
if (validateStep(currentStep)) {
|
|
if (currentStep < totalSteps) {
|
|
currentStep++;
|
|
updateStep();
|
|
} else {
|
|
// Submit form
|
|
submitForm();
|
|
}
|
|
}
|
|
}
|
|
|
|
function goBack() {
|
|
if (currentStep > 1) {
|
|
currentStep--;
|
|
updateStep();
|
|
}
|
|
}
|
|
|
|
function updateStep() {
|
|
// Hide all steps
|
|
document.querySelectorAll('.step-content').forEach(step => {
|
|
step.classList.remove('active');
|
|
});
|
|
|
|
// Show current step
|
|
document
|
|
.getElementById(`step${currentStep}`)
|
|
.classList.add('active');
|
|
|
|
updateProgressBar();
|
|
updateNavigationButtons();
|
|
clearAllErrors();
|
|
}
|
|
|
|
function updateProgressBar() {
|
|
// Update step indicators
|
|
document.querySelectorAll('.step').forEach((step, index) => {
|
|
const stepNum = index + 1;
|
|
step.classList.remove('active', 'completed');
|
|
|
|
if (stepNum < currentStep) {
|
|
step.classList.add('completed');
|
|
} else if (stepNum === currentStep) {
|
|
step.classList.add('active');
|
|
}
|
|
});
|
|
|
|
// Update step lines
|
|
document.querySelectorAll('.step-line').forEach((line, index) => {
|
|
line.classList.remove('completed');
|
|
if (index + 1 < currentStep) {
|
|
line.classList.add('completed');
|
|
}
|
|
});
|
|
|
|
// Update step label
|
|
const stepNames = ['Booking Type', 'Visit Details', 'Time Selection', 'Personal Details'];
|
|
document.getElementById('currentStepNum').textContent = currentStep;
|
|
document.getElementById('stepName').textContent = stepNames[currentStep - 1];
|
|
|
|
// Hide progress bar on confirmation step
|
|
const progressBar = document.getElementById('progressBar');
|
|
if (currentStep === 5) {
|
|
progressBar.style.display = 'none';
|
|
} else {
|
|
progressBar.style.display = 'block';
|
|
}
|
|
}
|
|
|
|
function updateNavigationButtons() {
|
|
const backBtn = document.getElementById('backBtn');
|
|
const nextBtn = document.getElementById('nextBtn');
|
|
const navigationButtons = document.getElementById('navigationButtons');
|
|
|
|
if (currentStep === 5) {
|
|
// Hide navigation buttons on confirmation step
|
|
navigationButtons.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
navigationButtons.style.display = 'flex';
|
|
|
|
// Update back button
|
|
backBtn.disabled = currentStep === 1;
|
|
|
|
// Update next button text
|
|
if (currentStep === totalSteps) {
|
|
nextBtn.textContent = 'Submit Booking';
|
|
} else {
|
|
nextBtn.textContent = 'Next';
|
|
}
|
|
}
|
|
|
|
function submitForm() {
|
|
if (!validateStep(4)) return;
|
|
|
|
const payload = {
|
|
name: bookingData.personalDetails.fullName,
|
|
mobileno: bookingData.personalDetails.phone,
|
|
address: bookingData.personalDetails.address,
|
|
aadhaarNo: bookingData.personalDetails.aadhar,
|
|
age: parseInt(bookingData.personalDetails.age || '0', 10),
|
|
bookingType: bookingData.type,
|
|
noofpersons: bookingData.members,
|
|
bookingDate: bookingData.date,
|
|
slotId: parseInt(bookingData.batch, 10),
|
|
selectedSlots: bookingData.subSlot
|
|
};
|
|
|
|
$.ajax({
|
|
url: '/victoriapublichall/api/savebooking',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify(payload),
|
|
success: function(summary) {
|
|
window.location.href = `/victoriapublichall/booking-summary?refId=${encodeURIComponent(summary.ref_id)}`;
|
|
},
|
|
|
|
error: function(xhr) {
|
|
console.error('Error:', xhr.responseText);
|
|
alert(xhr.responseJSON?.message || 'Error saving booking');
|
|
}
|
|
});
|
|
}
|
|
|
|
function displayBookingSummary() {
|
|
// Basic booking info
|
|
document.getElementById('summaryMembers').textContent = bookingData.members;
|
|
document.getElementById('summaryDate').textContent = bookingData.date;
|
|
document.getElementById('summaryTimeSlot').textContent =
|
|
bookingData.confirmation?.slotTiming || timeSlots[bookingData.batch].time;
|
|
|
|
// Group with count
|
|
const byGroup = {};
|
|
bookingData.subSlot.forEach(id => {
|
|
const g = Math.ceil(id / 10); // group number
|
|
byGroup[g] = (byGroup[g] || 0) + 1; // increment count
|
|
});
|
|
|
|
const lines = Object.keys(byGroup)
|
|
.sort((a, b) => a - b)
|
|
.map(g => `G${g}: ${byGroup[g]}`)
|
|
.join(', ');
|
|
|
|
document.getElementById('summarySlots').textContent = lines;
|
|
|
|
// Personal details
|
|
document.getElementById('summaryName').textContent = bookingData.personalDetails.fullName;
|
|
document.getElementById('summaryPhone').textContent = bookingData.personalDetails.phone;
|
|
}
|
|
|
|
// DOWNLOAD handler
|
|
document.getElementById('downloadBtn').addEventListener('click', () => {
|
|
const element = document.getElementById('bookingSummary');
|
|
html2pdf()
|
|
.set({ margin: 10,
|
|
filename: 'VPH_booking_summary.pdf',
|
|
html2canvas: { scale: 2 },
|
|
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
|
|
})
|
|
.from(element)
|
|
.save();
|
|
});
|
|
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|