| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <title>Book a meeting with SumUp</title> |
| <link rel="stylesheet" href="style.css" /> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> |
| </head> |
| <body> |
| <div class="container"> |
| <h2>Book a meeting with SumUp</h2> |
| <form id="booking-form" aria-labelledby="form-heading"> |
| <input type="text" id="name" placeholder="Your First Name" required /> |
| <input type="email" id="email" placeholder="Your Email" required /> |
| <button type="submit">Check availability</button> |
|
|
| <select id="slots" style="display:none;" aria-label="Available Time Slots"></select> |
| <button type="button" id="bookBtn" style="display:none;" aria-label="Confirm Booking">Confirm booking</button> |
| </form> |
|
|
| <div id="spinner" aria-live="polite"></div> |
| <p id="status" aria-live="polite"></p> |
|
|
| <div id="confirmation" class="hidden" aria-live="polite"> |
| <h3>✅ Meeting confirmed with SumUp</h3> |
| <p><strong>Name:</strong> <span id="conf-name"></span></p> |
| <p><strong>Email:</strong> <span id="conf-email"></span></p> |
| <p><strong>Time:</strong> <span id="conf-slot"></span></p> |
| </div> |
| </div> |
|
|
| <img src="sumup-logo.png" alt="SumUp logo" class="logo" /> |
|
|
| <script> |
| let repEmail = ""; |
| const slotsDropdown = document.getElementById("slots"); |
| const bookBtn = document.getElementById("bookBtn"); |
| const statusEl = document.getElementById("status"); |
| const spinner = document.getElementById("spinner"); |
| |
| function showSpinner() { spinner.style.display = "block"; } |
| function hideSpinner() { spinner.style.display = "none"; } |
| |
| function setStatus(message, type="info") { |
| statusEl.className = ""; |
| statusEl.classList.add(`status-${type}`); |
| statusEl.innerText = message; |
| statusEl.style.display = "block"; |
| } |
| |
| |
| |
| |
| const urlParams = new URLSearchParams(window.location.search); |
| const leadName = urlParams.get("ln") || ""; |
| const leadEmail = urlParams.get("le") || ""; |
| const salesRepEmailParam = urlParams.get("oe") || ""; |
| |
| document.getElementById("name").value = leadName; |
| document.getElementById("email").value = leadEmail; |
| |
| async function fetchSlots(name, email, repEmailParam) { |
| showSpinner(); |
| setStatus("Loading available slots...", "info"); |
| try { |
| const res = await fetch(`/available?name=${encodeURIComponent(name)}&email=${encodeURIComponent(email)}&salesRepEmail=${encodeURIComponent(repEmailParam)}`); |
| const data = await res.json(); |
| hideSpinner(); |
| if (data.error) { |
| setStatus(data.error, "error"); |
| return; |
| } |
| repEmail = data.repEmail; |
| const slots = data.slots; |
| slotsDropdown.innerHTML = ""; |
| if (!slots || slots.length === 0) { |
| setStatus("No available time slots found.", "error"); |
| return; |
| } |
| slots.forEach(slot => { |
| const option = document.createElement("option"); |
| option.value = slot.start; |
| option.text = new Date(slot.start).toLocaleString(); |
| slotsDropdown.appendChild(option); |
| }); |
| slotsDropdown.style.display = "block"; |
| bookBtn.style.display = "inline-block"; |
| setStatus("Select a time slot to book.", "success"); |
| } catch (error) { |
| hideSpinner(); |
| setStatus("Failed to fetch available slots.", "error"); |
| } |
| } |
| |
| |
| if (leadName && leadEmail && salesRepEmailParam) { |
| fetchSlots(leadName, leadEmail, salesRepEmailParam); |
| } |
| |
| |
| |
| |
| const form = document.getElementById("booking-form"); |
| form.addEventListener("submit", async (e) => { |
| e.preventDefault(); |
| const name = document.getElementById("name").value; |
| const email = document.getElementById("email").value; |
| if (!name || !email) { |
| setStatus("Please enter your name and email.", "error"); |
| return; |
| } |
| fetchSlots(name, email, salesRepEmailParam || repEmail); |
| }); |
| |
| |
| |
| |
| bookBtn.addEventListener("click", async () => { |
| const name = document.getElementById("name").value; |
| const email = document.getElementById("email").value; |
| const selectedSlot = slotsDropdown.value; |
| |
| if (!selectedSlot || !repEmail) { |
| setStatus("Please select a time slot first.", "error"); |
| return; |
| } |
| |
| showSpinner(); |
| setStatus("Booking your meeting...", "info"); |
| |
| try { |
| const res = await fetch("/book", { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ |
| name, |
| email, |
| salesRepEmail: repEmail, |
| selectedSlot |
| }) |
| }); |
| const result = await res.json(); |
| hideSpinner(); |
| if (result.message) setStatus(result.message, "success"); |
| |
| const confirmationPanel = document.getElementById("confirmation"); |
| confirmationPanel.classList.add("visible"); |
| |
| document.getElementById("conf-name").innerText = name; |
| document.getElementById("conf-email").innerText = email; |
| document.getElementById("conf-slot").innerText = new Date(selectedSlot).toLocaleString(); |
| |
| slotsDropdown.style.display = "none"; |
| bookBtn.style.display = "none"; |
| form.reset(); |
| |
| setTimeout(() => { statusEl.style.display = "none"; }, 3000); |
| |
| } catch (error) { |
| hideSpinner(); |
| setStatus("Booking failed. Please try again.", "error"); |
| } |
| }); |
| </script> |
| </body> |
| </html> |