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.
1070 lines
32 KiB
1070 lines
32 KiB
<!DOCTYPE html>
|
|
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
|
|
|
<head th:replace="~{fragments/user-head :: head}"></head>
|
|
|
|
<style>
|
|
|
|
.navbar-logo{
|
|
width:100% !important;
|
|
}
|
|
|
|
:root {
|
|
--accent: #12363b;
|
|
--muted: #6b6b6b;
|
|
--card-bg: #ffffff;
|
|
--slot-radius: 10px;
|
|
--green-start: #16a34a;
|
|
--green-end: #09905a;
|
|
}
|
|
|
|
body {
|
|
font-family: "Inter", "Helvetica Neue", Arial, sans-serif;
|
|
min-height: 100vh;
|
|
background-attachment: fixed;
|
|
background-repeat: no-repeat;
|
|
background-size: cover;
|
|
-webkit-font-smoothing: antialiased;
|
|
min-height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
}
|
|
|
|
|
|
|
|
label {
|
|
color: black;
|
|
}
|
|
|
|
|
|
/* Smooth horizontal scroll on mobile */
|
|
@media (max-width: 768px) {
|
|
.dataTables_wrapper {
|
|
overflow-x: auto;
|
|
}
|
|
|
|
table.dataTable {
|
|
width: 100% !important;
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
|
|
/* time slots container */
|
|
|
|
|
|
.time-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
|
gap: 12px;
|
|
}
|
|
|
|
.time-box {
|
|
border: 2px solid #dee2e6;
|
|
padding: 12px;
|
|
border-radius: 10px;
|
|
background: #fff;
|
|
}
|
|
|
|
.time-box.disabled {
|
|
opacity: 0.5;
|
|
pointer-events: none;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* av slots style*/
|
|
|
|
|
|
/* AV SLOT GRID */
|
|
.av-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr); /* 3 per row */
|
|
gap: 16px;
|
|
}
|
|
|
|
@media (max-width: 992px) {
|
|
.av-grid {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
}
|
|
|
|
@media (max-width: 576px) {
|
|
.av-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
/* AV SLOT CARD */
|
|
.av-card {
|
|
border: 2px solid #28a745;
|
|
border-radius: 12px;
|
|
padding: 14px 16px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
background: #fff;
|
|
}
|
|
|
|
.av-card.full {
|
|
border-color: #dc3545;
|
|
background: #fff5f5;
|
|
}
|
|
|
|
.av-time {
|
|
font-weight: 600;
|
|
font-size: 15px;
|
|
}
|
|
|
|
.av-seats {
|
|
font-size: 13px;
|
|
color: #6c757d;
|
|
}
|
|
|
|
.av-check input {
|
|
width: 20px;
|
|
height: 20px;
|
|
}
|
|
</style>
|
|
<body>
|
|
<div th:replace="~{fragments/preloader :: div}"></div>
|
|
<div id="pcoded" class="pcoded">
|
|
<div class="pcoded-container">
|
|
<!-- TOP NAV -->
|
|
<nav th:replace="~{fragments/nav-admin :: nav}"></nav>
|
|
|
|
<nav th:replace="~{fragments/nav-adminmenu :: nav}"></nav>
|
|
|
|
<div class="pcoded-main-container">
|
|
<div class="pcoded-wrapper">
|
|
<div class="pcoded-content">
|
|
<div class="pcoded-inner-content">
|
|
<br><br>
|
|
<div class="container mt-4">
|
|
<div class="card shadow">
|
|
|
|
<!-- HEADER -->
|
|
<div class="card-header bg-dark text-white ">
|
|
<h4 class="text-white text-center mb-0">Bulk Booking Request List</h4>
|
|
|
|
</div>
|
|
|
|
<!-- BODY -->
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-striped align-middle text-center" id="bookingTable"
|
|
style="width:100%">
|
|
<thead class="table-dark">
|
|
<tr>
|
|
<th class="text-center">S.No</th>
|
|
<th class="text-center">Ref ID</th>
|
|
<th class="text-center">Category</th>
|
|
<th class="text-center">Mobile No</th>
|
|
<th class="text-center">Prefer Date</th>
|
|
<th class="text-center">No.of People</th>
|
|
<th class="text-center">Payment Status</th>
|
|
<th class="text-center">Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- FOOTER -->
|
|
<div class="card-footer text-center">
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="viewModal" tabindex="-1">
|
|
<div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
|
|
<div class="modal-content">
|
|
|
|
<div class="modal-header bg-dark text-white">
|
|
<h5 class="modal-title">Bulk Booking Details</h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
|
|
<div class="modal-body">
|
|
|
|
<!-- hidden values -->
|
|
<input type="hidden" id="v_idproofid" name="v_idproofid">
|
|
<input type="hidden" id="v_userinput" name="v_userinput">
|
|
|
|
|
|
<!-- BOOKING DETAILS -->
|
|
<div class="row mb-2">
|
|
<div class="col-md-4"><strong>Reference ID:</strong> <span id="v_refid"></span></div>
|
|
<div class="col-md-4"><strong>Name:</strong> <span id="v_name"></span></div>
|
|
<div class="col-md-4"><strong>Contact:</strong> <span id="v_userinput_text"></span></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="row mb-2">
|
|
|
|
<div class="col-md-4"><strong>Preferred Date:</strong> <span id="v_date"></span></div>
|
|
<div class="col-md-4"><strong>Category:</strong> <span id="v_category"></span></div>
|
|
<div class="col-md-4"><strong>No. of People:</strong> <span id="v_people"></span></div>
|
|
|
|
</div>
|
|
|
|
<div class="row mb-2">
|
|
<div class="col-md-4"><strong>ID Proof:</strong> <span id="v_idproof"></span></div>
|
|
<div class="col-md-4"><strong>ID Details:</strong> <span id="v_id_details"></span></div>
|
|
<div class="col-md-4"><strong>Total Amount:</strong> <span id="v_amount"></span></div>
|
|
</div>
|
|
|
|
<div class="row mb-2">
|
|
|
|
<div class="col-md-4"><strong>Institution:</strong> <span id="v_institution"></span></div>
|
|
<div class="col-md-4"><strong>Organization:</strong> <span id="v_organization"></span></div>
|
|
</div>
|
|
|
|
|
|
|
|
<hr>
|
|
|
|
<!-- VISITOR BREAKUP -->
|
|
<h6 class="fw-bold">Visitor Breakup</h6>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-sm text-center">
|
|
<thead class="table-secondary">
|
|
<tr>
|
|
<th>S.No</th>
|
|
<th>Visitor Type</th>
|
|
<th>No. of People</th>
|
|
<th>Amount</th>
|
|
<th>Total</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="visitorBreakupBody"></tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!--<div class="row mt-2">
|
|
|
|
<div class="col-md-12 text-right">
|
|
<strong>Total Amount:</strong> ₹<span id="totalAmount">0</span>
|
|
</div>
|
|
</div>-->
|
|
|
|
|
|
|
|
<!-- slots related div -->
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label">Select Date</label>
|
|
<input type="date" class="form-control" id="searchdate">
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-bold">Capacity Summary</label>
|
|
<input type="text"
|
|
class="form-control"
|
|
id="totalCapacity"
|
|
value="0 / 0"
|
|
readonly>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="row">
|
|
<!-- LEFT : ALLOCATION -->
|
|
<div class="col-md-8">
|
|
|
|
<h6 class="fw-bold mt-3 d-none" id="timeSlotTitle">Allocated Time Slot</h6>
|
|
<div id="timeSlotContainer" class="time-grid mb-4"></div>
|
|
|
|
<h6 class="fw-bold d-none" id="avSlotTitle">Allocated AV Slots</h6>
|
|
<div id="avSlotContainer" class="av-grid"></div>
|
|
|
|
</div>
|
|
|
|
<!-- RIGHT : INFO MESSAGE -->
|
|
<div class="col-md-4">
|
|
<div id="allocationMessage"
|
|
class="alert alert-warning d-none"
|
|
role="alert">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--<div class="row mb-3">
|
|
<div class="col-md-8">
|
|
<div id="timeSlotContainer" class="time-grid mb-4"></div>
|
|
|
|
<div id="avSlotContainer" class="av-grid"></div>
|
|
<div>
|
|
<div class="col-md-4">
|
|
<div id="allocationMessage"
|
|
class="alert alert-warning d-none"
|
|
role="alert">
|
|
</div>
|
|
</div>
|
|
</div>-->
|
|
|
|
|
|
<!-- onclick="saveBulkBooking()"-->
|
|
|
|
<div class="text-center mt-4">
|
|
<button type="button" class="btn btn-success" id="submitBtn" disabled >Submit</button>
|
|
|
|
<button type="button" class="btn btn-secondary" onclick="goBack()">Back</button>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script th:replace="~{fragments/user-script :: script}"></script>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
|
|
<!-- DataTables -->
|
|
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.8/css/dataTables.bootstrap5.min.css">
|
|
<link rel="stylesheet" href="https://cdn.datatables.net/buttons/2.4.2/css/buttons.bootstrap5.min.css">
|
|
|
|
<script src="https://cdn.datatables.net/1.13.8/js/jquery.dataTables.min.js"></script>
|
|
<script src="https://cdn.datatables.net/1.13.8/js/dataTables.bootstrap5.min.js"></script>
|
|
<script src="https://cdn.datatables.net/responsive/2.5.0/js/dataTables.responsive.min.js"></script>
|
|
|
|
<!-- DataTable Buttons -->
|
|
<script src="https://cdn.datatables.net/buttons/2.4.2/js/dataTables.buttons.min.js"></script>
|
|
<script src="https://cdn.datatables.net/buttons/2.4.2/js/buttons.bootstrap5.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/pdfmake.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/vfs_fonts.js"></script>
|
|
<script src="https://cdn.datatables.net/buttons/2.4.2/js/buttons.html5.min.js"></script>
|
|
|
|
<!-- SweetAlert -->
|
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
|
|
|
<script>
|
|
|
|
|
|
<!-- global variables -->
|
|
|
|
let totalPeople = 0;
|
|
let timeSlotAllocated = 0;
|
|
let allAVSlots = [];
|
|
|
|
let currentBookingReadOnly = false;
|
|
let currentRefId = null;
|
|
|
|
|
|
$(document).ready(function () {
|
|
|
|
$('#bookingTable').DataTable({
|
|
ajax: {
|
|
url: '/victoriapublichall/bookingapi/getrequestlist',
|
|
type: 'GET',
|
|
dataSrc: ''
|
|
},
|
|
|
|
columns: [
|
|
{
|
|
data: null,
|
|
render: function (data, type, row, meta) {
|
|
return meta.row + 1;
|
|
}
|
|
},
|
|
{data: 'ref_id'},
|
|
{data: 'category'},
|
|
{
|
|
data: 'mob_no',
|
|
defaultContent: '-'
|
|
},
|
|
|
|
{
|
|
data: 'prefer_visit_date',
|
|
render: function (data) {
|
|
return data ? data : '-';
|
|
}
|
|
},
|
|
{data: 'no_of_people'},
|
|
{data: 'payment_status'},
|
|
{
|
|
data: 'ref_id',
|
|
render: function (data, type, row) {
|
|
return `<button class="btn btn-info btn-sm" onclick="viewDetails('${data}')">View</button>`;
|
|
}
|
|
}
|
|
],
|
|
|
|
responsive: true,
|
|
destroy: true,
|
|
pageLength: 10,
|
|
lengthMenu: [10, 25, 50, 100]
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function viewDetails(refId) {
|
|
|
|
$.ajax({
|
|
url: "/victoriapublichall/bookingapi/getrequestbyrefid",
|
|
type: "GET",
|
|
data: {refId: refId},
|
|
|
|
success: function (response) {
|
|
|
|
|
|
let booking = response.booking;
|
|
console.log("booking", booking)
|
|
let visitors = response.visitorBreakup;
|
|
|
|
currentRefId = booking.ref_id;
|
|
currentBookingReadOnly = false;
|
|
|
|
|
|
// BOOKING DETAILS
|
|
$("#v_refid").text(booking.ref_id || "-");
|
|
$("#v_name").text(booking.name || "-");
|
|
$("#v_mobile").text(booking.mob_no || "-");
|
|
$("#v_category").text(booking.category || "-");
|
|
$("#v_date").text(booking.prefer_visit_date || "-");
|
|
$("#v_people").text(booking.no_of_people || "-");
|
|
$("#v_idproof").text(booking.id_name || "-");
|
|
$("#v_id_details").text(booking.id_proof_number || "-");
|
|
$("#v_institution").text(booking.institution_name || "-");
|
|
$("#v_organization").text(booking.organization_name || "-");
|
|
$("#v_amount").text(booking.total_amount || "-")
|
|
|
|
// hidden
|
|
|
|
|
|
$("#v_idproofid").text(booking.idproodid);
|
|
$("#v_userinput_text").text(booking.mob_no || booking.email);
|
|
|
|
|
|
// VISITOR BREAKUP
|
|
let tbody = $("#visitorBreakupBody");
|
|
tbody.empty();
|
|
|
|
|
|
|
|
|
|
if (!visitors || visitors.length === 0) {
|
|
tbody.append(`
|
|
<tr>
|
|
<td colspan="5">No visitor breakup data available</td>
|
|
</tr>
|
|
`);
|
|
} else {
|
|
visitors.forEach((row, index) => {
|
|
|
|
let people = row.no_of_people || 0;
|
|
let amount = row.amount || 0;
|
|
let total = row.total_amount || 0;
|
|
|
|
totalPeople += people;
|
|
|
|
|
|
tbody.append(`
|
|
<tr>
|
|
<td>${index + 1}</td>
|
|
<td>${row.user_type}</td>
|
|
<td>${people}</td>
|
|
<td>₹${amount}</td>
|
|
<td>₹${total}</td>
|
|
</tr>
|
|
`);
|
|
});
|
|
}
|
|
|
|
|
|
|
|
totalPeople = Number(booking.no_of_people || 0);
|
|
timeSlotAllocated = 0;
|
|
$("#totalCapacity").val(`0 / ${totalPeople}`);
|
|
|
|
|
|
$("#searchdate").val("");
|
|
$("#timeSlotContainer").empty();
|
|
$("#avSlotContainer").empty();
|
|
|
|
|
|
|
|
if (booking.reg_payment_status === "PENDING") {
|
|
currentBookingReadOnly = true;
|
|
$("#submitBtn").prop("disabled", true);
|
|
$("#searchdate").prop("disabled", true);
|
|
|
|
loadAdminAllocation(booking.ref_id);
|
|
|
|
} else {
|
|
currentBookingReadOnly = false;
|
|
$("#submitBtn").prop("disabled", true);
|
|
$("#searchdate").prop("disabled", false);
|
|
}
|
|
|
|
|
|
$("#viewModal").modal("show");
|
|
},
|
|
|
|
error: function () {
|
|
Swal.fire("Error", "Unable to load booking details", "error");
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
// clear the fields while closing the modal
|
|
|
|
$('#viewModal').on('hidden.bs.modal', function () {
|
|
|
|
// 🔹 Reset globals
|
|
totalPeople = 0;
|
|
timeSlotAllocated = 0;
|
|
currentBookingReadOnly = false;
|
|
currentRefId = null;
|
|
allAVSlots = [];
|
|
|
|
$("#v_refid, #v_name, #v_mobile, #v_category, #v_date").text("");
|
|
$("#v_people, #v_idproof, #v_id_details").text("");
|
|
$("#v_institution, #v_organization, #v_amount").text("");
|
|
|
|
$("#visitorBreakupBody").empty();
|
|
$("#timeSlotContainer").empty();
|
|
$("#avSlotContainer").empty();
|
|
|
|
$("#timeSlotTitle, #avSlotTitle").addClass("d-none");
|
|
|
|
// 🔹 Reset message panel
|
|
$("#allocationMessage").addClass("d-none").html("");
|
|
|
|
// 🔹 Reset date & capacity
|
|
$("#searchdate").val("").prop("disabled", false);
|
|
$("#totalCapacity").val("0 / 0");
|
|
|
|
$("#submitBtn").show().prop("disabled", true);
|
|
});
|
|
|
|
|
|
|
|
|
|
// date related functions
|
|
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
|
|
const dateInput = document.getElementById("searchdate");
|
|
|
|
const today = new Date();
|
|
today.setDate(today.getDate() + 8);
|
|
|
|
const yyyy = today.getFullYear();
|
|
const mm = String(today.getMonth() + 1).padStart(2, '0');
|
|
const dd = String(today.getDate()).padStart(2, '0');
|
|
|
|
dateInput.min = `${yyyy}-${mm}-${dd}`;
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$("#searchdate").on("change", function () {
|
|
|
|
const visitDate = $(this).val();
|
|
if (!visitDate) return;
|
|
|
|
// tuesday not allowed bookings
|
|
|
|
|
|
const selectedDate = new Date(visitDate);
|
|
|
|
// 0=Sun, 1=Mon, 2=Tue ...
|
|
if (selectedDate.getDay() === 2) {
|
|
|
|
// 🔴 Clear already loaded slots
|
|
$("#timeSlotContainer").empty();
|
|
$("#avSlotContainer").empty();
|
|
|
|
// Reset capacity
|
|
timeSlotAllocated = 0;
|
|
$("#totalCapacity").val(`0 / ${totalPeople}`);
|
|
|
|
// Disable submit
|
|
$("#submitBtn").prop("disabled", true);
|
|
|
|
Swal.fire(
|
|
"Maintenance Day",
|
|
"Bookings not allowed on tuesdays. Please select another date.",
|
|
"warning"
|
|
);
|
|
|
|
// Clear date
|
|
$(this).val("");
|
|
return;
|
|
}
|
|
|
|
|
|
$.get("/victoriapublichall/bookingapi/getTimeMaster", { visitDate: visitDate,
|
|
view: "report" },
|
|
function (data) {
|
|
|
|
let html = "";
|
|
|
|
data.forEach(slot => {
|
|
|
|
if (slot.availCap <= 0) return;
|
|
|
|
html += `
|
|
<div class="time-box" data-time-id="${slot.time_id}">
|
|
<div class="fw-bold">${slot.timing}</div>
|
|
<div class="text-muted">
|
|
<span class="time-available">${slot.availCap}</span> Available
|
|
</div>
|
|
|
|
<div class="form-check">
|
|
<input type="checkbox"
|
|
class="form-check-input slot-check"
|
|
data-time-id="${slot.time_id}"
|
|
data-available="${slot.availCap}">
|
|
<label>Select Slot</label>
|
|
</div>
|
|
|
|
<div class="allocation-text text-success"></div>
|
|
</div>`;
|
|
});
|
|
|
|
$("#timeSlotContainer").html(html);
|
|
loadAllAVSlots(visitDate);
|
|
});
|
|
|
|
|
|
|
|
validateSubmitState();
|
|
});
|
|
|
|
$(document).on("change", ".slot-check", function () {
|
|
|
|
timeSlotAllocated = 0;
|
|
let remaining = totalPeople;
|
|
|
|
$(".allocation-text").text("");
|
|
|
|
$(".slot-check:checked").each(function () {
|
|
|
|
if (remaining <= 0) return;
|
|
|
|
const available = Number($(this).data("available"));
|
|
const allocated = Math.min(available, remaining);
|
|
|
|
remaining -= allocated;
|
|
timeSlotAllocated += allocated;
|
|
|
|
|
|
$(this).closest(".time-box")
|
|
.find(".allocation-text")
|
|
.text(`Allocated: ${allocated}`)
|
|
.data("allocated", allocated);
|
|
});
|
|
|
|
$("#totalCapacity").val(`${timeSlotAllocated} / ${totalPeople}`);
|
|
|
|
|
|
if (timeSlotAllocated === totalPeople) {
|
|
|
|
$(".slot-check").each(function () {
|
|
if (!this.checked) {
|
|
$(this).prop("disabled", true);
|
|
}
|
|
});
|
|
|
|
} else {
|
|
// Enable again if allocation reduced
|
|
$(".slot-check").prop("disabled", false);
|
|
}
|
|
renderSelectedAVSlots();
|
|
|
|
|
|
|
|
validateSubmitState();
|
|
});
|
|
|
|
function loadAllAVSlots(visitDate) {
|
|
|
|
$.get("/victoriapublichall/bookingapi/getTimeSlotDetailsbydate",
|
|
{ visitDate },
|
|
function (data) {
|
|
allAVSlots = data || [];
|
|
}
|
|
);
|
|
}
|
|
|
|
|
|
function renderSelectedAVSlots() {
|
|
|
|
const selectedTimeIds = $(".slot-check:checked")
|
|
.map(function () {
|
|
return Number($(this).data("time-id"));
|
|
})
|
|
.get();
|
|
|
|
if (selectedTimeIds.length === 0) {
|
|
$("#avSlotContainer").html(
|
|
"<p class='text-warning'>Select time slots to allocate AV</p>"
|
|
);
|
|
return;
|
|
}
|
|
|
|
let html = "";
|
|
|
|
selectedTimeIds.forEach(timeId => {
|
|
|
|
// get allocated people for THIS timeId
|
|
const allocationText = $(`.slot-check[data-time-id="${timeId}"]`)
|
|
.closest(".time-box")
|
|
.find(".allocation-text")
|
|
.text();
|
|
|
|
const allocatedForTime = allocationText
|
|
? Number(allocationText.replace(/\D/g, ''))
|
|
: 0;
|
|
|
|
if (allocatedForTime <= 0) return;
|
|
|
|
// AV slots ONLY for this timeId
|
|
const avSlotsForTime = allAVSlots.filter(av =>
|
|
av.time_id === timeId
|
|
);
|
|
|
|
html += renderAVSlotsPerTime(
|
|
timeId,
|
|
allocatedForTime,
|
|
avSlotsForTime
|
|
);
|
|
});
|
|
|
|
$("#avSlotContainer").html(html);
|
|
|
|
if (currentBookingReadOnly) {
|
|
$(".slot-check").prop("disabled", true);
|
|
$(".av-checkbox").prop("disabled", true);
|
|
}
|
|
|
|
validateSubmitState();
|
|
|
|
}
|
|
|
|
|
|
function renderAVSlotsPerTime(timeId, peopleForTime, avSlots) {
|
|
|
|
let remaining = peopleForTime;
|
|
let html = "";
|
|
|
|
avSlots.forEach(slot => {
|
|
|
|
if (remaining <= 0 || slot.availCap <= 0) return;
|
|
|
|
const allocated = Math.min(slot.availCap, remaining);
|
|
remaining -= allocated;
|
|
|
|
html += `
|
|
<div class="av-card" data-time-id="${timeId}">
|
|
<div>
|
|
<div class="av-time">
|
|
${slot.timing} (${slot.language})
|
|
</div>
|
|
<div class="av-seats">
|
|
Capacity: ${slot.availCap} | Allocated: <b>${allocated}</b>
|
|
</div>
|
|
</div>
|
|
<div class="av-check">
|
|
<input type="checkbox"
|
|
checked
|
|
disabled
|
|
class="av-checkbox"
|
|
data-slot-id="${slot.slot_id}">
|
|
</div>
|
|
</div>`;
|
|
});
|
|
|
|
return html;
|
|
}
|
|
|
|
|
|
function validateSubmitState() {
|
|
|
|
if (currentBookingReadOnly) {
|
|
$("#submitBtn").prop("disabled", true);
|
|
return;
|
|
}
|
|
|
|
const visitDate = $("#searchdate").val();
|
|
const selectedTimeCount = $(".slot-check:checked").length;
|
|
const avCount = $(".av-card").length;
|
|
|
|
let allocated = 0;
|
|
$(".allocation-text").each(function () {
|
|
const txt = $(this).text();
|
|
if (txt.includes("Allocated")) {
|
|
allocated += Number(txt.replace(/\D/g, ''));
|
|
}
|
|
});
|
|
|
|
const isValid =
|
|
visitDate &&
|
|
selectedTimeCount > 0 &&
|
|
avCount > 0 &&
|
|
allocated === totalPeople;
|
|
|
|
$("#submitBtn").prop("disabled", !isValid);
|
|
}
|
|
|
|
|
|
|
|
|
|
function loadAdminAllocation(bulkRefId) {
|
|
|
|
$.get("/victoriapublichall/bookingapi/getBulkAllocation",
|
|
{ bulkRefId },
|
|
function (res) {
|
|
|
|
if (!res.allocated) {
|
|
// No allocation yet → admin can allocate
|
|
$("#searchdate").prop("disabled", false);
|
|
$("#submitBtn").prop("disabled", true);
|
|
|
|
$("#allocationMessage").addClass("d-none");
|
|
$("#timeSlotTitle, #avSlotTitle").addClass("d-none");
|
|
|
|
return;
|
|
}
|
|
|
|
$("#timeSlotTitle").removeClass("d-none");
|
|
$("#avSlotTitle").removeClass("d-none");
|
|
|
|
$("#searchdate").val(res.visitDate).prop("disabled", true);
|
|
$("#submitBtn").hide();
|
|
|
|
$("#timeSlotContainer").empty();
|
|
$("#avSlotContainer").empty();
|
|
|
|
let totalAllocated = 0;
|
|
|
|
res.timeSlots.forEach(time => {
|
|
|
|
totalAllocated += time.people;
|
|
|
|
// TIME SLOT BOX
|
|
$("#timeSlotContainer").append(`
|
|
<div class="time-box disabled">
|
|
<div class="fw-bold">${time.timeRange}</div>
|
|
<div class="text-success">
|
|
Allocated: ${time.people}
|
|
</div>
|
|
</div>
|
|
`);
|
|
|
|
// AV SLOTS
|
|
time.avSlots.forEach(av => {
|
|
$("#avSlotContainer").append(`
|
|
<div class="av-card full">
|
|
<div>
|
|
<div class="av-time">
|
|
${av.time} (${av.language})
|
|
</div>
|
|
<div class="av-seats">
|
|
Allocated: <b>${av.people}</b>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`);
|
|
});
|
|
});
|
|
|
|
$("#totalCapacity").val(`${totalAllocated} / ${totalPeople}`);
|
|
|
|
|
|
$("#allocationMessage")
|
|
.removeClass("d-none")
|
|
.html(`
|
|
<h6 class="fw-bold mb-2">Allocation Status</h6>
|
|
<p class="mb-1">
|
|
This request (<b>${bulkRefId}</b>) has already been allocated.
|
|
</p>
|
|
<p class="mb-0 text-danger">
|
|
Waiting for payment confirmation.
|
|
</p>
|
|
`);
|
|
}
|
|
);
|
|
}
|
|
|
|
|
|
|
|
// save bulk booking
|
|
$("#submitBtn").on("click", function () {
|
|
|
|
const bulkRefId = $("#v_refid").text();
|
|
const mobNo = $("#v_mobile").text();
|
|
|
|
const userInput = $("#v_userinput_text").text();
|
|
|
|
const noOfPeople = $("#v_people").text();
|
|
const idType = $("#v_idproofid").text();
|
|
const idDetails = $("#v_id_details").text();
|
|
const visitDate = $("#searchdate").val();
|
|
|
|
|
|
if (currentBookingReadOnly) {
|
|
Swal.fire("Info", "This booking is already submitted", "info");
|
|
return;
|
|
}
|
|
|
|
let allocated = 0;
|
|
$(".allocation-text").each(function () {
|
|
const txt = $(this).text();
|
|
if (txt.includes("Allocated")) {
|
|
allocated += Number(txt.replace(/\D/g, ''));
|
|
}
|
|
});
|
|
|
|
if (allocated !== totalPeople) {
|
|
Swal.fire(
|
|
"Allocation mismatch",
|
|
`Total People: ${totalPeople}, Allocated: ${allocated}`,
|
|
"error"
|
|
);
|
|
return;
|
|
}
|
|
|
|
$("#submitBtn").prop("disabled", true);
|
|
|
|
if (!visitDate) {
|
|
Swal.fire("Warning", "Please select visit date", "warning");
|
|
return;
|
|
}
|
|
|
|
let timeIds = $(".slot-check:checked")
|
|
.map(function () { return $(this).data("time-id"); })
|
|
.get();
|
|
|
|
let avSlotIds = $(".av-checkbox")
|
|
.map(function () { return $(this).data("slot-id"); })
|
|
.get();
|
|
|
|
|
|
$.ajax({ url: "/victoriapublichall/bookingapi/saveBulkApproveBooking",
|
|
type: "POST",
|
|
traditional: true,
|
|
data: {
|
|
bulkRefId: bulkRefId,
|
|
userInput: userInput,
|
|
noOfPeople: noOfPeople,
|
|
idType: idType,
|
|
idDetails: idDetails,
|
|
visitDate: visitDate,
|
|
timeIds: timeIds,
|
|
avSlotIds: avSlotIds },
|
|
success: function () {
|
|
|
|
Swal.fire({
|
|
title: "Success",
|
|
text: "Booking saved successfully",
|
|
icon: "success",
|
|
allowOutsideClick: false,
|
|
allowEscapeKey: false,
|
|
allowEnterKey: false,
|
|
confirmButtonText: "OK"
|
|
}).then((result) => {
|
|
|
|
if (result.isConfirmed) {
|
|
|
|
currentBookingReadOnly = true;
|
|
|
|
$(".slot-check").prop("disabled", true);
|
|
$(".av-checkbox").prop("disabled", true);
|
|
$("#searchdate").prop("disabled", true);
|
|
|
|
$("#viewModal").modal("hide");
|
|
|
|
$("#bookingTable").DataTable().ajax.reload();
|
|
}
|
|
});
|
|
|
|
},
|
|
|
|
error: function () {
|
|
|
|
$("#submitBtn").prop("disabled", false);
|
|
|
|
Swal.fire("Error", "Save failed", "error");
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
|
|
// goback function
|
|
|
|
function goBack() {
|
|
|
|
|
|
$("#viewModal").modal("hide");
|
|
|
|
|
|
currentBookingReadOnly = false;
|
|
currentRefId = null;
|
|
|
|
|
|
$("#timeSlotContainer").empty();
|
|
$("#avSlotContainer").empty();
|
|
|
|
|
|
$("#timeSlotTitle, #avSlotTitle").addClass("d-none");
|
|
|
|
|
|
$("#searchdate")
|
|
.val("")
|
|
.prop("disabled", false);
|
|
|
|
$("#totalCapacity").val(`0 / 0`);
|
|
|
|
|
|
|
|
|
|
$(".slot-check, .av-checkbox").prop("disabled", false);
|
|
}
|
|
|
|
|
|
|
|
document.getElementById("searchdate").addEventListener("change", function () {
|
|
const selectedDate = new Date(this.value);
|
|
|
|
if (selectedDate.getDay() === 2) {
|
|
Swal.fire(
|
|
"Invalid Date",
|
|
"Bookings are not allowed on Tuesdays.",
|
|
"warning"
|
|
);
|
|
this.value = ""; // reset date
|
|
}
|
|
});
|
|
|
|
</script>
|
|
|
|
|
|
|
|
</body>
|
|
</html>
|