4 Commits

Author SHA1 Message Date
kbe
2aae7fe8ea Merge branch 'feat/ticket' into develop 2025-09-06 21:31:34 +02:00
kbe
b8efa1e26d feat: Ticket ID now appears on PDF
- Promoters can now check the name and ID based on their dashboard
2025-09-06 21:29:41 +02:00
kbe
9e6c48dc5c refactor: Simplify PDF ticket design for single-page layout
- Switch to standard A4 page size with proper 40px margins
- Remove complex gradient backgrounds and card layouts for simplicity
- Implement clean, minimalist design with clear visual hierarchy
- Use two-column layout for efficient space utilization
- Center QR code with optimal 120px size for scanning
- Simplify typography with consistent font sizes and colors
- Remove unnecessary visual elements (shadows, rounded corners, badges)
- Ensure all content fits comfortably on single page
- Maintain brand colors (purple for header) with subtle styling
- Keep all essential information: event details, ticket holder, QR code
- Preserve robust error handling and QR code validation

Design improvements:
• Single-page layout that fits within A4 margins
• Clean two-column information display
• Simplified color scheme with good contrast
• Optimized spacing for readability
• Centered QR code for easy scanning
• Minimal but professional appearance

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-06 21:11:00 +02:00
kbe
6e3413a128 feat: Implement professional PDF ticket design with modern styling
- Redesign PDF layout with modern gradient background and card-based structure
- Add sophisticated color scheme using purple/indigo brand colors
- Implement visual hierarchy with improved typography and spacing
- Create information grid layout with labeled sections and visual indicators
- Add color-coded price badge with rounded corners and proper contrast
- Enhance QR code section with dedicated background card and better positioning
- Improve security elements and footer styling with professional appearance
- Increase ticket size to 400x650px for better readability and visual impact
- Fix encoding issues for French characters and special symbols compatibility
- Maintain all existing functionality while significantly improving visual design

New features:
• Modern gradient header with brand identity
• Card-based layout with subtle shadow effects
• Grid-based information layout with clear visual hierarchy
• Professional color coding and typography choices
• Enhanced QR code presentation with dedicated section
• Improved security messaging and timestamp styling

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-06 21:05:39 +02:00
4 changed files with 181 additions and 37 deletions

View File

@@ -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
@@ -20,14 +20,14 @@ class TicketPdfGenerator
# Header
pdf.fill_color "2D1B69"
pdf.font "Helvetica", style: :bold, size: 24
pdf.text "ApéroNight", align: :center
pdf.text ENV.fetch("APP_NAME", "Aperonight"), align: :center
pdf.move_down 10
# Event name
pdf.fill_color "000000"
pdf.font "Helvetica", style: :bold, size: 18
pdf.text ticket.event.name, align: :center
pdf.move_down 20
pdf.move_down 10
# Ticket info box
pdf.stroke_color "E5E7EB"
@@ -40,21 +40,29 @@ class TicketPdfGenerator
pdf.font "Helvetica", size: 12
# Customer name
pdf.text "Ticket Holder:", style: :bold
pdf.text "#{ticket.first_name} #{ticket.last_name}"
pdf.indent 10 do
pdf.text "Ticket Holder:", style: :bold
pdf.text "#{ticket.first_name} #{ticket.last_name}"
end
pdf.move_down 8
# Ticket details
pdf.text "Ticket Type:", style: :bold
pdf.text ticket.ticket_type.name
pdf.indent 10 do
pdf.text "Ticket Type:", style: :bold
pdf.text ticket.ticket_type.name
end
pdf.move_down 8
pdf.text "Price:", style: :bold
pdf.text "#{ticket.price_euros}"
pdf.indent 10 do
pdf.text "Price:", style: :bold
pdf.text "#{ticket.price_euros}"
end
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.indent 10 do
pdf.text "Date & Time:", style: :bold
pdf.text ticket.event.start_time.strftime("%B %d, %Y at %I:%M %p")
end
pdf.move_down 20
# Venue information
@@ -71,7 +79,7 @@ class TicketPdfGenerator
# QR Code
pdf.fill_color "000000"
pdf.font "Helvetica", style: :bold, size: 14
pdf.text "Ticket QR Code", align: :center
pdf.text "QR Code", align: :center
pdf.move_down 10
# Ensure all required data is present before generating QR code
@@ -102,13 +110,19 @@ class TicketPdfGenerator
# QR code text
pdf.font "Helvetica", size: 8
pdf.fill_color "6B7280"
pdf.text "QR Code: #{ticket.qr_code[0..7]}...", align: :center
pdf.text "#{ticket.qr_code}", align: :center
# Ticket ID
pdf.font "Helvetica", size: 8
pdf.fill_color "6B7280"
pdf.text "Ticket ID: #{ticket.id}", align: :center
# Footer
pdf.move_down 30
pdf.stroke_color "E5E7EB"
pdf.horizontal_line 0, 310
pdf.move_down 10
pdf.move_down 6
pdf.font "Helvetica", size: 8
pdf.fill_color "6B7280"
@@ -121,6 +135,136 @@ class TicketPdfGenerator
private
def create_simple_header(pdf)
# Brand name
pdf.fill_color "6366F1"
pdf.font "Helvetica", style: :bold, size: 24
pdf.text "AperoNight", align: :center
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
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_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)
# 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.font "Helvetica", style: :bold, size: 12
pdf.fill_color "1F2937"
pdf.text "Code d'entree", align: :center
pdf.move_down 10
# 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..15]}...", align: :center
end
pdf.move_down 40
end
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"
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
pdf.print_qr_code(qr_code_data, extent: size, align: :center)
end
def create_simple_footer(pdf)
# Security notice
pdf.font "Helvetica", size: 8
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
end
def build_qr_code_data(ticket)
# Try multiple approaches to get valid QR code data
begin

View File

@@ -84,7 +84,7 @@
</div>
<div class="flex items-center space-x-2">
<%= link_to download_ticket_path(ticket, format: :pdf),
<%= link_to ticket_download_path(ticket.qr_code, format: :pdf),
class: "inline-flex items-center px-4 py-2 bg-gradient-to-r from-purple-600 to-indigo-600 text-white rounded-lg hover:from-purple-700 hover:to-indigo-700 transition-all duration-200 text-sm font-medium shadow-sm" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>

View File

@@ -127,7 +127,7 @@
<p class="text-gray-600 text-sm mb-3">Gardez vos billets sur votre téléphone ou imprimez-les.</p>
<div class="space-y-2">
<% @order.tickets.each do |ticket| %>
<%= link_to download_ticket_path(ticket), class: "inline-flex items-center px-3 py-2 border border-purple-300 rounded-md text-sm font-medium text-purple-700 bg-purple-50 hover:bg-purple-100 transition-colors mr-2 mb-2" do %>
<%= link_to ticket_download_path(ticket.qr_code), class: "inline-flex items-center px-3 py-2 border border-purple-300 rounded-md text-sm font-medium text-purple-700 bg-purple-50 hover:bg-purple-100 transition-colors mr-2 mb-2" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>

View File

@@ -84,7 +84,7 @@
</div>
<div class="flex items-center space-x-2">
<%= link_to download_ticket_path(ticket, format: :pdf),
<%= link_to ticket_download_path(ticket.qr_code, format: :pdf),
class: "inline-flex items-center px-4 py-2 bg-gradient-to-r from-purple-600 to-indigo-600 text-white rounded-lg hover:from-purple-700 hover:to-indigo-700 transition-all duration-200 text-sm font-medium shadow-sm" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>