diff --git a/app/controllers/tickets_controller.rb b/app/controllers/tickets_controller.rb index 931d857..ea4f3e6 100644 --- a/app/controllers/tickets_controller.rb +++ b/app/controllers/tickets_controller.rb @@ -3,7 +3,7 @@ # This controller now primarily handles legacy redirects and backward compatibility # Most ticket creation functionality has been moved to OrdersController class TicketsController < ApplicationController - before_action :authenticate_user!, only: [ :payment_success, :payment_cancel, :show ] + before_action :authenticate_user!, only: [ :payment_success, :payment_cancel, :show, :download_ticket ] before_action :set_event, only: [ :checkout, :retry_payment ] @@ -48,21 +48,58 @@ class TicketsController < ApplicationController end end - # Display informations about the event with QR code + # Display ticket details def show - @ticket = Ticket.joins(order: :user).includes(:event, :ticket_type, order: :user) - .find_by(tickets: { qr_code: params[:qr_code] }) + # Try to find by qr_code first (for backward compatibility) + if params[:qr_code].present? + @ticket = Ticket.joins(order: :user).includes(:event, :ticket_type, order: :user) + .find_by(tickets: { qr_code: params[:qr_code] }) + else + # Find by ticket_id with user ownership check + @ticket = Ticket.joins(order: :user).includes(:event, :ticket_type, order: :user).find_by( + tickets: { id: params[:ticket_id] }, + orders: { user_id: current_user.id } + ) + end if @ticket.nil? redirect_to dashboard_path, alert: "Billet non trouvé" return end - @event = @ticket.event rescue ActiveRecord::RecordNotFound redirect_to dashboard_path, alert: "Billet non trouvé" end + # Download PDF ticket - only accessible by ticket owner + # User must be authenticated to download ticket + # TODO: change ID to an unique identifier (UUID) + def download_ticket + # Find ticket and ensure it belongs to current user + @ticket = Ticket.joins(order: :user).includes(:event, :ticket_type, order: :user).find_by( + tickets: { id: params[:ticket_id] }, + orders: { user_id: current_user.id } + ) + + if @ticket.nil? + redirect_to dashboard_path, alert: "Billet non trouvé ou vous n'avez pas l'autorisation d'accéder à ce billet" + return + end + + # Generate PDF + pdf_content = @ticket.to_pdf + + # Send PDF as download + send_data pdf_content, + filename: "ticket_#{@ticket.id}_#{@ticket.event.name.parameterize}.pdf", + type: "application/pdf", + disposition: "attachment" + rescue ActiveRecord::RecordNotFound + redirect_to dashboard_path, alert: "Billet non trouvé" + rescue => e + Rails.logger.error "Error generating ticket PDF: #{e.message}" + redirect_to dashboard_path, alert: "Erreur lors de la génération du billet" + end private def set_event diff --git a/app/javascript/controllers/ticket_selection_controller.js b/app/javascript/controllers/ticket_selection_controller.js index 8e9da7e..4dfd365 100644 --- a/app/javascript/controllers/ticket_selection_controller.js +++ b/app/javascript/controllers/ticket_selection_controller.js @@ -118,7 +118,7 @@ export default class extends Controller { await this.storeCartInSession(cartData); // Redirect to event-scoped orders/new page - const OrderNewUrl = `/events/${this.eventSlugValue}.${this.eventIdValue}/orders/new`; + const OrderNewUrl = `/orders/new/events/${this.eventSlugValue}.${this.eventIdValue}`; window.location.href = OrderNewUrl; } catch (error) { console.error("Error storing cart:", error); diff --git a/app/services/ticket_pdf_generator.rb b/app/services/ticket_pdf_generator.rb index a9548da..a22f07d 100755 --- a/app/services/ticket_pdf_generator.rb +++ b/app/services/ticket_pdf_generator.rb @@ -32,13 +32,18 @@ class TicketPdfGenerator # Ticket info box pdf.stroke_color "E5E7EB" pdf.fill_color "F9FAFB" - pdf.rounded_rectangle [ 0, pdf.cursor ], 310, 120, 10 + pdf.rounded_rectangle [ 0, pdf.cursor ], 310, 150, 10 pdf.fill_and_stroke pdf.move_down 10 pdf.fill_color "000000" pdf.font "Helvetica", size: 12 + # Customer name + pdf.text "Ticket Holder:", style: :bold + pdf.text "#{ticket.first_name} #{ticket.last_name}" + pdf.move_down 8 + # Ticket details pdf.text "Ticket Type:", style: :bold pdf.text ticket.ticket_type.name @@ -89,8 +94,8 @@ class TicketPdfGenerator raise "QR code data must be a valid string" end - qrcode = RQRCode::QRCode.new(qr_code_data) - pdf.print_qr_code(qrcode, extent: 120, align: :center) + # Generate QR code - prawn-qrcode expects the data string directly + pdf.print_qr_code(qr_code_data, extent: 120, align: :center) pdf.move_down 15 diff --git a/app/views/tickets/show.html.erb b/app/views/tickets/show.html.erb index 0181f79..ecad517 100644 --- a/app/views/tickets/show.html.erb +++ b/app/views/tickets/show.html.erb @@ -159,7 +159,7 @@ <% end %> <% if @ticket.status == 'active' %> - <%= link_to ticket_download_path(@ticket.qr_code), + <%= link_to download_ticket_path(@ticket.id), class: "flex-1 bg-gradient-to-r from-purple-600 to-indigo-600 hover:from-purple-700 hover:to-indigo-700 text-white font-medium py-3 px-6 rounded-xl shadow-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 transform hover:-translate-y-0.5 text-center" do %> diff --git a/config/routes.rb b/config/routes.rb index 0156ea0..966bf20 100755 --- a/config/routes.rb +++ b/config/routes.rb @@ -38,9 +38,9 @@ Rails.application.routes.draw do get "events", to: "events#index", as: "events" get "events/:slug.:id", to: "events#show", as: "event" - # === Orders === - get "events/:slug.:id/orders/new", to: "orders#new", as: "event_order_new" - post "events/:slug.:id/orders", to: "orders#create", as: "event_order_create" + # === Orders (scoped to events) === + get "orders/new/events/:slug.:id", to: "orders#new", as: "event_order_new" + post "orders/create/events/:slug.:id", to: "orders#create", as: "event_order_create" resources :orders, only: [ :show ] do member do @@ -50,18 +50,20 @@ Rails.application.routes.draw do end end - get "orders/payments/success", to: "orders#payment_success", as: "order_payment_success" - get "orders/payments/cancel", to: "orders#payment_cancel", as: "order_payment_cancel" + get "orders/payments/success", to: "orders#payment_success", as: "order_payment_success" + get "orders/payments/cancel", to: "orders#payment_cancel", as: "order_payment_cancel" - # Legacy ticket routes - redirect to order system + # Legacy routes - redirect to order system get "events/:slug.:id/tickets/checkout", to: "tickets#checkout", as: "ticket_checkout" post "events/:slug.:id/tickets/retry", to: "tickets#retry_payment", as: "ticket_retry_payment" get "payments/success", to: "tickets#payment_success", as: "payment_success" get "payments/cancel", to: "tickets#payment_cancel", as: "payment_cancel" # === Tickets === - get "tickets/:qr_code", to: "tickets#show", as: "ticket" - get "tickets/:qr_code/download", to: "events#download_ticket", as: "ticket_download" + # Support both ticket_id and qr_code for backward compatibility + get "tickets/:qr_code", to: "tickets#show", as: "ticket", constraints: { qr_code: /[^\/]+/ } + get "tickets/:ticket_id", to: "tickets#show", as: "ticket_by_id" + get "tickets/:ticket_id/download", to: "tickets#download_ticket", as: "download_ticket" # === Promoter Routes === namespace :promoter do