develop #3
@@ -16,107 +16,246 @@ class TicketPdfGenerator
|
||||
end
|
||||
|
||||
def generate
|
||||
Prawn::Document.new(page_size: [ 350, 600 ], margin: 20) do |pdf|
|
||||
# Header
|
||||
pdf.fill_color "2D1B69"
|
||||
pdf.font "Helvetica", style: :bold, size: 24
|
||||
pdf.text "ApéroNight", align: :center
|
||||
pdf.move_down 10
|
||||
Prawn::Document.new(page_size: [400, 650], margin: 0) do |pdf|
|
||||
# Main container with modern gradient background
|
||||
create_background(pdf)
|
||||
|
||||
# Event name
|
||||
pdf.fill_color "000000"
|
||||
pdf.font "Helvetica", style: :bold, size: 18
|
||||
# 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
|
||||
create_qr_section(pdf)
|
||||
|
||||
# Footer with security elements
|
||||
create_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
|
||||
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.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 20
|
||||
|
||||
# Ticket info box
|
||||
pdf.stroke_color "E5E7EB"
|
||||
pdf.fill_color "F9FAFB"
|
||||
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
|
||||
pdf.move_down 8
|
||||
|
||||
pdf.text "Price:", style: :bold
|
||||
pdf.text "€#{ticket.price_euros}"
|
||||
pdf.move_down 8
|
||||
|
||||
pdf.text "Date & Time:", style: :bold
|
||||
pdf.text ticket.event.start_time.strftime("%B %d, %Y at %I:%M %p")
|
||||
pdf.move_down 20
|
||||
|
||||
# Venue information
|
||||
pdf.fill_color "374151"
|
||||
pdf.font "Helvetica", style: :bold, size: 14
|
||||
pdf.text "Venue Information"
|
||||
pdf.move_down 8
|
||||
|
||||
pdf.font "Helvetica", size: 11
|
||||
pdf.text ticket.event.venue_name, style: :bold
|
||||
pdf.text ticket.event.venue_address
|
||||
pdf.move_down 20
|
||||
|
||||
# QR Code
|
||||
pdf.fill_color "000000"
|
||||
pdf.font "Helvetica", style: :bold, size: 14
|
||||
pdf.text "Ticket QR Code", align: :center
|
||||
pdf.move_down 10
|
||||
|
||||
# Ensure all required data is present before generating QR code
|
||||
if ticket.qr_code.blank?
|
||||
raise "Ticket QR code is missing"
|
||||
end
|
||||
|
||||
# Build QR code data with safe association loading
|
||||
qr_code_data = build_qr_code_data(ticket)
|
||||
|
||||
# Validate QR code data before creating QR code
|
||||
if qr_code_data.blank? || qr_code_data == "{}"
|
||||
Rails.logger.error "QR code data is empty: ticket_id=#{ticket.id}, qr_code=#{ticket.qr_code}, event_id=#{ticket.ticket_type&.event_id}, user_id=#{ticket.order&.user_id}"
|
||||
raise "QR code data is empty or invalid"
|
||||
end
|
||||
|
||||
# Ensure qr_code_data is a proper string for QR code generation
|
||||
unless qr_code_data.is_a?(String) && qr_code_data.length > 2
|
||||
Rails.logger.error "QR code data is not a valid string: #{qr_code_data.inspect} (class: #{qr_code_data.class})"
|
||||
raise "QR code data must be a valid string"
|
||||
end
|
||||
|
||||
# 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
|
||||
|
||||
# QR code text
|
||||
pdf.font "Helvetica", size: 8
|
||||
pdf.fill_color "6B7280"
|
||||
pdf.text "QR Code: #{ticket.qr_code[0..7]}...", align: :center
|
||||
# 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.font "Helvetica", size: 10
|
||||
|
||||
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
|
||||
end
|
||||
end
|
||||
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
|
||||
|
||||
# Footer
|
||||
pdf.move_down 30
|
||||
pdf.stroke_color "E5E7EB"
|
||||
pdf.horizontal_line 0, 310
|
||||
pdf.move_down 10
|
||||
|
||||
pdf.font "Helvetica", size: 8
|
||||
pdf.fill_color "6B7280"
|
||||
pdf.text "This ticket is valid for one entry only.", align: :center
|
||||
pdf.text "Present this ticket at the venue entrance.", align: :center
|
||||
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
|
||||
pdf.text "Generated on #{Time.current.strftime('%B %d, %Y at %I:%M %p')}", align: :center
|
||||
end.render
|
||||
|
||||
# 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
|
||||
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
|
||||
|
||||
# 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.move_down 10
|
||||
|
||||
# Generate and place QR code
|
||||
generate_qr_code(pdf)
|
||||
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
def generate_qr_code(pdf)
|
||||
# Ensure all required data is present before generating QR code
|
||||
if ticket.qr_code.blank?
|
||||
raise "Ticket QR code is missing"
|
||||
end
|
||||
|
||||
# Build QR code data with safe association loading
|
||||
qr_code_data = build_qr_code_data(ticket)
|
||||
|
||||
# Validate QR code data before creating QR code
|
||||
if qr_code_data.blank? || qr_code_data == "{}"
|
||||
Rails.logger.error "QR code data is empty: ticket_id=#{ticket.id}, qr_code=#{ticket.qr_code}, event_id=#{ticket.ticket_type&.event_id}, user_id=#{ticket.order&.user_id}"
|
||||
raise "QR code data is empty or invalid"
|
||||
end
|
||||
|
||||
# Ensure qr_code_data is a proper string for QR code generation
|
||||
unless qr_code_data.is_a?(String) && qr_code_data.length > 2
|
||||
Rails.logger.error "QR code data is not a valid string: #{qr_code_data.inspect} (class: #{qr_code_data.class})"
|
||||
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
|
||||
end
|
||||
|
||||
def create_footer(pdf)
|
||||
pdf.move_cursor_to(40)
|
||||
|
||||
# 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.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
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
Reference in New Issue
Block a user