- Each ticket has a unique URL for viewing and downloading - Only the ticket owner can access their ticket - The customer's name is clearly displayed on the ticket - The PDF can be downloaded directly from the ticket view page - All existing functionality continues to work as expected
170 lines
4.7 KiB
JavaScript
170 lines
4.7 KiB
JavaScript
import { Controller } from "@hotwired/stimulus";
|
|
|
|
// Controller for handling ticket selection on the event show page
|
|
// Manages quantity inputs, calculates totals, and enables/disables the checkout button
|
|
export default class extends Controller {
|
|
static targets = [
|
|
"quantityInput",
|
|
"totalQuantity",
|
|
"totalAmount",
|
|
"checkoutButton",
|
|
"form",
|
|
];
|
|
static values = { eventSlug: String, eventId: String };
|
|
|
|
// Initialize the controller and update the cart summary
|
|
connect() {
|
|
this.updateCartSummary();
|
|
this.bindFormSubmission();
|
|
}
|
|
|
|
// Bind form submission to handle cart storage
|
|
bindFormSubmission() {
|
|
if (this.hasFormTarget) {
|
|
this.formTarget.addEventListener("submit", this.submitCart.bind(this));
|
|
}
|
|
}
|
|
|
|
// Increment the quantity for a specific ticket type
|
|
increment(event) {
|
|
const ticketTypeId = event.currentTarget.dataset.target;
|
|
const input = this.quantityInputTargets.find(
|
|
(input) => input.dataset.target === ticketTypeId,
|
|
);
|
|
const value = parseInt(input.value) || 0;
|
|
const max = parseInt(input.max) || 0;
|
|
|
|
if (value < max) {
|
|
input.value = value + 1;
|
|
this.updateCartSummary();
|
|
}
|
|
}
|
|
|
|
// Decrement the quantity for a specific ticket type
|
|
decrement(event) {
|
|
const ticketTypeId = event.currentTarget.dataset.target;
|
|
const input = this.quantityInputTargets.find(
|
|
(input) => input.dataset.target === ticketTypeId,
|
|
);
|
|
const value = parseInt(input.value) || 0;
|
|
|
|
if (value > 0) {
|
|
input.value = value - 1;
|
|
this.updateCartSummary();
|
|
}
|
|
}
|
|
|
|
// Update quantity when directly edited in the input field
|
|
updateQuantity(event) {
|
|
const input = event.currentTarget;
|
|
let value = parseInt(input.value) || 0;
|
|
const max = parseInt(input.max) || 0;
|
|
|
|
// Ensure value is within valid range (0 to max available)
|
|
if (value < 0) value = 0;
|
|
if (value > max) value = max;
|
|
|
|
input.value = value;
|
|
this.updateCartSummary();
|
|
}
|
|
|
|
// Calculate and update the cart summary (total quantity and amount)
|
|
updateCartSummary() {
|
|
let totalQuantity = 0;
|
|
let totalAmount = 0;
|
|
|
|
// Sum up quantities and calculate total amount
|
|
this.quantityInputTargets.forEach((input) => {
|
|
const quantity = parseInt(input.value) || 0;
|
|
const price = parseInt(input.dataset.price) || 0;
|
|
|
|
totalQuantity += quantity;
|
|
totalAmount += quantity * price;
|
|
});
|
|
|
|
// Update the displayed total quantity and amount
|
|
this.totalQuantityTarget.textContent = totalQuantity;
|
|
this.totalAmountTarget.textContent = `€${(totalAmount / 100).toFixed(2)}`;
|
|
|
|
// Enable/disable checkout button based on whether any tickets are selected
|
|
if (totalQuantity > 0) {
|
|
this.checkoutButtonTarget.classList.remove(
|
|
"opacity-50",
|
|
"cursor-not-allowed",
|
|
);
|
|
this.checkoutButtonTarget.disabled = false;
|
|
} else {
|
|
this.checkoutButtonTarget.classList.add(
|
|
"opacity-50",
|
|
"cursor-not-allowed",
|
|
);
|
|
this.checkoutButtonTarget.disabled = true;
|
|
}
|
|
}
|
|
|
|
// Handle form submission - store cart in session before proceeding
|
|
async submitCart(event) {
|
|
event.preventDefault();
|
|
|
|
const cartData = this.buildCartData();
|
|
|
|
if (Object.keys(cartData).length === 0) {
|
|
alert("Veuillez sélectionner au moins un billet");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Store cart data in session
|
|
await this.storeCartInSession(cartData);
|
|
|
|
// Redirect to event-scoped orders/new page
|
|
const OrderNewUrl = `/orders/new/events/${this.eventSlugValue}.${this.eventIdValue}`;
|
|
window.location.href = OrderNewUrl;
|
|
} catch (error) {
|
|
console.error("Error storing cart:", error);
|
|
alert("Une erreur est survenue. Veuillez réessayer.");
|
|
}
|
|
}
|
|
|
|
// Build cart data from current form state
|
|
buildCartData() {
|
|
const cartData = {};
|
|
|
|
this.quantityInputTargets.forEach((input) => {
|
|
const quantity = parseInt(input.value) || 0;
|
|
if (quantity > 0) {
|
|
const ticketTypeId = input.dataset.target;
|
|
cartData[ticketTypeId] = {
|
|
quantity: quantity,
|
|
};
|
|
}
|
|
});
|
|
|
|
return cartData;
|
|
}
|
|
|
|
// Store cart data in session via AJAX
|
|
async storeCartInSession(cartData) {
|
|
const storeCartUrl = `/api/v1/events/${this.eventIdValue}/store_cart`;
|
|
|
|
const response = await fetch(storeCartUrl, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"X-CSRF-Token": document
|
|
.querySelector('meta[name="csrf-token"]')
|
|
.getAttribute("content"),
|
|
},
|
|
body: JSON.stringify({ cart: cartData }),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(
|
|
`Failed to store cart data: ${response.status} ${response.statusText}`,
|
|
);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
}
|