/** * Booking JavaScript for handling JSON API calls */ // Store event data from template let eventData = null; const loader = function() { // Booking form const bookingForm = document.getElementById('booking-form'); if (bookingForm) { bookingForm.addEventListener('submit', handleBookingFormSubmit); } }; document.addEventListener('DOMContentLoaded', loader); /** * Handle booking form submission */ async function handleBookingFormSubmit(e) { e.preventDefault(); const submitBtn = e.target.querySelector('button[type="submit"]'); const originalHTML = submitBtn.innerHTML; try { // Show loading state submitBtn.disabled = true; submitBtn.innerHTML = 'Wird gesendet...'; // Get event slug from form const eventSlug = e.target.getAttribute('data-event-slug'); // Build booking data from form const bookingData = buildBookingDataFromForm(e.target); // Send API request to prepare booking const response = await fetch(`/api/events/${eventSlug}/prepareBooking`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(bookingData) }); const result = await response.json(); if (!response.ok) { throw new Error(result.error || 'Unbekannter Fehler aufgetreten'); } // Save token to localStorage localStorage.setItem('bookingPreparationToken', result.data.token); console.log(result.data.token); // Create form to submit to confirmation page const confirmForm = document.createElement('form'); confirmForm.method = 'POST'; confirmForm.action = `/event/${eventSlug}/confirm`; const tokenInput = document.createElement('input'); tokenInput.type = 'hidden'; tokenInput.name = 'token'; tokenInput.value = result.data.token; confirmForm.appendChild(tokenInput); document.body.appendChild(confirmForm); confirmForm.submit(); } catch (error) { // Show error message showErrorMessage(error.message); // Reset button state submitBtn.disabled = false; submitBtn.innerHTML = originalHTML; } } addEventListener("focusout", (event) => { buildBookingDataFromForm(document.getElementById('booking-form')); }); function elementChange(event) { buildBookingDataFromForm(document.getElementById('booking-form')); loadForm(); } function updateAddon(btn) { buildBookingDataFromForm(document.getElementById('booking-form')); loadForm(); } function addParticipant() { buildBookingDataFromForm(document.getElementById('booking-form')); var bookingDataStr = localStorage.getItem('bookingData'); var bookingData = JSON.parse(bookingDataStr); bookingData.participants.push({}) localStorage.setItem('bookingData', JSON.stringify(bookingData)); loadForm(); } function removeParticipant(btn) { console.log(btn.dataset.participantDelete); const delBtns = document.querySelectorAll("button[data-participant-delete]") console.log(delBtns.length); // Only remove if there is more than one participant if (delBtns.length <= 1) { return } // Manilupate the participant array buildBookingDataFromForm(document.getElementById('booking-form')); var bookingDataStr = localStorage.getItem('bookingData'); var bookingData = JSON.parse(bookingDataStr); bookingData.participants.splice(btn.dataset.participantDelete, 1); localStorage.setItem('bookingData', JSON.stringify(bookingData)); loadForm(); } /** * Build booking data object from form */ function buildBookingDataFromForm(form) { // Contact information - backend expects these at root level, not nested in 'address' const bookingData = { email: form.querySelector('#email').value || '', street: form.querySelector('#street').value || '', postalCode: form.querySelector('#postal_code').value || '', city: form.querySelector('#city').value || '', phone: form.querySelector('#phone').value || '', country: form.querySelector('#country').value || 'de', vouchers: form.querySelector('#voucher').value.split(','), participants: [] }; // Process participants const participantSections = form.querySelectorAll('.participant-section'); participantSections.forEach((section, index) => { const participant = { name: section.querySelector(`[name="participants[${index}][name]"]`).value, isMember: section.querySelector(`[name="participants[${index}][is_member]"]`).checked, ticketCategoryId: parseInt(section.querySelector(`[name="participants[${index}][category]"]`).value, 10), questionnaire: {}, addons: [], }; // Process questionnaire answers const questionnaireInputs = section.querySelectorAll('[name^="participants[' + index + '][questionnaire]"]'); questionnaireInputs.forEach(input => { const nameMatch = input.name.match(/\[questionnaire\]\[([^\]]+)\]/); if (nameMatch) { const slug = nameMatch[1]; // Handle different input types if (input.type === 'radio') { if (input.checked) { participant.questionnaire[slug] = input.value; } } else if (input.type === 'checkbox') { if (input.checked) { participant.questionnaire[slug] = 'true'; } else { participant.questionnaire[slug] = 'false'; } } else { // Only include non-empty values if (input.value.trim() !== '') { participant.questionnaire[slug] = input.value; } } } }); const addons = section.querySelectorAll('input[data-addon-checkbox]'); for (const addon of addons) { if (!addon.checked) { continue; } const addonId = parseInt(addon.dataset.addonId, 10); const payloadAddon = { addonId: addonId, questionnaire: {} } const questions = section.querySelectorAll(`[data-participant-index='${index}'][data-addon-id='${addonId}'][data-addon-question]`); for (const question of questions) { switch (question.dataset.addonQuestionType) { case 'bool': case 'string': case 'uint': case 'dropdown': payloadAddon.questionnaire[question.dataset.addonQuestion] = question.value; break; case 'radio': // Only add the selected radio button if (question.checked) { payloadAddon.questionnaire[question.dataset.addonQuestion] = question.value; } break; } } participant.addons.push(payloadAddon); } bookingData.participants.push(participant); }); // Debug logging console.log('Built booking data:', bookingData); localStorage.setItem('bookingData', JSON.stringify(bookingData)); return bookingData; } function showErrorMessage(message) { // Remove existing error message const existingError = document.getElementById('api-error-message'); if (existingError) { existingError.remove(); } // Create error message element with Bootstrap styling const errorElement = document.createElement('div'); errorElement.id = 'api-error-message'; errorElement.className = 'alert alert-danger alert-dismissible fade show'; errorElement.innerHTML = ` Fehler: ${message} `; // Insert at the top of the main container const container = document.querySelector('main'); const firstChild = container.querySelector('.row'); container.insertBefore(errorElement, firstChild); // Scroll to error message errorElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); }