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.

199 lines
8.0 KiB

1 year ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>VPH Booking Summary</title>
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7. <link href="https://fonts.googleapis.com/css?family=Montserrat:400,600&display=swap" rel="stylesheet" />
  8. <script src="https://code.jquery.com/jquery-3.6.4.min.js"
  9. integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8="
  10. crossorigin="anonymous"></script>
  11. <style>
  12. :root { --ink:#1f2937; --muted:#6b7280; --bg1:#eff6ff; --bg2:#f0fdfa; --brand:#3b82f6; }
  13. *{box-sizing:border-box}
  14. body{
  15. font-family:'Montserrat',system-ui,Arial;
  16. background:linear-gradient(135deg,var(--bg1) 0%, var(--bg2) 100%);
  17. min-height:100vh; margin:0; padding:2rem 1rem;
  18. color:var(--ink);
  19. }
  20. .container{ max-width:64rem; margin:0 auto; }
  21. .header{ text-align:center; margin-bottom:1.5rem; }
  22. .header h1{ font-size:2rem; margin:.25rem 0; }
  23. .confirmation-header{ text-align:center; margin:1.25rem 0 1rem; }
  24. .success-icon{ font-size:3rem; margin-bottom:.75rem; }
  25. .confirmation-header h2{ margin:.25rem 0 .25rem; }
  26. .confirmation-header p{ color:var(--muted); margin:0; }
  27. .card{
  28. background:#fff; border-radius:12px; padding:1.25rem;
  29. box-shadow:0 10px 15px -3px rgba(0,0,0,.1);
  30. }
  31. .booking-summary h3{ margin:0 0 .75rem; font-size:1.1rem;text-align:center; }
  32. /* table layout (rock-solid for html2pdf) */
  33. .summary-table{
  34. width: 80%;
  35. margin-left: 100px; border-collapse:collapse; background:#f9fafb; border-radius:8px; overflow:hidden;
  36. }
  37. .summary-table th, .summary-table td{ padding:12px 10px; border-bottom:1px solid #e5e7eb; text-align:left; vertical-align:top; }
  38. .summary-table th{ width:450px; color:var(--muted); font-weight:600; }
  39. .summary-table tr:last-child th, .summary-table tr:last-child td{ border-bottom:0; }
  40. .important-note{ background:#eff6ff; padding:1rem; border-radius:8px; font-size:.95rem; color:#1e40af; margin-top:1rem; }
  41. .actions{ display:flex; justify-content:center; gap:12px; margin-top:1.25rem; }
  42. .btn{ border:0; border-radius:8px; padding:.75rem 1.25rem; font-weight:600; cursor:pointer; }
  43. .btn-secondary{ background:#e5e7eb; color:#111827; }
  44. .btn-secondary:hover{ background:#d1d5db; }
  45. .btn[disabled]{ opacity:.6; cursor:not-allowed; }
  46. .muted{ text-align:center; color:var(--muted); margin-top:.5rem; font-size:.9rem; }
  47. body.pdf-export { padding: 0 !important; }
  48. body.pdf-export .header { display: none !important; }
  49. body.pdf-export #bookingSummary { margin: 0 !important; }
  50. body.pdf-export .confirmation-header { margin: .5rem 0 !important; }
  51. body.pdf-export .summary-table { width: 100% !important; margin-left: 0 !important; }
  52. </style>
  53. </head>
  54. <body>
  55. <div class="container">
  56. <div class="header">
  57. <h1>Victoria Public Hall — Experiential Museum</h1>
  58. </div>
  59. <div id="bookingSummary" class="card">
  60. <div class="confirmation-header">
  61. <div class="success-icon">✅</div>
  62. <h2>Booking Confirmed!</h2>
  63. <p>Your VPH visit has been successfully booked</p>
  64. </div>
  65. <div class="booking-summary">
  66. <h3>Booking Summary</h3>
  67. <table class="summary-table">
  68. <tr><th>Contact Person</th><td id="summaryName"></td></tr>
  69. <tr><th>Phone</th><td id="summaryPhone"></td></tr>
  70. <tr><th>Number of Persons</th><td id="summaryMembers"></td></tr>
  71. <tr><th>Visit Date</th><td id="summaryDate"></td></tr>
  72. <tr><th>Time Slot</th><td id="summaryTimeSlot"></td></tr>
  73. <tr><th>Selected Slots</th><td id="summarySlots"></td></tr>
  74. </table>
  75. </div>
  76. <div class="important-note">
  77. <strong>Important:</strong> Please arrive 15 minutes before your scheduled time. Bring a valid ID proof for verification.
  78. </div>
  79. </div>
  80. <div class="actions">
  81. <button id="downloadBtn" class="btn btn-secondary" disabled>Preparing…</button>
  82. <a href="/victoriapublichall" class="btn btn-secondary">Book Another Visit</a>
  83. </div>
  84. </div>
  85. <script>
  86. async function ensureHtml2pdf() {
  87. if (window.html2pdf) return;
  88. try {
  89. await new Promise((resolve, reject) => {
  90. const s = document.createElement('script');
  91. s.src = 'https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js';
  92. s.onload = resolve; s.onerror = reject; document.head.appendChild(s);
  93. });
  94. } catch (e) {
  95. await new Promise((resolve, reject) => {
  96. const s = document.createElement('script');
  97. s.src = 'https://cdn.jsdelivr.net/npm/html2pdf.js@0.10.1/dist/html2pdf.bundle.min.js';
  98. s.onload = resolve; s.onerror = reject; document.head.appendChild(s);
  99. });
  100. }
  101. }
  102. (function () {
  103. const $btn = $('#downloadBtn');
  104. let summaryLoaded = false;
  105. const qs = new URLSearchParams(location.search);
  106. const refId = qs.get('refId') || '';
  107. if (!refId) { alert('Missing refId. Open this page as booking-summary.html?refId=YOUR_REF_ID'); return; }
  108. // build API URL — default uses @GetMapping("/summary/{refId}")
  109. const apiUrl = `/victoriapublichall/api/summary/${encodeURIComponent(refId)}`;
  110. // if your controller uses @RequestParam instead, switch to:
  111. // const apiUrl = `/victoriapublichall/api/summary?refId=${encodeURIComponent(refId)}`;
  112. $.getJSON(apiUrl)
  113. .done(function (data) {
  114. $('#summaryName').text(data.name || '');
  115. $('#summaryPhone').text(data.mobileno || '');
  116. $('#summaryMembers').text(data.noofpersons ?? '');
  117. if (data.booking_date) {
  118. const d = new Date(data.booking_date);
  119. const day = String(d.getDate()).padStart(2,'0');
  120. const month = String(d.getMonth()+1).padStart(2,'0'); // months are 0-indexed
  121. const year = d.getFullYear();
  122. $('#summaryDate').text(`${day}-${month}-${year}`);
  123. } else {
  124. $('#summaryDate').text('-');
  125. } $('#summaryTimeSlot').text(data.slot_timing || '');
  126. // seats -> "G1: n, G2: n" (10 per group)
  127. const seatsRaw = (data.seats || '').toString();
  128. const numbers = seatsRaw.split(',').map(s => parseInt(s.trim(), 10)).filter(n => !isNaN(n));
  129. const groupCounts = {};
  130. numbers.forEach(n => { const g = Math.ceil(n / 10); groupCounts[g] = (groupCounts[g] || 0) + 1; });
  131. const lines = Object.keys(groupCounts).sort((a,b)=>a-b).map(g => `G${g}: ${groupCounts[g]}`).join(', ');
  132. $('#summarySlots').text(lines || seatsRaw || '-');
  133. summaryLoaded = true;
  134. $btn.prop('disabled', false).text('Download PDF');
  135. })
  136. .fail(function (xhr) {
  137. alert(xhr?.responseJSON?.message || 'No booking found for this refId.');
  138. });
  139. $btn.on('click', async function () {
  140. if (!summaryLoaded) { alert('Please wait, loading details…'); return; }
  141. await ensureHtml2pdf();
  142. if (!window.html2pdf) { alert('PDF engine failed to load.'); return; }
  143. // Enter "PDF export" layout to reduce spacing
  144. document.body.classList.add('pdf-export');
  145. // Make sure fonts/layout are ready before capture
  146. if (document.fonts?.ready) { try { await document.fonts.ready; } catch {} }
  147. await new Promise(r => requestAnimationFrame(r));
  148. await new Promise(r => setTimeout(r, 30)); // tiny extra paint buffer
  149. const element = document.getElementById('bookingSummary');
  150. window.html2pdf().set({
  151. margin: 10,
  152. filename: 'VPH_booking_summary.pdf',
  153. image: { type: 'jpeg', quality: 0.98 },
  154. html2canvas: { scale: 2, useCORS: true, backgroundColor: null, scrollY: 0 },
  155. jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
  156. // let it paginate instead of clipping the bottom
  157. pagebreak: { mode: ['css', 'legacy'] }
  158. })
  159. .from(element)
  160. .save()
  161. .then(() => {
  162. // restore normal layout
  163. document.body.classList.remove('pdf-export');
  164. })
  165. .catch(() => {
  166. document.body.classList.remove('pdf-export');
  167. });
  168. });
  169. })();
  170. </script>
  171. </body>
  172. </html>