diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index 8c3812f..d9c19d9 100755 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -1,7 +1,7 @@ class EventsController < ApplicationController - before_action :authenticate_user!, only: [:checkout, :payment_success, :download_ticket] - before_action :set_event, only: [:show, :checkout] - + before_action :authenticate_user!, only: [ :checkout, :payment_success, :download_ticket ] + before_action :set_event, only: [ :show, :checkout ] + # Display all events def index @events = Event.includes(:user).upcoming.page(params[:page]).per(12) @@ -43,14 +43,14 @@ class EventsController < ApplicationController # Create Stripe line item line_items << { price_data: { - currency: 'eur', + currency: "eur", product_data: { name: "#{@event.name} - #{ticket_type.name}", - description: ticket_type.description, + description: ticket_type.description }, - unit_amount: ticket_type.price_cents, + unit_amount: ticket_type.price_cents }, - quantity: quantity, + quantity: quantity } # Store for ticket creation @@ -72,10 +72,10 @@ class EventsController < ApplicationController begin # Create Stripe Checkout Session session = Stripe::Checkout::Session.create({ - payment_method_types: ['card'], + payment_method_types: [ "card" ], line_items: line_items, - mode: 'payment', - success_url: payment_success_url(event_id: @event.id, session_id: '{CHECKOUT_SESSION_ID}'), + mode: "payment", + success_url: payment_success_url(event_id: @event.id, session_id: "{CHECKOUT_SESSION_ID}"), cancel_url: event_url(@event.slug, @event), customer_email: current_user.email, metadata: { @@ -98,29 +98,29 @@ class EventsController < ApplicationController begin session = Stripe::Checkout::Session.retrieve(session_id) - - if session.payment_status == 'paid' + + if session.payment_status == "paid" # Create tickets @event = Event.find(event_id) - order_items = JSON.parse(session.metadata['order_items']) + order_items = JSON.parse(session.metadata["order_items"]) @tickets = [] order_items.each do |item| - ticket_type = TicketType.find(item['ticket_type_id']) - item['quantity'].times do + ticket_type = TicketType.find(item["ticket_type_id"]) + item["quantity"].times do ticket = Ticket.create!( user: current_user, ticket_type: ticket_type, - status: 'active' + status: "active" ) @tickets << ticket - + # Send confirmation email for each ticket TicketMailer.purchase_confirmation(ticket).deliver_now end end - render 'payment_success' + render "payment_success" else redirect_to event_path(@event.slug, @event), alert: "Le paiement n'a pas été complété avec succès" end @@ -134,14 +134,14 @@ class EventsController < ApplicationController # Download ticket PDF def download_ticket @ticket = current_user.tickets.find(params[:ticket_id]) - + respond_to do |format| format.pdf do pdf = @ticket.to_pdf - send_data pdf, + send_data pdf, filename: "ticket-#{@ticket.event.name.parameterize}-#{@ticket.qr_code[0..7]}.pdf", - type: 'application/pdf', - disposition: 'attachment' + type: "application/pdf", + disposition: "attachment" end end end diff --git a/app/javascript/controllers/ticket_cart_controller.js b/app/javascript/controllers/ticket_cart_controller.js index a9eef3b..a93fdcc 100755 --- a/app/javascript/controllers/ticket_cart_controller.js +++ b/app/javascript/controllers/ticket_cart_controller.js @@ -102,7 +102,12 @@ export default class extends Controller { proceedToCheckout() { if (Object.keys(this.cart).length === 0) { - alert('Veuillez sélectionner au moins un billet') + this.showNotification('Veuillez sélectionner au moins un billet', 'warning') + return + } + + // Validate cart contents + if (!this.validateCartAvailability()) { return } @@ -110,17 +115,13 @@ export default class extends Controller { const isAuthenticated = document.body.dataset.userAuthenticated === "true" if (!isAuthenticated) { - if (confirm('Vous devez être connecté pour acheter des billets. Souhaitez-vous vous connecter maintenant ?')) { - // Store cart in session storage - sessionStorage.setItem('pending_cart', JSON.stringify({ - eventId: this.eventIdValue, - cart: this.cart - })) - window.location.href = '/auth/sign_in' - } + this.showLoginModal() return } + // Show loading state + this.setCheckoutLoading(true) + // Create form and submit to checkout const form = document.createElement('form') form.method = 'POST' @@ -145,6 +146,141 @@ export default class extends Controller { form.submit() } + validateCartAvailability() { + // Check each ticket type availability before checkout + for (let ticketTypeId in this.cart) { + const input = this.quantityTargetFor(ticketTypeId) + if (input) { + const maxAvailable = parseInt(input.max) + const requested = this.cart[ticketTypeId].quantity + + if (requested > maxAvailable) { + this.showNotification(`Seulement ${maxAvailable} billets disponibles pour ${this.cart[ticketTypeId].name}`, 'error') + // Adjust cart to maximum available + input.value = maxAvailable + this.updateCartItem(ticketTypeId, input) + return false + } + } + } + return true + } + + showLoginModal() { + // Create and show modern login modal + const modal = document.createElement('div') + modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50' + modal.innerHTML = ` +
Vous devez être connecté pour acheter des billets. Votre panier sera conservé.
+${message}
+