wip: OrdersController#new

This commit is contained in:
kbe
2025-09-03 01:52:48 +02:00
parent 6965eb89fd
commit 839120f2f4
10 changed files with 206 additions and 96 deletions

View File

@@ -1,150 +1,169 @@
import { Controller } from "@hotwired/stimulus"
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 }
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()
this.updateCartSummary();
this.bindFormSubmission();
}
// Bind form submission to handle cart storage
bindFormSubmission() {
if (this.hasFormTarget) {
this.formTarget.addEventListener('submit', this.submitCart.bind(this))
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
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()
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
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()
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
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
if (value < 0) value = 0;
if (value > max) value = max;
input.value = value
this.updateCartSummary()
input.value = value;
this.updateCartSummary();
}
// Calculate and update the cart summary (total quantity and amount)
updateCartSummary() {
let totalQuantity = 0
let totalAmount = 0
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
this.quantityInputTargets.forEach((input) => {
const quantity = parseInt(input.value) || 0;
const price = parseInt(input.dataset.price) || 0;
totalQuantity += quantity
totalAmount += quantity * price
})
totalQuantity += quantity;
totalAmount += quantity * price;
});
// Update the displayed total quantity and amount
this.totalQuantityTarget.textContent = totalQuantity
this.totalAmountTarget.textContent = `${(totalAmount / 100).toFixed(2)}`
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
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
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()
event.preventDefault();
const cartData = this.buildCartData()
const cartData = this.buildCartData();
if (Object.keys(cartData).length === 0) {
alert('Veuillez sélectionner au moins un billet')
return
alert("Veuillez sélectionner au moins un billet");
return;
}
try {
// Store cart data in session
await this.storeCartInSession(cartData)
// Redirect to tickets/new page
const ticketNewUrl = `/events/${this.eventSlugValue}.${this.eventIdValue}/tickets/new`
window.location.href = ticketNewUrl
await this.storeCartInSession(cartData);
// Redirect to orders/new page
const OrderNewUrl = `/orders/new`;
window.location.href = OrderNewUrl;
} catch (error) {
console.error('Error storing cart:', error)
alert('Une erreur est survenue. Veuillez réessayer.')
console.error("Error storing cart:", error);
alert("Une erreur est survenue. Veuillez réessayer.");
}
}
// Build cart data from current form state
buildCartData() {
const cartData = {}
const cartData = {};
this.quantityInputTargets.forEach(input => {
const quantity = parseInt(input.value) || 0
this.quantityInputTargets.forEach((input) => {
const quantity = parseInt(input.value) || 0;
if (quantity > 0) {
const ticketTypeId = input.dataset.target
const ticketTypeId = input.dataset.target;
cartData[ticketTypeId] = {
quantity: quantity
}
quantity: quantity,
};
}
})
});
return cartData
return cartData;
}
// Store cart data in session via AJAX
async storeCartInSession(cartData) {
const storeCartUrl = `/api/v1/events/${this.eventIdValue}/store_cart`
const storeCartUrl = `/api/v1/events/${this.eventIdValue}/store_cart`;
const response = await fetch(storeCartUrl, {
method: 'POST',
method: "POST",
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
"Content-Type": "application/json",
"X-CSRF-Token": document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content"),
},
body: JSON.stringify({ cart: cartData })
})
body: JSON.stringify({ cart: cartData }),
});
if (!response.ok) {
throw new Error(`Failed to store cart data: ${response.status} ${response.statusText}`)
throw new Error(
`Failed to store cart data: ${response.status} ${response.statusText}`,
);
}
return response.json()
return response.json();
}
}