diff --git a/app/services/ticket_pdf_generator.rb b/app/services/ticket_pdf_generator.rb index 6846ab2..68e929a 100755 --- a/app/services/ticket_pdf_generator.rb +++ b/app/services/ticket_pdf_generator.rb @@ -4,8 +4,8 @@ require "rqrcode" # PDF ticket generator service using Prawn # -# Generates PDF tickets with QR codes for event entry validation -# Includes event details, venue information, and unique QR code for each ticket +# Generates simple, compact PDF tickets with QR codes for event entry validation +# Clean, minimalist design that fits on a single page class TicketPdfGenerator # Suppress Prawn's internationalization warning for built-in fonts Prawn::Fonts::AFM.hide_m17n_warning = true @@ -16,199 +16,109 @@ class TicketPdfGenerator end def generate - Prawn::Document.new(page_size: [400, 650], margin: 0) do |pdf| - # Main container with modern gradient background - create_background(pdf) - - # Header section with brand and visual hierarchy - create_header(pdf) - - # Event information card - create_event_card(pdf) - - # Ticket holder information - create_holder_section(pdf) - - # QR Code section with modern styling + Prawn::Document.new(page_size: "A4", margin: 40) do |pdf| + # Simple header + create_simple_header(pdf) + + # Event and ticket info in compact layout + create_ticket_info(pdf) + + # QR code section create_qr_section(pdf) - - # Footer with security elements - create_footer(pdf) - + + # Simple footer + create_simple_footer(pdf) + end.render end private - def create_background(pdf) - # Gradient background effect - pdf.fill_color "F8FAFC" - pdf.fill_rectangle [0, pdf.bounds.height], pdf.bounds.width, pdf.bounds.height - - # Top decorative band + def create_simple_header(pdf) + # Brand name pdf.fill_color "6366F1" - pdf.fill_rectangle [0, pdf.bounds.height], pdf.bounds.width, 120 - - # Subtle gradient effect - pdf.fill_color "8B5CF6" - pdf.fill_rectangle [0, pdf.bounds.height], pdf.bounds.width, 80 - end - - def create_header(pdf) - pdf.move_cursor_to(pdf.bounds.height - 30) - - # ApéroNight logo/brand - pdf.fill_color "FFFFFF" - pdf.font "Helvetica", style: :bold, size: 32 + pdf.font "Helvetica", style: :bold, size: 24 pdf.text "AperoNight", align: :center - - pdf.move_down 8 - pdf.font "Helvetica", size: 12 - pdf.fill_color "E2E8F0" - pdf.text "EVENEMENT TICKET", align: :center, character_spacing: 2 - end - - def create_event_card(pdf) - pdf.move_cursor_to(480) - - # Main event card with shadow effect - card_y = pdf.cursor - - # Shadow effect - pdf.fill_color "E2E8F0" - pdf.rounded_rectangle [22, card_y - 2], 356, 152, 15 - pdf.fill - - # Main card - pdf.fill_color "FFFFFF" - pdf.stroke_color "E5E7EB" - pdf.line_width 1 - pdf.rounded_rectangle [20, card_y], 360, 150, 15 - pdf.fill_and_stroke - - # Event name with accent - pdf.bounding_box([40, card_y - 20], width: 320, height: 110) do - pdf.fill_color "1F2937" - pdf.font "Helvetica", style: :bold, size: 20 - pdf.text ticket.event.name, align: :center - - pdf.move_down 15 - - # Event details grid - create_event_details_grid(pdf) - end - end - - def create_event_details_grid(pdf) - details = [ - { label: "DATE", value: ticket.event.start_time.strftime("%d %B %Y"), icon: "[CAL]" }, - { label: "HEURE", value: ticket.event.start_time.strftime("%H:%M"), icon: "[TIME]" }, - { label: "LIEU", value: ticket.event.venue_name, icon: "[LOC]" }, - { label: "TYPE", value: ticket.ticket_type.name, icon: "[TICK]" } - ] - + + pdf.move_down 5 pdf.font "Helvetica", size: 10 + pdf.fill_color "64748B" + pdf.text "Billet d'entree", align: :center + + pdf.move_down 20 + + # Simple divider line + pdf.stroke_color "E5E7EB" + pdf.horizontal_line 0, pdf.bounds.width + pdf.move_down 20 + end - details.each_slice(2).with_index do |row, row_index| - y_offset = row_index * 35 - - row.each_with_index do |detail, col_index| - x_offset = col_index * 160 - - pdf.bounding_box([x_offset, pdf.cursor - y_offset], width: 150, height: 30) do - # Icon and label - pdf.fill_color "6B7280" - pdf.font "Helvetica", style: :bold, size: 8 - pdf.text "#{detail[:icon]} #{detail[:label]}", character_spacing: 1 - - pdf.move_down 3 - - # Value - pdf.fill_color "1F2937" - pdf.font "Helvetica", style: :bold, size: 11 - pdf.text detail[:value] - end + def create_ticket_info(pdf) + # Event name - prominent + pdf.fill_color "1F2937" + pdf.font "Helvetica", style: :bold, size: 18 + pdf.text ticket.event.name, align: :center + pdf.move_down 15 + + # Two-column layout for ticket details + pdf.bounding_box([0, pdf.cursor], width: pdf.bounds.width, height: 120) do + # Left column + pdf.bounding_box([0, pdf.cursor], width: pdf.bounds.width / 2 - 20, height: 120) do + create_info_item(pdf, "Date", ticket.event.start_time.strftime("%d %B %Y")) + create_info_item(pdf, "Heure", ticket.event.start_time.strftime("%H:%M")) + create_info_item(pdf, "Lieu", ticket.event.venue_name) + end + + # Right column + pdf.bounding_box([pdf.bounds.width / 2 + 20, pdf.cursor], width: pdf.bounds.width / 2 - 20, height: 120) do + create_info_item(pdf, "Type", ticket.ticket_type.name) + create_info_item(pdf, "Prix", "#{sprintf('%.2f', ticket.price_euros)} EUR") + create_info_item(pdf, "Titulaire", "#{ticket.first_name} #{ticket.last_name}") end end + + pdf.move_down 30 end - def create_holder_section(pdf) - pdf.move_cursor_to(280) - - # Ticket holder section - pdf.bounding_box([20, pdf.cursor], width: 360, height: 60) do - # Section header with accent line - pdf.fill_color "6366F1" - pdf.fill_rectangle [0, pdf.cursor], 60, 2 - - pdf.move_down 10 - - pdf.fill_color "1F2937" - pdf.font "Helvetica", style: :bold, size: 12 - pdf.text "DETENTEUR DU BILLET", character_spacing: 1 - - pdf.move_down 8 - - # Holder name with elegant styling - pdf.font "Helvetica", style: :bold, size: 18 - pdf.fill_color "374151" - pdf.text "#{ticket.first_name.upcase} #{ticket.last_name.upcase}" - - pdf.move_down 5 - - # Price badge - create_price_badge(pdf) - end - end - - def create_price_badge(pdf) - price_text = "€#{sprintf('%.2f', ticket.price_euros)}" - - # Price badge background - pdf.fill_color "10B981" - pdf.rounded_rectangle [0, pdf.cursor], 80, 25, 12 - pdf.fill - - # Price text - pdf.fill_color "FFFFFF" - pdf.font "Helvetica", style: :bold, size: 12 - pdf.text_box price_text, at: [0, pdf.cursor], - width: 80, height: 25, - align: :center, valign: :center + def create_info_item(pdf, label, value) + pdf.font "Helvetica", style: :bold, size: 9 + pdf.fill_color "64748B" + pdf.text label.upcase + + pdf.move_down 2 + pdf.font "Helvetica", size: 11 + pdf.fill_color "1F2937" + pdf.text value + pdf.move_down 12 end def create_qr_section(pdf) - pdf.move_cursor_to(190) - - # QR Code section with modern card design - pdf.bounding_box([20, pdf.cursor], width: 360, height: 140) do - # QR background card - pdf.fill_color "F1F5F9" - pdf.stroke_color "E2E8F0" - pdf.rounded_rectangle [0, pdf.cursor], 360, 130, 15 - pdf.fill_and_stroke - + # Center the QR code horizontally + qr_size = 120 + x_position = (pdf.bounds.width - qr_size) / 2 + + pdf.bounding_box([x_position, pdf.cursor], width: qr_size, height: qr_size + 40) do # QR Code title - pdf.move_down 15 - pdf.fill_color "475569" pdf.font "Helvetica", style: :bold, size: 12 - pdf.text "CODE D'ENTREE", align: :center, character_spacing: 2 - + pdf.fill_color "1F2937" + pdf.text "Code d'entree", align: :center pdf.move_down 10 - - # Generate and place QR code - generate_qr_code(pdf) - + + # Generate QR code + generate_simple_qr_code(pdf, qr_size) + pdf.move_down 10 - + # QR code ID pdf.font "Helvetica", size: 8 pdf.fill_color "64748B" - pdf.text "ID: #{ticket.qr_code[0..11]}...", align: :center, character_spacing: 0.5 + pdf.text "ID: #{ticket.qr_code[0..15]}...", align: :center end + + pdf.move_down 40 end - def generate_qr_code(pdf) + def generate_simple_qr_code(pdf, size) # Ensure all required data is present before generating QR code if ticket.qr_code.blank? raise "Ticket QR code is missing" @@ -229,37 +139,31 @@ class TicketPdfGenerator raise "QR code data must be a valid string" end - # Create QR code with white background - pdf.bounding_box([130, pdf.cursor], width: 100, height: 100) do - pdf.fill_color "FFFFFF" - pdf.rounded_rectangle [0, pdf.cursor], 100, 100, 8 - pdf.fill - - # Generate QR code - pdf.print_qr_code(qr_code_data, extent: 85, align: :center) - end + # Generate QR code + pdf.print_qr_code(qr_code_data, extent: size, align: :center) end - def create_footer(pdf) - pdf.move_cursor_to(40) - + def create_simple_footer(pdf) # Security notice pdf.font "Helvetica", size: 8 - pdf.fill_color "6B7280" - pdf.text "[!] Ce billet est valable pour une seule entree", align: :center - pdf.text "Presentez ce billet a l'entree de l'evenement", align: :center - - pdf.move_down 8 - - # Generation timestamp with modern styling + pdf.fill_color "64748B" + pdf.text "Ce billet est valable pour une seule entree.", align: :center + pdf.text "Presentez ce code QR a l'entree de l'evenement.", align: :center + + pdf.move_down 10 + + # Divider line + pdf.stroke_color "E5E7EB" + pdf.horizontal_line 0, pdf.bounds.width + pdf.move_down 5 + + # Generation timestamp pdf.font "Helvetica", size: 7 pdf.fill_color "9CA3AF" timestamp = "Genere le #{Time.current.strftime('%d/%m/%Y a %H:%M')}" - pdf.text timestamp, align: :center, character_spacing: 0.3 + pdf.text timestamp, align: :center end - private - def build_qr_code_data(ticket) # Try multiple approaches to get valid QR code data begin @@ -289,4 +193,4 @@ class TicketPdfGenerator # Final fallback: simple ticket identifier "TICKET-#{ticket.id}" end -end +end \ No newline at end of file