From 15e3c7dff52ea1c42a0bf2ec601f72189b0ab238 Mon Sep 17 00:00:00 2001 From: kbe Date: Fri, 5 Sep 2025 12:02:44 +0200 Subject: [PATCH] style: correct coding style with rubocop linter --- QWEN.md | 28 -- README-checkout-implementation.md | 45 --- app/controllers/api/v1/events_controller.rb | 4 +- app/controllers/concerns/stripe_concern.rb | 4 +- app/controllers/events_controller.rb | 4 +- app/controllers/orders_controller.rb | 14 +- app/controllers/pages_controller.rb | 6 +- app/controllers/promoter/events_controller.rb | 32 +- .../promoter/ticket_types_controller.rb | 26 +- app/controllers/tickets_controller.rb | 10 +- app/helpers/application_helper.rb | 2 +- app/helpers/lucide_helper.rb | 50 +-- app/helpers/stripe_helper.rb | 6 +- app/jobs/cleanup_expired_drafts_job.rb | 4 +- app/jobs/expired_orders_cleanup_job.rb | 6 +- app/mailers/ticket_mailer.rb | 10 +- app/models/event.rb | 1 - app/models/order.rb | 2 +- app/models/ticket_type.rb | 8 +- app/services/ticket_pdf_generator.rb | 14 +- auth-messages-implementation-plan.md | 225 ----------- checkout-implementation-summary.md | 47 --- config/application.rb | 1 - config/initializers/devise.rb | 10 +- config/initializers/stripe.rb | 8 +- .../initializers/ticket_cleanup_scheduler.rb | 4 +- config/routes.rb | 2 +- .../20250816145933_devise_create_users.rb | 2 + db/migrate/20250823170409_create_orders.rb | 6 +- docs/application-optimization.md | 157 -------- docs/architecture.md | 363 ------------------ docs/branch-naming.md | 142 ------- docs/checkout-handle.md | 322 ---------------- docs/theme-rules.md.old | 103 ----- lib/tasks/tickets.rake | 4 +- stripe-fix-documentation.md | 47 --- stripe-lazy-initialization-documentation.md | 60 --- test/models/application_record_test.rb | 2 +- test/models/ticket_test.rb | 1 - test/models/user_test.rb | 2 +- 40 files changed, 121 insertions(+), 1663 deletions(-) delete mode 100755 QWEN.md delete mode 100755 README-checkout-implementation.md delete mode 100755 auth-messages-implementation-plan.md delete mode 100755 checkout-implementation-summary.md delete mode 100755 docs/application-optimization.md delete mode 100755 docs/architecture.md delete mode 100755 docs/branch-naming.md delete mode 100755 docs/checkout-handle.md delete mode 100755 docs/theme-rules.md.old delete mode 100644 stripe-fix-documentation.md delete mode 100644 stripe-lazy-initialization-documentation.md diff --git a/QWEN.md b/QWEN.md deleted file mode 100755 index 3eb79f0..0000000 --- a/QWEN.md +++ /dev/null @@ -1,28 +0,0 @@ -# Qwen Code Customization - -## Project Context -- Working on a Ruby on Rails project named "aperonight" -- Using Docker for containerization -- Following Ruby version 3.1.0 (as indicated by .ruby-version) -- Using Bundler for gem management (Gemfile) -- Using Node.js for frontend assets (package.json likely present) - -## Preferences -- Prefer to use Ruby and Rails conventions -- Follow Docker best practices for development environments -- Use standard Ruby/Rails project structure -- When creating new files, follow Rails conventions -- When modifying existing files, maintain consistency with current code style -- Use git for version control (as seen in .gitignore) -- Prefer to work with the project's existing toolchain (Bundler, etc.) - -## Behavior -- When asked to make changes, first understand the context by examining relevant files -- When creating new files, ensure they follow project conventions -- When modifying files, preserve existing code style and patterns -- When implementing new features, suggest appropriate file locations and naming conventions -- When debugging, suggest using the project's existing test suite and development tools -- When suggesting changes, provide clear explanations of why the change is beneficial - -## Qwen Added Memories -- We've implemented the checkout process with name collection for tickets that require identification. We've added first_name and last_name fields to the tickets table, updated the Ticket model with validations, added new routes and controller actions, created a view for collecting names, and updated the JavaScript controller. The database migration needs to be run in the Docker environment when the gem issues are resolved. diff --git a/README-checkout-implementation.md b/README-checkout-implementation.md deleted file mode 100755 index 61b8952..0000000 --- a/README-checkout-implementation.md +++ /dev/null @@ -1,45 +0,0 @@ -# Checkout Process Implementation - -This document describes the implementation of the checkout process with name collection for tickets that require identification. - -## Implementation Details - -The implementation includes: - -1. Database migration to add first_name and last_name fields to tickets -2. Updates to the Ticket model to validate names when required -3. New routes and controller actions for name collection -4. A new view for collecting ticket holder names -5. Updates to the existing JavaScript controller - -## Running the Migration - -Once the Docker environment is fixed, run the following command to apply the database migration: - -```bash -docker compose exec rails bundle exec rails db:migrate -``` - -## Testing the Implementation - -1. Start the Docker containers: - ```bash - docker compose up -d - ``` - -2. Visit an event page and select tickets that require identification -3. The checkout process should redirect to the name collection page -4. After submitting names, the user should be redirected to the payment page -5. After successful payment, tickets should be created with the provided names - -## Code Structure - -- Migration: `db/migrate/20250828143000_add_names_to_tickets.rb` -- Model: `app/models/ticket.rb` -- Controller: `app/controllers/events_controller.rb` -- Views: - - `app/views/events/collect_names.html.erb` (new) - - `app/views/events/show.html.erb` (updated) - - `app/views/components/_ticket_card.html.erb` (updated) -- Routes: `config/routes.rb` (updated) -- JavaScript: `app/javascript/controllers/ticket_cart_controller.js` (no changes needed) \ No newline at end of file diff --git a/app/controllers/api/v1/events_controller.rb b/app/controllers/api/v1/events_controller.rb index 399031a..e67d0b8 100755 --- a/app/controllers/api/v1/events_controller.rb +++ b/app/controllers/api/v1/events_controller.rb @@ -5,8 +5,8 @@ module Api module V1 class EventsController < ApiController # Skip API key authentication for store_cart action (used by frontend forms) - skip_before_action :authenticate_api_key, only: [:store_cart] - + skip_before_action :authenticate_api_key, only: [ :store_cart ] + # Charge l'évén avant certaines actions pour réduire les duplications before_action :set_event, only: [ :show, :update, :destroy, :store_cart ] diff --git a/app/controllers/concerns/stripe_concern.rb b/app/controllers/concerns/stripe_concern.rb index ff9b50f..d757349 100644 --- a/app/controllers/concerns/stripe_concern.rb +++ b/app/controllers/concerns/stripe_concern.rb @@ -10,9 +10,9 @@ module StripeConcern # but kept for backward compatibility def initialize_stripe return false unless stripe_configured? - + # Stripe is already initialized at application startup Rails.logger.debug "Stripe already initialized at application startup" true end -end \ No newline at end of file +end diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index baa812f..9491b4c 100755 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -3,8 +3,7 @@ # This controller manages all events. It load events for homepage # and display for pagination. class EventsController < ApplicationController - - before_action :authenticate_user!, only: [ ] + before_action :authenticate_user!, only: [] before_action :set_event, only: [ :show ] # Display all events @@ -27,5 +26,4 @@ class EventsController < ApplicationController def set_event @event = Event.includes(:ticket_types).find(params[:id]) end - end diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb index 3721ea2..eb2b39d 100644 --- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -4,12 +4,12 @@ # Orders group multiple tickets together for better transaction management class OrdersController < ApplicationController before_action :authenticate_user! - before_action :set_order, only: [:show, :checkout, :retry_payment, :increment_payment_attempt] - before_action :set_event, only: [:new, :create] + before_action :set_order, only: [ :show, :checkout, :retry_payment, :increment_payment_attempt ] + before_action :set_event, only: [ :new, :create ] # Display new order form with name collection # - # On this page user can see order summary and complete the tickets details + # On this page user can see order summary and complete the tickets details # (first name and last name) for each ticket ordered def new @cart_data = session[:pending_cart] || {} @@ -52,7 +52,7 @@ class OrdersController < ApplicationController end success = false - + ActiveRecord::Base.transaction do @order = current_user.orders.create!(event: @event, status: "draft") @@ -60,7 +60,7 @@ class OrdersController < ApplicationController next if ticket_attrs[:first_name].blank? || ticket_attrs[:last_name].blank? ticket_type = @event.ticket_types.find(ticket_attrs[:ticket_type_id]) - + ticket = @order.tickets.build( ticket_type: ticket_type, first_name: ticket_attrs[:first_name], @@ -82,7 +82,7 @@ class OrdersController < ApplicationController raise ActiveRecord::Rollback end end - + # Handle redirects outside transaction if success session[:draft_order_id] = @order.id @@ -260,7 +260,7 @@ class OrdersController < ApplicationController end Stripe::Checkout::Session.create( - payment_method_types: ["card"], + payment_method_types: [ "card" ], line_items: line_items, mode: "payment", success_url: order_payment_success_url + "?session_id={CHECKOUT_SESSION_ID}", diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index 38dca2d..56c346c 100755 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -20,8 +20,8 @@ class PagesController < ApplicationController # Metrics for dashboard cards @booked_events = current_user.orders.joins(tickets: { ticket_type: :event }) .where(events: { state: :published }) - .where(orders: { status: ['paid', 'completed'] }) - .sum('1') + .where(orders: { status: [ "paid", "completed" ] }) + .sum("1") @events_today = Event.published.where("DATE(start_time) = ?", Date.current).count @events_tomorrow = Event.published.where("DATE(start_time) = ?", Date.current + 1).count @upcoming_events = Event.published.upcoming.count @@ -33,7 +33,7 @@ class PagesController < ApplicationController .limit(5) # Draft orders that can be retried - @draft_orders = current_user.orders.includes(tickets: [:ticket_type, :event]) + @draft_orders = current_user.orders.includes(tickets: [ :ticket_type, :event ]) .can_retry_payment .order(:expires_at) diff --git a/app/controllers/promoter/events_controller.rb b/app/controllers/promoter/events_controller.rb index 3f2deda..ac68538 100644 --- a/app/controllers/promoter/events_controller.rb +++ b/app/controllers/promoter/events_controller.rb @@ -5,7 +5,7 @@ class Promoter::EventsController < ApplicationController before_action :authenticate_user! before_action :ensure_can_manage_events! - before_action :set_event, only: [:show, :edit, :update, :destroy, :publish, :unpublish, :cancel, :mark_sold_out] + before_action :set_event, only: [ :show, :edit, :update, :destroy, :publish, :unpublish, :cancel, :mark_sold_out ] # Display all events for the current promoter def index @@ -25,9 +25,9 @@ class Promoter::EventsController < ApplicationController # Create a new event def create @event = current_user.events.build(event_params) - + if @event.save - redirect_to promoter_event_path(@event), notice: 'Event créé avec succès!' + redirect_to promoter_event_path(@event), notice: "Event créé avec succès!" else render :new, status: :unprocessable_entity end @@ -41,7 +41,7 @@ class Promoter::EventsController < ApplicationController # Update an existing event def update if @event.update(event_params) - redirect_to promoter_event_path(@event), notice: 'Event mis à jour avec succès!' + redirect_to promoter_event_path(@event), notice: "Event mis à jour avec succès!" else render :edit, status: :unprocessable_entity end @@ -50,16 +50,16 @@ class Promoter::EventsController < ApplicationController # Delete an event def destroy @event.destroy - redirect_to promoter_events_path, notice: 'Event supprimé avec succès!' + redirect_to promoter_events_path, notice: "Event supprimé avec succès!" end # Publish an event (make it visible to public) def publish if @event.draft? @event.update(state: :published) - redirect_to promoter_event_path(@event), notice: 'Event publié avec succès!' + redirect_to promoter_event_path(@event), notice: "Event publié avec succès!" else - redirect_to promoter_event_path(@event), alert: 'Cet event ne peut pas être publié.' + redirect_to promoter_event_path(@event), alert: "Cet event ne peut pas être publié." end end @@ -67,9 +67,9 @@ class Promoter::EventsController < ApplicationController def unpublish if @event.published? @event.update(state: :draft) - redirect_to promoter_event_path(@event), notice: 'Event dépublié avec succès!' + redirect_to promoter_event_path(@event), notice: "Event dépublié avec succès!" else - redirect_to promoter_event_path(@event), alert: 'Cet event ne peut pas être dépublié.' + redirect_to promoter_event_path(@event), alert: "Cet event ne peut pas être dépublié." end end @@ -77,9 +77,9 @@ class Promoter::EventsController < ApplicationController def cancel if @event.published? @event.update(state: :canceled) - redirect_to promoter_event_path(@event), notice: 'Event annulé avec succès!' + redirect_to promoter_event_path(@event), notice: "Event annulé avec succès!" else - redirect_to promoter_event_path(@event), alert: 'Cet event ne peut pas être annulé.' + redirect_to promoter_event_path(@event), alert: "Cet event ne peut pas être annulé." end end @@ -87,9 +87,9 @@ class Promoter::EventsController < ApplicationController def mark_sold_out if @event.published? @event.update(state: :sold_out) - redirect_to promoter_event_path(@event), notice: 'Event marqué comme complet!' + redirect_to promoter_event_path(@event), notice: "Event marqué comme complet!" else - redirect_to promoter_event_path(@event), alert: 'Cet event ne peut pas être marqué comme complet.' + redirect_to promoter_event_path(@event), alert: "Cet event ne peut pas être marqué comme complet." end end @@ -97,14 +97,14 @@ class Promoter::EventsController < ApplicationController def ensure_can_manage_events! unless current_user.can_manage_events? - redirect_to dashboard_path, alert: 'Vous n\'avez pas les permissions nécessaires pour gérer des événements.' + redirect_to dashboard_path, alert: "Vous n'avez pas les permissions nécessaires pour gérer des événements." end end def set_event @event = current_user.events.find(params[:id]) rescue ActiveRecord::RecordNotFound - redirect_to promoter_events_path, alert: 'Event non trouvé ou vous n\'avez pas accès à cet event.' + redirect_to promoter_events_path, alert: "Event non trouvé ou vous n'avez pas accès à cet event." end def event_params @@ -114,4 +114,4 @@ class Promoter::EventsController < ApplicationController :start_time, :end_time, :featured ) end -end \ No newline at end of file +end diff --git a/app/controllers/promoter/ticket_types_controller.rb b/app/controllers/promoter/ticket_types_controller.rb index 8a5b57b..b97411e 100644 --- a/app/controllers/promoter/ticket_types_controller.rb +++ b/app/controllers/promoter/ticket_types_controller.rb @@ -6,7 +6,7 @@ class Promoter::TicketTypesController < ApplicationController before_action :authenticate_user! before_action :ensure_can_manage_events! before_action :set_event - before_action :set_ticket_type, only: [:show, :edit, :update, :destroy] + before_action :set_ticket_type, only: [ :show, :edit, :update, :destroy ] # Display all ticket types for an event def index @@ -30,9 +30,9 @@ class Promoter::TicketTypesController < ApplicationController # Create a new ticket type def create @ticket_type = @event.ticket_types.build(ticket_type_params) - + if @ticket_type.save - redirect_to promoter_event_ticket_types_path(@event), notice: 'Type de billet créé avec succès!' + redirect_to promoter_event_ticket_types_path(@event), notice: "Type de billet créé avec succès!" else render :new, status: :unprocessable_entity end @@ -46,7 +46,7 @@ class Promoter::TicketTypesController < ApplicationController # Update an existing ticket type def update if @ticket_type.update(ticket_type_params) - redirect_to promoter_event_ticket_type_path(@event, @ticket_type), notice: 'Type de billet mis à jour avec succès!' + redirect_to promoter_event_ticket_type_path(@event, @ticket_type), notice: "Type de billet mis à jour avec succès!" else render :edit, status: :unprocessable_entity end @@ -55,10 +55,10 @@ class Promoter::TicketTypesController < ApplicationController # Delete a ticket type def destroy if @ticket_type.tickets.any? - redirect_to promoter_event_ticket_types_path(@event), alert: 'Impossible de supprimer ce type de billet car des billets ont déjà été vendus.' + redirect_to promoter_event_ticket_types_path(@event), alert: "Impossible de supprimer ce type de billet car des billets ont déjà été vendus." else @ticket_type.destroy - redirect_to promoter_event_ticket_types_path(@event), notice: 'Type de billet supprimé avec succès!' + redirect_to promoter_event_ticket_types_path(@event), notice: "Type de billet supprimé avec succès!" end end @@ -67,11 +67,11 @@ class Promoter::TicketTypesController < ApplicationController original = @event.ticket_types.find(params[:id]) @ticket_type = original.dup @ticket_type.name = "#{original.name} (Copie)" - + if @ticket_type.save - redirect_to edit_promoter_event_ticket_type_path(@event, @ticket_type), notice: 'Type de billet dupliqué avec succès!' + redirect_to edit_promoter_event_ticket_type_path(@event, @ticket_type), notice: "Type de billet dupliqué avec succès!" else - redirect_to promoter_event_ticket_types_path(@event), alert: 'Erreur lors de la duplication.' + redirect_to promoter_event_ticket_types_path(@event), alert: "Erreur lors de la duplication." end end @@ -79,20 +79,20 @@ class Promoter::TicketTypesController < ApplicationController def ensure_can_manage_events! unless current_user.can_manage_events? - redirect_to dashboard_path, alert: 'Vous n\'avez pas les permissions nécessaires pour gérer des événements.' + redirect_to dashboard_path, alert: "Vous n'avez pas les permissions nécessaires pour gérer des événements." end end def set_event @event = current_user.events.find(params[:event_id]) rescue ActiveRecord::RecordNotFound - redirect_to promoter_events_path, alert: 'Event non trouvé ou vous n\'avez pas accès à cet event.' + redirect_to promoter_events_path, alert: "Event non trouvé ou vous n'avez pas accès à cet event." end def set_ticket_type @ticket_type = @event.ticket_types.find(params[:id]) rescue ActiveRecord::RecordNotFound - redirect_to promoter_event_ticket_types_path(@event), alert: 'Type de billet non trouvé.' + redirect_to promoter_event_ticket_types_path(@event), alert: "Type de billet non trouvé." end def ticket_type_params @@ -101,4 +101,4 @@ class Promoter::TicketTypesController < ApplicationController :sale_start_at, :sale_end_at, :minimum_age, :requires_id ) end -end \ No newline at end of file +end diff --git a/app/controllers/tickets_controller.rb b/app/controllers/tickets_controller.rb index e3cc048..f60b255 100644 --- a/app/controllers/tickets_controller.rb +++ b/app/controllers/tickets_controller.rb @@ -36,10 +36,10 @@ class TicketsController < ApplicationController # Redirect retry payment to order system def retry_payment @event = Event.includes(:ticket_types).find(params[:id]) - + # Look for draft order for this event order = current_user.orders.find_by(event: @event, status: "draft") - + if order&.can_retry_payment? redirect_to retry_payment_order_path(order) else @@ -58,15 +58,15 @@ class TicketsController < ApplicationController def set_event event_id = params[:id] || session[:event_id] - + Rails.logger.debug "TicketsController#set_event - params[:id]: #{params[:id].inspect}, session[:event_id]: #{session[:event_id].inspect}" - + unless event_id Rails.logger.error "TicketsController#set_event - No event ID found" redirect_to events_path, alert: "Aucun événement spécifié" return end - + @event = Event.includes(:ticket_types).find(event_id) Rails.logger.debug "TicketsController#set_event - Found event: #{@event.id} - #{@event.name}" rescue ActiveRecord::RecordNotFound diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index d946b5c..fcaad19 100755 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -6,7 +6,7 @@ module ApplicationHelper # Include flash message helpers include FlashMessagesHelper - + # Include Stripe helper include StripeHelper end diff --git a/app/helpers/lucide_helper.rb b/app/helpers/lucide_helper.rb index cb37661..7e61db7 100644 --- a/app/helpers/lucide_helper.rb +++ b/app/helpers/lucide_helper.rb @@ -1,28 +1,28 @@ module LucideHelper # Create a Lucide icon element - # + # # @param name [String] The name of the Lucide icon # @param options [Hash] Additional options # @option options [String] :class Additional CSS classes # @option options [String] :size Size class (e.g., 'w-4 h-4', 'w-6 h-6') # @option options [Hash] :data Additional data attributes - # + # # @return [String] HTML string for the icon # # Usage: - # lucide_icon('user') + # lucide_icon('user') # lucide_icon('check-circle', class: 'text-green-500', size: 'w-5 h-5') # lucide_icon('menu', data: { action: 'click->header#toggleMenu' }) def lucide_icon(name, options = {}) - css_classes = ["lucide-icon"] + css_classes = [ "lucide-icon" ] css_classes << options[:size] if options[:size] css_classes << options[:class] if options[:class] - + data_attributes = { lucide: name } data_attributes.merge!(options[:data]) if options[:data] - - content_tag :i, "", - class: css_classes.join(" "), + + content_tag :i, "", + class: css_classes.join(" "), data: data_attributes, **options.except(:class, :size, :data) end @@ -35,23 +35,23 @@ module LucideHelper # @option options [String] :class Additional CSS classes for button # @option options [String] :icon_class Additional CSS classes for icon # @option options [String] :icon_size Size class for icon - # + # # Usage: # lucide_button('plus', text: 'Add Item', class: 'btn btn-primary') # lucide_button('trash-2', class: 'btn-danger', data: { confirm: 'Are you sure?' }) def lucide_button(name, options = {}) text = options.delete(:text) icon_class = options.delete(:icon_class) - icon_size = options.delete(:icon_size) || 'w-4 h-4' - + icon_size = options.delete(:icon_size) || "w-4 h-4" + icon = lucide_icon(name, class: icon_class, size: icon_size) - + content = if text.present? - safe_join([icon, " ", text]) - else + safe_join([ icon, " ", text ]) + else icon - end - + end + content_tag :button, content, options end @@ -60,23 +60,23 @@ module LucideHelper # @param name [String] The name of the Lucide icon # @param url [String] The URL for the link # @param options [Hash] Link options - # + # # Usage: # lucide_link('edit', edit_user_path(user), text: 'Edit') # lucide_link('external-link', 'https://example.com', text: 'Visit', target: '_blank') def lucide_link(name, url, options = {}) text = options.delete(:text) icon_class = options.delete(:icon_class) - icon_size = options.delete(:icon_size) || 'w-4 h-4' - + icon_size = options.delete(:icon_size) || "w-4 h-4" + icon = lucide_icon(name, class: icon_class, size: icon_size) - + content = if text.present? - safe_join([icon, " ", text]) - else + safe_join([ icon, " ", text ]) + else icon - end - + end + link_to content, url, options end -end \ No newline at end of file +end diff --git a/app/helpers/stripe_helper.rb b/app/helpers/stripe_helper.rb index f65da63..9d6bd84 100644 --- a/app/helpers/stripe_helper.rb +++ b/app/helpers/stripe_helper.rb @@ -3,10 +3,10 @@ module StripeHelper def safe_stripe_call(&block) # Check if Stripe is properly configured return nil unless Rails.application.config.stripe[:secret_key].present? - + # Stripe is now initialized at application startup Rails.logger.debug "Using globally initialized Stripe" - + begin yield if block_given? rescue Stripe::StripeError => e @@ -14,4 +14,4 @@ module StripeHelper nil end end -end \ No newline at end of file +end diff --git a/app/jobs/cleanup_expired_drafts_job.rb b/app/jobs/cleanup_expired_drafts_job.rb index c7d5a29..e2dcda8 100644 --- a/app/jobs/cleanup_expired_drafts_job.rb +++ b/app/jobs/cleanup_expired_drafts_job.rb @@ -3,7 +3,7 @@ class CleanupExpiredDraftsJob < ApplicationJob def perform expired_count = 0 - + Ticket.expired_drafts.find_each do |ticket| Rails.logger.info "Expiring draft ticket #{ticket.id} for user #{ticket.user_id}" ticket.expire_if_overdue! @@ -12,4 +12,4 @@ class CleanupExpiredDraftsJob < ApplicationJob Rails.logger.info "Expired #{expired_count} draft tickets" if expired_count > 0 end -end \ No newline at end of file +end diff --git a/app/jobs/expired_orders_cleanup_job.rb b/app/jobs/expired_orders_cleanup_job.rb index 1040cc6..a2b868e 100644 --- a/app/jobs/expired_orders_cleanup_job.rb +++ b/app/jobs/expired_orders_cleanup_job.rb @@ -4,9 +4,9 @@ class ExpiredOrdersCleanupJob < ApplicationJob def perform # Find and expire all draft orders that have passed their expiry time expired_orders = Order.expired_drafts - + Rails.logger.info "Found #{expired_orders.count} expired orders to process" - + expired_orders.find_each do |order| begin order.expire_if_overdue! @@ -17,7 +17,7 @@ class ExpiredOrdersCleanupJob < ApplicationJob next end end - + Rails.logger.info "Completed expired orders cleanup job" end end diff --git a/app/mailers/ticket_mailer.rb b/app/mailers/ticket_mailer.rb index 9320020..2e1c698 100755 --- a/app/mailers/ticket_mailer.rb +++ b/app/mailers/ticket_mailer.rb @@ -1,21 +1,21 @@ class TicketMailer < ApplicationMailer - default from: 'notifications@aperonight.com' + default from: "notifications@aperonight.com" def purchase_confirmation(ticket) @ticket = ticket @user = ticket.user @event = ticket.event - + # Generate PDF attachment pdf = @ticket.to_pdf attachments["ticket-#{@event.name.parameterize}-#{@ticket.qr_code[0..7]}.pdf"] = { - mime_type: 'application/pdf', + mime_type: "application/pdf", content: pdf } - + mail( to: @user.email, subject: "Confirmation d'achat - #{@event.name}" ) end -end \ No newline at end of file +end diff --git a/app/models/event.rb b/app/models/event.rb index f6fa08f..e036a7a 100755 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -48,5 +48,4 @@ class Event < ApplicationRecord # Scope for published events ordered by start time scope :upcoming, -> { published.where("start_time >= ?", Time.current).order(start_time: :asc) } - end diff --git a/app/models/order.rb b/app/models/order.rb index 9a6004c..1ec1600 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -90,4 +90,4 @@ class Order < ApplicationRecord def draft? status == "draft" end -end \ No newline at end of file +end diff --git a/app/models/ticket_type.rb b/app/models/ticket_type.rb index d3e8073..2bca6cd 100755 --- a/app/models/ticket_type.rb +++ b/app/models/ticket_type.rb @@ -12,7 +12,7 @@ class TicketType < ApplicationRecord validates :sale_end_at, presence: true validates :minimum_age, numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 120 }, allow_nil: true validates :event_id, presence: true - validates :requires_id, inclusion: { in: [true, false] } + validates :requires_id, inclusion: { in: [ true, false ] } # Custom validations validate :sale_end_after_start @@ -22,7 +22,7 @@ class TicketType < ApplicationRecord scope :available_now, -> { where("sale_start_at <= ? AND sale_end_at >= ?", Time.current, Time.current) } scope :upcoming, -> { where("sale_start_at > ?", Time.current) } scope :expired, -> { where("sale_end_at < ?", Time.current) } - + # Helper methods def price_euros return 0.0 if price_cents.nil? @@ -45,7 +45,7 @@ class TicketType < ApplicationRecord def available_quantity return 0 if quantity.nil? - [quantity - tickets.count, 0].max + [ quantity - tickets.count, 0 ].max end def sales_status @@ -53,7 +53,7 @@ class TicketType < ApplicationRecord return :expired if sale_end_at < Time.current return :upcoming if sale_start_at > Time.current return :sold_out if sold_out? - return :available + :available end def total_potential_revenue diff --git a/app/services/ticket_pdf_generator.rb b/app/services/ticket_pdf_generator.rb index a513b33..a5e6221 100755 --- a/app/services/ticket_pdf_generator.rb +++ b/app/services/ticket_pdf_generator.rb @@ -1,6 +1,6 @@ -require 'prawn' -require 'prawn/qrcode' -require 'rqrcode' +require "prawn" +require "prawn/qrcode" +require "rqrcode" class TicketPdfGenerator attr_reader :ticket @@ -10,7 +10,7 @@ class TicketPdfGenerator end def generate - Prawn::Document.new(page_size: [350, 600], margin: 20) do |pdf| + Prawn::Document.new(page_size: [ 350, 600 ], margin: 20) do |pdf| # Header pdf.fill_color "2D1B69" pdf.font "Helvetica", style: :bold, size: 24 @@ -26,7 +26,7 @@ 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, 120, 10 pdf.fill_and_stroke pdf.move_down 10 @@ -82,7 +82,7 @@ class TicketPdfGenerator qrcode = RQRCode::QRCode.new(qr_code_data) pdf.print_qr_code(qrcode, extent: 120, align: :center) - + pdf.move_down 15 # QR code text @@ -104,4 +104,4 @@ class TicketPdfGenerator pdf.text "Generated on #{Time.current.strftime('%B %d, %Y at %I:%M %p')}", align: :center end.render end -end \ No newline at end of file +end diff --git a/auth-messages-implementation-plan.md b/auth-messages-implementation-plan.md deleted file mode 100755 index 9f85b29..0000000 --- a/auth-messages-implementation-plan.md +++ /dev/null @@ -1,225 +0,0 @@ -# Authentication Messages Implementation Plan - -## Overview -This document outlines the implementation of error/warn/info messages for login, registration, logout, password reset, and other authentication flows based on the existing purple/pink theme. - -## Current State Analysis -- **Theme**: Purple/pink gradient system with neutral colors -- **Authentication**: Devise with custom controllers -- **Missing**: Flash message display system -- **Existing**: Only form validation errors are displayed - -## Implementation Steps - -### 1. Flash Message Component -Create a reusable flash message component that integrates with the theme. - -### 2. CSS Classes for Message Types -Add theme-consistent styles for different message types: -- Success (green/purple) -- Error (red) -- Warning (yellow/orange) -- Info (blue) - -### 3. JavaScript Enhancement -Add auto-dismiss functionality and animations - -### 4. Integration -Update layouts and views to use the new message system - -## Files to Create/Update - -### A. Flash Message Partial -**File**: `app/views/shared/_flash_messages.html.erb` -```erb -<% flash.each do |type, message| %> - <% if message.present? %> -
-
-
- <%= flash_icon(type) %> -
-
-

<%= message %>

-
-
- -
-
-
- <% end %> -<% end %> -``` - -### B. Flash Message Styling -**File**: `app/assets/stylesheets/components/flash.css` -```css -/* Flash Messages - Theme Integration */ -.flash-message { - @apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mb-4; -} - -.flash-message .flash-container { - @apply rounded-lg p-4 shadow-md border; -} - -.flash-message-success .flash-container { - @apply bg-gradient-to-r from-green-50 to-purple-50 border-green-200 text-green-800; -} - -.flash-message-error .flash-container { - @apply bg-gradient-to-r from-red-50 to-pink-50 border-red-200 text-red-800; -} - -.flash-message-warning .flash-container { - @apply bg-gradient-to-r from-yellow-50 to-orange-50 border-yellow-200 text-yellow-800; -} - -.flash-message-info .flash-container { - @apply bg-gradient-to-r from-blue-50 to-purple-50 border-blue-200 text-blue-800; -} - -.flash-message-notice .flash-container { - @apply bg-gradient-to-r from-purple-50 to-pink-50 border-purple-200 text-purple-800; -} - -.flash-message-alert .flash-container { - @apply bg-gradient-to-r from-red-50 to-pink-50 border-red-200 text-red-800; -} -``` - -### C. Helper Methods -**File**: `app/helpers/flash_messages_helper.rb` -```ruby -module FlashMessagesHelper - def flash_class(type) - case type.to_s - when 'notice' then 'flash-message-success' - when 'success' then 'flash-message-success' - when 'error' then 'flash-message-error' - when 'alert' then 'flash-message-error' - when 'warning' then 'flash-message-warning' - when 'info' then 'flash-message-info' - else "flash-message-#{type}" - end - end - - def flash_icon(type) - case type.to_s - when 'notice', 'success' - content_tag :svg, class: "h-5 w-5 text-green-400", fill: "currentColor", viewBox: "0 0 20 20" do - content_tag :path, "", "fill-rule": "evenodd", "d": "M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z", "clip-rule": "evenodd" - end - when 'error', 'alert' - content_tag :svg, class: "h-5 w-5 text-red-400", fill: "currentColor", viewBox: "0 0 20 20" do - content_tag :path, "", "fill-rule": "evenodd", "d": "M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z", "clip-rule": "evenodd" - end - when 'warning' - content_tag :svg, class: "h-5 w-5 text-yellow-400", fill: "currentColor", viewBox: "0 0 20 20" do - content_tag :path, "", "fill-rule": "evenodd", "d": "M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z", "clip-rule": "evenodd" - end - else - content_tag :svg, class: "h-5 w-5 text-blue-400", fill: "currentColor", viewBox: "0 0 20 20" do - content_tag :path, "", "fill-rule": "evenodd", "d": "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z", "clip-rule": "evenodd" - end - end - end -end -``` - -### D. JavaScript Controller -**File**: `app/javascript/controllers/flash_message_controller.js` -```javascript -import { Controller } from "@hotwired/stimulus" - -export default class extends Controller { - static targets = ["message"] - - connect() { - // Auto-dismiss after 5 seconds - this.timeout = setTimeout(() => { - this.close() - }, 5000) - } - - disconnect() { - if (this.timeout) { - clearTimeout(this.timeout) - } - } - - close() { - this.element.classList.add('opacity-0', 'transition-opacity', 'duration-300') - setTimeout(() => { - this.element.remove() - }, 300) - } -} -``` - -### E. Update Application Layout -**File**: `app/views/layouts/application.html.erb` (add flash messages) -```erb - -
- <%= render "components/header" %> - -
- <%= render "shared/flash_messages" %> - <%= yield %> -
- - <%= render "components/footer" %> -
- -``` - -### F. Update Authentication Views -Update all Devise views to remove the old error display and rely on flash messages. - -## Testing Checklist - -### Authentication Flows to Test: -1. **Registration** - - Successful registration - - Registration with validation errors - - Email confirmation - -2. **Login** - - Successful login - - Invalid credentials - - Account locked/unconfirmed - -3. **Password Reset** - - Request reset email - - Reset password success/failure - -4. **Account Management** - - Update profile - - Change password - - Delete account - -### Message Types to Verify: -- [ ] Success messages (green/purple) -- [ ] Error messages (red/pink) -- [ ] Warning messages (yellow/orange) -- [ ] Info messages (blue/purple) - -## Implementation Order -1. Create CSS classes and theme integration -2. Create helper methods -3. Create partial templates -4. Add to application layout -5. Test each authentication flow -6. Add JavaScript enhancements - -## Notes -- All messages use the existing purple/pink theme colors -- Responsive design for mobile/desktop -- Auto-dismiss functionality with manual close option -- Smooth animations and transitions -- Accessibility compliant with focus indicators diff --git a/checkout-implementation-summary.md b/checkout-implementation-summary.md deleted file mode 100755 index 009b82e..0000000 --- a/checkout-implementation-summary.md +++ /dev/null @@ -1,47 +0,0 @@ -# Checkout Process Implementation Summary - -## Database Changes - -1. **Migration**: Added `first_name` and `last_name` fields to the `tickets` table - - File: `db/migrate/20250828143000_add_names_to_tickets.rb` - -## Model Changes - -1. **Ticket Model**: - - Added validations for `first_name` and `last_name` when required by ticket type - - Added `requires_names?` method to check if names are required based on ticket type - -## Controller Changes - -1. **Events Controller**: - - Modified `checkout` action to redirect to name collection when tickets require names - - Added `collect_names` action to display form for collecting ticket holder names - - Added `process_names` action to handle submitted names and proceed to payment - - Updated `payment_success` action to create tickets with names when provided - -## View Changes - -1. **Events Show View**: - - Added `change` event listener to quantity inputs in ticket cards - -2. **Ticket Card Component**: - - Added `change` event listener to quantity inputs - -3. **New View**: - - Created `app/views/events/collect_names.html.erb` for collecting ticket holder names - -## Route Changes - -1. **New Routes**: - - `GET events/:slug.:id/names` - Collect names for tickets requiring identification - - `POST events/:slug.:id/names` - Process submitted names and proceed to payment - -## JavaScript Changes - -1. **Ticket Cart Controller**: - - No changes needed as name collection is handled server-side - -## Outstanding Tasks - -1. Run the database migration in the Docker environment once gem issues are resolved -2. Test the complete checkout flow with name collection \ No newline at end of file diff --git a/config/application.rb b/config/application.rb index a09e088..227f091 100755 --- a/config/application.rb +++ b/config/application.rb @@ -26,6 +26,5 @@ module Aperonight config.i18n.load_path += Dir[Rails.root.join("my", "locales", "*.{rb,yml}")] # config.i18n.default_locale = :fr - end end diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 50f3fbe..96d8082 100755 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -24,7 +24,7 @@ Devise.setup do |config| # Configure the e-mail address which will be shown in Devise::Mailer, # note that it will be overwritten if you use your own mailer class # with default "from" parameter. - config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com' + config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" # Configure the class responsible to send e-mails. # config.mailer = 'Devise::Mailer' @@ -36,7 +36,7 @@ Devise.setup do |config| # Load and configure the ORM. Supports :active_record (default) and # :mongoid (bson_ext recommended) by default. Other ORMs may be # available as additional gems. - require 'devise/orm/active_record' + require "devise/orm/active_record" # ==> Configuration for any authentication mechanism # Configure which keys are used when authenticating a user. The default is @@ -58,12 +58,12 @@ Devise.setup do |config| # Configure which authentication keys should be case-insensitive. # These keys will be downcased upon creating or modifying a user and when used # to authenticate or find a user. Default is :email. - config.case_insensitive_keys = [:email] + config.case_insensitive_keys = [ :email ] # Configure which authentication keys should have whitespace stripped. # These keys will have whitespace before and after removed upon creating or # modifying a user and when used to authenticate or find a user. Default is :email. - config.strip_whitespace_keys = [:email] + config.strip_whitespace_keys = [ :email ] # Tell if authentication through request.params is enabled. True by default. # It can be set to an array that will enable params authentication only for the @@ -97,7 +97,7 @@ Devise.setup do |config| # Notice that if you are skipping storage for all authentication paths, you # may want to disable generating routes to Devise's sessions controller by # passing skip: :sessions to `devise_for` in your config/routes.rb - config.skip_session_storage = [:http_auth] + config.skip_session_storage = [ :http_auth ] # By default, Devise cleans up the CSRF token on authentication to # avoid CSRF token fixation attacks. This means that, when using AJAX diff --git a/config/initializers/stripe.rb b/config/initializers/stripe.rb index 7d260ae..0b0d2b5 100755 --- a/config/initializers/stripe.rb +++ b/config/initializers/stripe.rb @@ -1,10 +1,10 @@ Rails.application.configure do # Load environment variables from .env file if dotenv is not available - env_file = Rails.root.join('.env') + env_file = Rails.root.join(".env") if File.exist?(env_file) && !defined?(Dotenv) File.readlines(env_file).each do |line| - next if line.strip.empty? || line.start_with?('#') - key, value = line.split('=', 2) + next if line.strip.empty? || line.start_with?("#") + key, value = line.split("=", 2) ENV[key.strip] = value.strip if key && value end end @@ -37,4 +37,4 @@ Rails.application.configure do else Rails.logger.warn "Stripe secret key not found - Stripe will not be initialized" end -end \ No newline at end of file +end diff --git a/config/initializers/ticket_cleanup_scheduler.rb b/config/initializers/ticket_cleanup_scheduler.rb index ba1e8d7..a13f870 100644 --- a/config/initializers/ticket_cleanup_scheduler.rb +++ b/config/initializers/ticket_cleanup_scheduler.rb @@ -15,9 +15,9 @@ Rails.application.config.after_initialize do rescue => e Rails.logger.error "Failed to schedule expired drafts cleanup: #{e.message}" end - + sleep 10.minutes end end end -end \ No newline at end of file +end diff --git a/config/routes.rb b/config/routes.rb index e48b4a8..88ad1ea 100755 --- a/config/routes.rb +++ b/config/routes.rb @@ -42,7 +42,7 @@ Rails.application.routes.draw do 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" - resources :orders, only: [:show] do + resources :orders, only: [ :show ] do member do get :checkout post :retry_payment diff --git a/db/migrate/20250816145933_devise_create_users.rb b/db/migrate/20250816145933_devise_create_users.rb index d96ab0a..4075e7f 100755 --- a/db/migrate/20250816145933_devise_create_users.rb +++ b/db/migrate/20250816145933_devise_create_users.rb @@ -43,6 +43,8 @@ class DeviseCreateUsers < ActiveRecord::Migration[8.0] # t.string :company_email, null: true # Email de la société # t.string :company_website, null: true # Site web de la société + t.string :stripe_customer_id, null: true + t.timestamps null: false end diff --git a/db/migrate/20250823170409_create_orders.rb b/db/migrate/20250823170409_create_orders.rb index 8e0f057..7c1a3ad 100644 --- a/db/migrate/20250823170409_create_orders.rb +++ b/db/migrate/20250823170409_create_orders.rb @@ -13,8 +13,8 @@ class CreateOrders < ActiveRecord::Migration[8.0] end # Indexes for performance - add_index :orders, [:user_id, :status], name: 'idx_orders_user_status' - add_index :orders, [:event_id, :status], name: 'idx_orders_event_status' + add_index :orders, [ :user_id, :status ], name: 'idx_orders_user_status' + add_index :orders, [ :event_id, :status ], name: 'idx_orders_event_status' add_index :orders, :expires_at, name: 'idx_orders_expires_at' end -end \ No newline at end of file +end diff --git a/docs/application-optimization.md b/docs/application-optimization.md deleted file mode 100755 index 3e4ff1d..0000000 --- a/docs/application-optimization.md +++ /dev/null @@ -1,157 +0,0 @@ -# Application.js Size Optimization Guide - -## Current Issue -The `application.js` bundle is 1.4MB (2.3MB with source maps), which is significantly larger than recommended. - -## Root Causes -1. **Single bundle includes everything**: All dependencies, React, controllers, and components -2. **No code splitting**: Everything is bundled into one file -3. **Development dependencies**: Alpine.js and other dev tools included -4. **No minification/optimization**: Source maps and uncompressed code - -## Optimization Strategies - -### 1. Split Bundles (Recommended) -Create separate bundles for different parts of the application: - -**Update package.json build scripts:** -```json -{ - "scripts": { - "build": "npm run build:main && npm run build:components", - "build:main": "esbuild app/javascript/application.js --bundle --minify --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets", - "build:components": "esbuild app/javascript/components/*.* --bundle --minify --format=esm --outdir=app/assets/builds/components --public-path=/assets --loader:.js=jsx", - "build:css": "postcss ./app/assets/stylesheets/application.postcss.css -o ./app/assets/builds/application.css" - } -} -``` - -### 2. Remove Unused Dependencies -**package.json optimization:** -```json -{ - "dependencies": { - "@hotwired/stimulus": "^3.2.2", - "@hotwired/turbo-rails": "^8.0.13", - "@radix-ui/react-slot": "^1.2.3", - "react": "^18.3.1", - "react-dom": "^18.3.1" - }, - "devDependencies": { - "@tailwindcss/postcss": "^4.1.4", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "esbuild": "^0.25.4", - "postcss": "^8.5.3", - "tailwind-merge": "^3.3.1", - "tailwindcss": "^4.1.4", - "tailwindcss-animate": "^1.0.7" - } -} -``` - -**Remove these from devDependencies:** -- `alpinejs` - if not used -- `@types/alpinejs` - if Alpine.js removed -- `cssnano` - if using Tailwind's built-in minification -- `pm2` - production deployment tool - -### 3. Dynamic Imports (Code Splitting) -**Update application.js:** -```javascript -// Instead of importing everything statically -import "@hotwired/turbo-rails" -import "./controllers" - -// Use dynamic imports for heavy components -const loadComponent = async (componentName) => { - const { default: component } = await import(`./components/${componentName}`) - return component -} -``` - -### 4. Tree Shaking & Minification -**Enhanced build command:** -```json -{ - "build": "esbuild app/javascript/application.js --bundle --minify --tree-shaking --drop:console --drop:debugger --sourcemap=external --format=esm --outdir=app/assets/builds --public-path=/assets" -} -``` - -### 5. Separate Vendor Bundle -**Create vendor.js:** -```javascript -// app/javascript/vendor.js -import "react" -import "react-dom" -import "@radix-ui/react-slot" -``` - -**Update build to create vendor bundle:** -```json -{ - "build:vendor": "esbuild app/javascript/vendor.js --bundle --minify --format=esm --outdir=app/assets/builds --public-path=/assets", - "build:app": "esbuild app/javascript/application.js --bundle --minify --external:react --external:react-dom --format=esm --outdir=app/assets/builds --public-path=/assets" -} -``` - -### 6. Conditional Loading -**Lazy load heavy components:** -```javascript -// app/javascript/application.js -if (document.querySelector('[data-controller="shadcn-test"]')) { - import('./controllers/shadcn_test_controller') -} -``` - -### 7. Production Optimization Checklist - -**Step 1: Analyze bundle size** -```bash -npm install --save-dev webpack-bundle-analyzer -npx esbuild app/javascript/application.js --bundle --analyze -``` - -**Step 2: Implement optimizations** -```bash -# Remove unused dependencies -npm uninstall alpinejs @types/alpinejs cssnano pm2 - -# Update build scripts -npm run build -``` - -**Step 3: Verify size reduction** -Should reduce from 1.4MB to ~200-400KB - -## Quick Fix Commands - -```bash -# 1. Remove Alpine.js (if unused) -npm uninstall alpinejs @types/alpinejs - -# 2. Update build with optimization -npm install --save-dev esbuild@latest - -# 3. Modify package.json scripts -# (Copy the optimized scripts above) - -# 4. Build with optimization -npm run build -``` - -## Expected Results -- **Before**: 1.4MB application.js -- **After**: 200-400KB with code splitting -- **Vendor bundle**: ~100KB (cached) -- **App bundle**: ~100-300KB (dynamic) - -## Monitoring -Add bundle size monitoring to CI/CD: -```json -{ - "size-limits": { - "app/assets/builds/application.js": "500kb", - "app/assets/builds/application.css": "50kb" - } -} \ No newline at end of file diff --git a/docs/architecture.md b/docs/architecture.md deleted file mode 100755 index 1000d3b..0000000 --- a/docs/architecture.md +++ /dev/null @@ -1,363 +0,0 @@ -# Aperonight - Technical Architecture - -## Overview - -Aperonight is a Ruby on Rails web application designed for proposing night parties in Paris and allowing event makers to create their own events. The application serves two primary user groups: - -### For Customers: -- View upcoming and past parties -- Book tickets with customizable bundles (simple entry, VIP, group passes, etc.) -- Complete secure payments via credit card, PayPal, or bank transfer -- Access mobile-friendly interface for ticket management -- Receive unique, scannable tickets (QR codes) - -### For Promoters: -- Create and schedule parties -- Define custom ticket bundles and pricing -- Aggregate events from external platforms (Shogun, Bizouk, Weezevent) -- Scan tickets at events using mobile devices - -## Technical Architecture - -### 1. Database Schema - -```ruby -# User - Handles both customers and promoters -create_table :users do |t| - t.string :email - t.string :password_digest - t.string :role # customer or promoter - t.timestamps -end - -# Event - Events created by promoters -create_table :events do |t| - t.string :name - t.text :description - t.datetime :start_time - t.datetime :end_time - t.string :location - t.integer :promoter_id - t.timestamps -end - -# TicketType - Customizable bundles defined by promoters -create_table :ticket_types do |t| - t.string :name - t.text :description - t.decimal :price - t.integer :event_id - t.timestamps -end - -# Ticket - Individual ticket instances purchased by customers -create_table :tickets do |t| - t.string :uuid - t.string :qr_code - t.integer :event_id - t.integer :user_id - t.integer :ticket_type_id - t.boolean :used, default: false - t.timestamps -end - -# Payment - Transaction records for ticket purchases -create_table :payments do |t| - t.string :payment_method # credit_card, paypal, bank_account - t.string :transaction_id - t.integer :user_id - t.integer :ticket_id - t.decimal :amount - t.timestamps -end -``` - -### 2. Core Models - -```ruby -class User < ApplicationRecord - devise :database_authenticatable, :registerable, - :recoverable, :rememberable, :validatable - has_many :tickets - has_many :payments - has_many :parties, foreign_key: 'promoter_id' -end - -class Event < ApplicationRecord - belongs_to :promoter, class_name: 'User' - has_many :tickets - has_many :ticket_types -end - -class TicketType < ApplicationRecord - belongs_to :event - has_many :tickets -end - -class Ticket < ApplicationRecord - belongs_to :event - belongs_to :user - belongs_to :ticket_type - has_one :payment - - before_create :generate_uuid_and_qr_code - - private - - def generate_uuid_and_qr_code - self.uuid = SecureRandom.uuid - self.qr_code = RQRCode::QRCode.new(self.uuid).as_svg - end -end - -class Payment < ApplicationRecord - belongs_to :user - belongs_to :ticket - - enum payment_method: { - credit_card: 'credit_card', - paypal: 'paypal', - bank_account: 'bank_account' - } - - after_create :process_payment - - private - - def process_payment - case self.payment_method - when 'credit_card' - process_stripe_payment - when 'paypal' - process_paypal_payment - when 'bank_account' - process_bank_account_payment - end - end -end -``` - -### 3. Key Controllers - -#### Parties Controller -```ruby -class PartiesController < ApplicationController - before_action :authenticate_user! - before_action :set_event, only: [:show, :edit, :update, :destroy] - - def index - @parties = Event.all - end - - def show - @ticket_types = @event.ticket_types - end - - def new - @event = Event.new - @event.ticket_types.build - end - - def create - @event = current_user.parties.build(event_params) - if @event.save - redirect_to @event, notice: 'Event was successfully created.' - else - render :new - end - end - - private - - def set_event - @event = Event.find(params[:id]) - end - - def event_params - params.require(:event).permit( - :name, :description, :start_time, :end_time, :location, - ticket_types_attributes: [:id, :name, :description, :price, :_destroy] - ) - end -end -``` - -#### Tickets Controller -```ruby -class TicketsController < ApplicationController - before_action :authenticate_user! - before_action :set_event, only: [:new, :create] - - def new - @ticket = Ticket.new - end - - def create - @ticket = current_user.tickets.build(ticket_params) - if @ticket.save - redirect_to @ticket, notice: 'Ticket was successfully booked.' - else - render :new - end - end - - def scan - end - - def validate - qr_code = params[:qr_code] - ticket = Ticket.find_by(qr_code: qr_code) - if ticket && !ticket.used - ticket.update(used: true) - render json: { valid: true } - else - render json: { valid: false } - end - end - - private - - def set_event - @event = Event.find(params[:event_id]) - end - - def ticket_params - params.require(:ticket).permit(:ticket_type_id, :event_id) - end -end -``` - -### 4. Payment Integration - -#### Stripe Configuration -```ruby -# config/initializers/stripe.rb -Rails.configuration.stripe = { - publishable_key: ENV['STRIPE_PUBLISHABLE_KEY'], - secret_key: ENV['STRIPE_SECRET_KEY'] -} - -Stripe.api_key = Rails.configuration.stripe[:secret_key] -``` - -#### PayPal Configuration -```ruby -# config/initializers/paypal.rb -PayPal::SDK.configure({ - mode: ENV['PAYPAL_MODE'], # 'sandbox' or 'live' - client_id: ENV['PAYPAL_CLIENT_ID'], - client_secret: ENV['PAYPAL_CLIENT_SECRET'] -}) -``` - -### 5. Frontend Considerations - -#### Mobile Ticket Scanning -```erb - - - - - Scan Ticket - - - -

Scan Ticket

-
-
- - - -``` - -### 6. Routes Configuration - -```ruby -# config/routes.rb -Rails.application.routes.draw do - devise_for :users - resources :parties do - resources :ticket_types, only: [:new, :create, :edit, :update, :destroy] - end - resources :ticket_types, only: [:index, :show] - resources :tickets do - resources :payments, only: [:new, :create] - collection do - post 'validate' - end - end - get 'paypal_success', to: 'payments#paypal_success' - get 'paypal_cancel', to: 'payments#paypal_cancel' - get 'tickets/scan', to: 'tickets#scan' - root 'parties#index' -end -``` - -## Implementation Recommendations - -### Authentication & Authorization -- Use Devise for user authentication -- Implement Pundit or CanCanCan for role-based access control -- Distinguish clearly between customer and promoter permissions - -### Payment Processing -- Integrate Stripe for credit card payments -- Add PayPal support through official SDK -- Consider Plaid for bank account integration - -### Performance & Scalability -- Implement Redis for caching frequently accessed data -- Use CDN for static assets (images, CSS, JS) -- Employ background job processing (Sidekiq) for emails and payments -- Optimize database queries with proper indexing - -### Security Considerations -- Validate all user inputs -- Sanitize HTML output to prevent XSS -- Secure payment processing with PCI compliance -- Implement rate limiting for API endpoints -- Regular security audits and dependency updates - diff --git a/docs/branch-naming.md b/docs/branch-naming.md deleted file mode 100755 index c8e1367..0000000 --- a/docs/branch-naming.md +++ /dev/null @@ -1,142 +0,0 @@ -Cette conversation a été générée avec Duck.ai (https://duck.ai) à l'aide du modèle Mistral Small 3 d'Mistral AI. Les chats IA peuvent afficher des informations inexactes ou offensantes (voir https://duckduckgo.com/duckai/privacy-terms pour en savoir plus). - -==================== - -Message 1 sur 2 de l'utilisateur - 16/08/2025, 08:06:58: -In a git Ruby on Rails web project, how to name branches? - -Mistral Small 3: -Naming branches in a Git Ruby on Rails web project (or any Git project) is an important aspect of maintaining a clean and understandable repository. Here are some best practices for naming branches: - -1. **Use Descriptive Names**: The branch name should clearly describe the purpose or the feature being worked on. This helps other developers understand the context of the branch at a glance. - -2. **Use Hyphens or Underscores**: Use hyphens (`-`) or underscores (`_`) to separate words in the branch name. This makes the name more readable. - -3. **Prefix with the Type of Change**: Prefix the branch name with a type indicator such as `feature`, `bugfix`, `hotfix`, `release`, `chore`, `refactor`, `docs`, etc. This helps in categorizing the branches. - -4. **Keep It Short but Meaningful**: While being descriptive, try to keep the branch name short to avoid clutter. - -5. **Avoid Special Characters**: Stick to alphanumeric characters and hyphens/underscores. Avoid using special characters like `@`, `#`, `$`, etc. - -6. **Use Lowercase Letters**: Consistently use lowercase letters to avoid case sensitivity issues. - -Here are some examples of well-named branches: - -- `feature/user-authentication` -- `bugfix/login-issue` -- `hotfix/critical-bug` -- `release/v1.2.0` -- `chore/update-dependencies` -- `refactor/user-model` -- `docs/update-readme` - -### Example Workflow - -1. **Creating a New Feature Branch**: - ```sh - git checkout -b feature/user-authentication - ``` - -2. **Creating a Bugfix Branch**: - ```sh - git checkout -b bugfix/login-issue - ``` - -3. **Creating a Hotfix Branch**: - ```sh - git checkout -b hotfix/critical-bug - ``` - -4. **Creating a Release Branch**: - ```sh - git checkout -b release/v1.2.0 - ``` - -5. **Creating a Chore Branch**: - ```sh - git checkout -b chore/update-dependencies - ``` - -6. **Creating a Refactor Branch**: - ```sh - git checkout -b refactor/user-model - ``` - -7. **Creating a Docs Branch**: - ```sh - git checkout -b docs/update-readme - ``` - -By following these guidelines, you can maintain a well-organized and understandable branch structure in your Git repository, making collaboration and code management much easier. - --------------------- - -Message 2 sur 2 de l'utilisateur - 16/08/2025, 08:08:31: -Is it a good practice to use main, develop and functionnal names for branches? - -Mistral Small 3: -Using `main`, `develop`, and feature branches is a common and widely accepted practice in Git workflows, particularly in the context of the Git Flow or GitHub Flow methodologies. Here's a breakdown of these branches and why they are useful: - -1. **`main` Branch**: - - **Purpose**: The `main` branch (or sometimes called `master`) is the stable branch that always contains the production-ready code. It represents the current state of the project that is deployed to production. - - **Usage**: This branch should only contain code that has been thoroughly tested and is ready for release. It is typically protected to prevent direct commits and requires pull requests for changes. - - **Example**: `git checkout main` - -2. **`develop` Branch**: - - **Purpose**: The `develop` branch is used for integrating features and preparing for the next release. It serves as a staging area for features that are in progress but not yet ready for production. - - **Usage**: Developers merge their feature branches into `develop` for integration testing. This branch can be less stable than `main` but should still be in a reasonably good state. - - **Example**: `git checkout develop` - -3. **Feature Branches**: - - **Purpose**: Feature branches are used to develop new features, bug fixes, or any other isolated work. Each feature branch is created from `develop` and merged back into `develop` once the work is complete. - - **Usage**: These branches allow for parallel development without interfering with the stability of `develop` or `main`. They are typically named descriptively, as discussed earlier (e.g., `feature/user-authentication`, `bugfix/login-issue`). - - **Example**: `git checkout -b feature/user-authentication` - -### Example Workflow - -1. **Create a Feature Branch**: - ```sh - git checkout develop - git pull origin develop - git checkout -b feature/user-authentication - ``` - -2. **Develop the Feature**: - - Make changes, commit them, and push the branch to the remote repository. - ```sh - git add . - git commit -m "Add user authentication feature" - git push origin feature/user-authentication - ``` - -3. **Merge the Feature Branch into `develop`**: - - Once the feature is complete and tested, create a pull request to merge it into `develop`. - ```sh - git checkout develop - git pull origin develop - git merge feature/user-authentication - git push origin develop - ``` - -4. **Prepare for Release**: - - When preparing for a new release, create a release branch from `develop`. - ```sh - git checkout -b release/v1.2.0 - ``` - -5. **Merge Release Branch into `main`**: - - After testing the release branch, merge it into `main` and tag the release. - ```sh - git checkout main - git merge release/v1.2.0 - git tag -a v1.2.0 -m "Release version 1.2.0" - git push origin main --tags - ``` - -### Benefits - -- **Stability**: The `main` branch remains stable and always contains production-ready code. -- **Integration**: The `develop` branch allows for continuous integration of features, making it easier to test and integrate new work. -- **Isolation**: Feature branches isolate work, allowing multiple developers to work on different features simultaneously without conflicts. - -Using `main`, `develop`, and feature branches is a good practice as it helps maintain a clean, organized, and manageable repository, facilitating better collaboration and code management. \ No newline at end of file diff --git a/docs/checkout-handle.md b/docs/checkout-handle.md deleted file mode 100755 index c8c78fb..0000000 --- a/docs/checkout-handle.md +++ /dev/null @@ -1,322 +0,0 @@ -# Backend Checkout Handling Improvements - -Based on your current Stripe integration, here are key improvements for robust checkout handling: - -## 1. Enhanced Inventory Management with Concurrency Protection - -The current implementation doesn't prevent overselling during concurrent purchases. - -Add database-level concurrency protection: - -```ruby -# app/controllers/events_controller.rb -def checkout - cart_data = JSON.parse(params[:cart] || "{}") - - if cart_data.empty? - redirect_to event_path(@event.slug, @event), alert: "Veuillez sélectionner au moins un billet" - return - end - - # Use transaction with row-level locking for inventory protection - ActiveRecord::Base.transaction do - line_items = [] - order_items = [] - - cart_data.each do |ticket_type_id, item| - # Lock the ticket type row to prevent race conditions - ticket_type = @event.ticket_types.lock.find_by(id: ticket_type_id) - next unless ticket_type - - quantity = item["quantity"].to_i - next if quantity <= 0 - - # Check real-time availability with locked row - sold_count = ticket_type.tickets.count - available = ticket_type.quantity - sold_count - - if quantity > available - redirect_to event_path(@event.slug, @event), alert: "Plus que #{available} billets disponibles pour #{ticket_type.name}" - return - end - - # Create line items and order data - line_items << { - price_data: { - currency: "eur", - product_data: { - name: "#{@event.name} - #{ticket_type.name}", - description: ticket_type.description - }, - unit_amount: ticket_type.price_cents - }, - quantity: quantity - } - - order_items << { - ticket_type_id: ticket_type.id, - ticket_type_name: ticket_type.name, - quantity: quantity, - price_cents: ticket_type.price_cents - } - end - - if order_items.empty? - redirect_to event_path(@event.slug, @event), alert: "Commande invalide" - return - end - - # Create Stripe session only after inventory validation - session = Stripe::Checkout::Session.create({ - payment_method_types: ["card"], - line_items: line_items, - 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: { - event_id: @event.id, - user_id: current_user.id, - order_items: order_items.to_json - } - }) - - redirect_to session.url, allow_other_host: true - end -rescue ActiveRecord::RecordNotFound - redirect_to event_path(@event.slug, @event), alert: "Type de billet introuvable" -rescue Stripe::StripeError => e - redirect_to event_path(@event.slug, @event), alert: "Erreur de traitement du paiement : #{e.message}" -end -``` - -## 2. Webhook Handler for Reliable Payment Confirmation - -Create a dedicated webhook endpoint for more reliable payment processing: - -### Routes Configuration - -```ruby -# config/routes.rb -post '/webhooks/stripe', to: 'webhooks#stripe' -``` - -### Webhooks Controller - -```ruby -# app/controllers/webhooks_controller.rb -class WebhooksController < ApplicationController - skip_before_action :verify_authenticity_token - before_action :verify_stripe_signature - - def stripe - case @event.type - when 'checkout.session.completed' - handle_successful_payment(@event.data.object) - when 'payment_intent.payment_failed' - handle_failed_payment(@event.data.object) - end - - head :ok - end - - private - - def handle_successful_payment(session) - # Process ticket creation in background job for reliability - CreateTicketsJob.perform_later(session.id) - end - - def handle_failed_payment(session) - Rails.logger.error "Payment failed for session: #{session.id}" - # Add any additional handling for failed payments - end - - def verify_stripe_signature - payload = request.body.read - sig_header = request.env['HTTP_STRIPE_SIGNATURE'] - - begin - @event = Stripe::Webhook.construct_event( - payload, sig_header, ENV['STRIPE_WEBHOOK_SECRET'] - ) - rescue JSON::ParserError, Stripe::SignatureVerificationError => e - Rails.logger.error "Stripe webhook signature verification failed: #{e.message}" - head :bad_request - end - end -end -``` - -## 3. Background Job for Ticket Creation - -Use background jobs to prevent timeouts and improve reliability: - -```ruby -# app/jobs/create_tickets_job.rb -class CreateTicketsJob < ApplicationJob - queue_as :default - retry_on StandardError, wait: :exponentially_longer, attempts: 5 - - def perform(session_id) - session = Stripe::Checkout::Session.retrieve(session_id) - return unless session.payment_status == 'paid' - - # Prevent duplicate processing - return if Ticket.exists?(stripe_session_id: session_id) - - order_items = JSON.parse(session.metadata['order_items']) - user = User.find(session.metadata['user_id']) - event = Event.find(session.metadata['event_id']) - - ActiveRecord::Base.transaction do - order_items.each do |item| - ticket_type = TicketType.find(item['ticket_type_id']) - - item['quantity'].times do - ticket = Ticket.create!( - user: user, - ticket_type: ticket_type, - status: 'active', - stripe_session_id: session_id, # Prevent duplicates - price_cents: item['price_cents'] # Store historical price - ) - - # Send email asynchronously - TicketMailer.purchase_confirmation(ticket).deliver_later - end - end - end - end -end -``` - -## 4. Enhanced Error Handling & Recovery in Payment Success - -Improve the payment success handler with better error recovery: - -```ruby -# app/controllers/events_controller.rb - Enhanced payment_success method -def payment_success - session_id = params[:session_id] - event_id = params[:event_id] - - # Validate parameters - unless session_id.present? && event_id.present? - redirect_to dashboard_path, alert: "Paramètres de confirmation manquants" - return - end - - begin - @tickets = Ticket.includes(:ticket_type, :event) - .where(stripe_session_id: session_id, user: current_user) - - if @tickets.any? - # Tickets already created (webhook processed first) - @event = @tickets.first.event - render 'payment_success' - else - # Fallback: create tickets synchronously if webhook failed - session = Stripe::Checkout::Session.retrieve(session_id) - - if session.payment_status == 'paid' - CreateTicketsJob.perform_now(session_id) - redirect_to payment_success_path(session_id: session_id, event_id: event_id) - else - redirect_to dashboard_path, alert: "Le paiement n'est pas encore confirmé" - end - end - - rescue Stripe::StripeError => e - logger.error "Stripe error in payment_success: #{e.message}" - redirect_to dashboard_path, alert: "Erreur de confirmation de paiement" - rescue => e - logger.error "Unexpected error in payment_success: #{e.message}" - redirect_to dashboard_path, alert: "Une erreur inattendue s'est produite" - end -end -``` - -## 5. Database Schema Improvements - -Add migration for better payment tracking: - -```ruby -# db/migrate/xxx_add_payment_tracking_to_tickets.rb -class AddPaymentTrackingToTickets < ActiveRecord::Migration[7.0] - def change - add_column :tickets, :stripe_session_id, :string - add_column :tickets, :purchased_at, :timestamp, default: -> { 'CURRENT_TIMESTAMP' } - - add_index :tickets, :stripe_session_id, unique: true - add_index :tickets, [:user_id, :purchased_at] - end -end -``` - -## 6. Security Considerations - -1. **Rate Limiting**: Add rate limiting to checkout endpoints -2. **CSRF Protection**: Already implemented ✅ -3. **Input Validation**: Validate all cart data thoroughly -4. **Audit Logging**: Log all payment attempts and outcomes -5. **PCI Compliance**: Never store card data (Stripe handles this) ✅ - -## 7. Monitoring & Observability - -Add metrics tracking to monitor checkout performance: - -```ruby -# Add to ApplicationController or EventsController -around_action :track_checkout_metrics, only: [:checkout] - -private - -def track_checkout_metrics - start_time = Time.current - begin - yield - # Log successful checkout - Rails.logger.info("Checkout completed", { - event_id: @event&.id, - user_id: current_user&.id, - duration: Time.current - start_time - }) - rescue => e - # Log failed checkout - Rails.logger.error("Checkout failed", { - event_id: @event&.id, - user_id: current_user&.id, - error: e.message, - duration: Time.current - start_time - }) - raise - end -end -``` - -## Summary of Improvements - -Your ticket checkout system is already well-implemented with Stripe integration! The enhancements above will make it production-ready: - -### Critical Improvements - -1. Add database row locking to prevent overselling during concurrent purchases -2. Implement Stripe webhooks for reliable payment processing -3. Use background jobs for ticket creation to prevent timeouts -4. Add duplicate prevention with stripe_session_id tracking - -### Security & Reliability - -5. Enhanced error recovery with fallback ticket creation -6. Comprehensive logging for debugging and monitoring -7. Database schema improvements for better payment tracking - -### Key Files to Modify - -- `app/controllers/events_controller.rb` - Add inventory locking -- `app/controllers/webhooks_controller.rb` - New webhook handler -- `app/jobs/create_tickets_job.rb` - Background ticket creation -- Migration for `stripe_session_id` field - -These enhancements will make your checkout system robust for high-traffic scenarios and edge cases. diff --git a/docs/theme-rules.md.old b/docs/theme-rules.md.old deleted file mode 100755 index 4274d4b..0000000 --- a/docs/theme-rules.md.old +++ /dev/null @@ -1,103 +0,0 @@ -# Theme Rules & Color Palette - Aperonight - -Extracted from `app/views/pages/home.html.erb` - -## Color Palette - -### Primary Colors -- **Indigo**: `#4338ca` (rgb(67, 56, 202)) - Used in hero gradient -- **Purple**: `#8b5cf6` (rgb(139, 92, 246)) - Primary brand color -- **Pink**: `#ec4899` (rgb(236, 72, 153)) - Accent color - -### Background Gradients -- **Hero**: `bg-gradient-to-br from-indigo-900 via-purple-800 to-pink-700` -- **CTA**: `bg-gradient-to-r from-purple-900 via-indigo-900 to-pink-900` -- **Cards**: `bg-gradient-to-br from-gray-800 to-gray-900` -- **Buttons**: `bg-gradient-to-r from-purple-600 to-pink-600` - -### Text Colors -- **White**: `text-white` - Primary text -- **Gray-200**: `text-gray-200` - Secondary text -- **Gray-300**: `text-gray-300` - Subtle text -- **Gray-400**: `text-gray-400` - Muted text -- **Transparent gradient**: `text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-pink-400` - Special highlight - -### Background Colors -- **Gray-900**: `bg-gray-900` - Main background -- **Black**: `bg-black` - Overlay backgrounds -- **Gray-800**: `bg-gray-800` - Card backgrounds -- **White/Transparent**: `bg-white bg-opacity-10 backdrop-blur-sm` - Glass effect - -## Spacing & Layout - -### Hero Section -- **Height**: `min-h-[70vh]` -- **Max-width**: `max-w-7xl mx-auto` -- **Padding**: `px-4 sm:px-6 lg:px-8` - -### Grid Layouts -- **Responsive**: `grid-cols-1 md:grid-cols-2 lg:grid-cols-3` -- **Gap**: `gap-8` standard spacing - -### Padding Classes -- **Section**: `py-16`, `py-20` -- **Card**: `p-4`, `p-6`, `p-8` -- **Button**: `py-3`, `py-4`, `px-6`, `px-8` - -## Typography - -### Font Sizes -- **Hero Title**: `text-5xl md:text-7xl` -- **Section Title**: `text-4xl` -- **Card Title**: `text-2xl` -- **Body**: `text-xl`, `text-lg` -- **Small**: `text-sm` - -### Font Weights -- **Bold**: `font-bold` (headings) -- **Semibold**: `font-semibold` (buttons, important text) -- **Medium**: `font-medium` (labels) - -## Interactive States - -### Hover Effects -- **Scale**: `hover:scale-105` -- **Transition**: `transition-all duration-300` -- **Button Hover**: `hover:from-purple-700 hover:to-pink-700` -- **Glass Hover**: `hover:bg-opacity-20` - -### Shadows -- **Default**: `shadow-lg` -- **Strong**: `shadow-xl` -- **Card**: `shadow-2xl` - -## Border Radius -- **Buttons**: `rounded-full` (pill-shaped) -- **Cards**: `rounded-2xl` -- **Inputs**: `rounded-lg` - -## Icon Colors -- **Primary**: `text-white` (on colored backgrounds) -- **Accent**: `text-purple-400`, `text-pink-400` -- **Muted**: `text-gray-400` - -## Usage Examples - -### Primary Button -```html -class="bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white font-semibold py-4 px-8 rounded-full transition-all duration-300 transform hover:scale-105 shadow-lg" -``` - -### Card Background -```html -class="bg-gradient-to-br from-gray-800 to-gray-900 rounded-2xl overflow-hidden hover:transform hover:scale-105 transition-all duration-300 shadow-xl" -``` - -### Hero Gradient -```html -class="bg-gradient-to-br from-indigo-900 via-purple-800 to-pink-700" -``` - -### Glass Effect -```html -class="bg-white bg-opacity-10 backdrop-blur-sm border border-white border-opacity-30" \ No newline at end of file diff --git a/lib/tasks/tickets.rake b/lib/tasks/tickets.rake index 14bef56..6ce6cf5 100644 --- a/lib/tasks/tickets.rake +++ b/lib/tasks/tickets.rake @@ -11,7 +11,7 @@ namespace :tickets do total_drafts = Ticket.draft.count expired_drafts = Ticket.expired_drafts.count retryable_drafts = Ticket.can_retry_payment.count - + puts "=== Draft Ticket Statistics ===" puts "Total draft tickets: #{total_drafts}" puts "Expired draft tickets: #{expired_drafts}" @@ -19,4 +19,4 @@ namespace :tickets do puts "Max payment attempts: #{Ticket::MAX_PAYMENT_ATTEMPTS}" puts "Draft expiry time: #{Ticket::DRAFT_EXPIRY_TIME}" end -end \ No newline at end of file +end diff --git a/stripe-fix-documentation.md b/stripe-fix-documentation.md deleted file mode 100644 index 599519d..0000000 --- a/stripe-fix-documentation.md +++ /dev/null @@ -1,47 +0,0 @@ -# Stripe Configuration Fix - Updated with Lazy Initialization - -## Problem -The "Retour" link on the collect_names page sometimes displayed a Stripe API key error: -``` -Erreur de traitement du paiement : No API key provided. Set your API key using "Stripe.api_key = ". -``` - -## Root Cause -The error occurred when Stripe code was executed without the API key being properly set. This could happen in development environments or when environment variables were not properly configured. - -## Solution Evolution - -We initially implemented a fix that enhanced the Stripe initializer and added better error handling. However, we have now updated our approach to use **lazy initialization** - Stripe is only initialized during the checkout process when actually needed. - -## Current Solution - Lazy Initialization Approach - -1. **Deferred Stripe Initialization** (`config/initializers/stripe.rb`): - - Stripe configuration is loaded at startup but API key is NOT set - - Stripe.api_key is only set during the checkout process when needed - -2. **Enhanced Stripe Helper** (`app/helpers/stripe_helper.rb`): - - Added `initialize_stripe` method to initialize Stripe only when needed - - Updated `safe_stripe_call` method to automatically initialize Stripe if not already done - -3. **Checkout Process Updates**: - - Added explicit Stripe initialization in `process_payment` method - - Added explicit Stripe initialization in `payment_success` method - - Added proper error handling for initialization failures - -4. **Benefits of This Approach**: - - Stripe is only initialized when actually needed (during checkout) - - Application startup is not dependent on Stripe service availability - - Payment-related issues are isolated and don't affect other application features - - More efficient resource usage (Stripe library only fully loaded during checkout) - -## Verification -The fix has been tested and verified to work correctly: -- Stripe is not initialized at application startup -- Stripe is properly initialized during the checkout process -- All Stripe functionality works as expected -- Error handling is improved - -## Prevention -The enhanced error handling will prevent the application from crashing when Stripe is not properly configured and will display user-friendly error messages instead. - -For detailed implementation, see `stripe-lazy-initialization-documentation.md`. \ No newline at end of file diff --git a/stripe-lazy-initialization-documentation.md b/stripe-lazy-initialization-documentation.md deleted file mode 100644 index c72b48b..0000000 --- a/stripe-lazy-initialization-documentation.md +++ /dev/null @@ -1,60 +0,0 @@ -# Stripe Configuration - Lazy Initialization Approach - -## Problem -The "Retour" link on the collect_names page sometimes displayed a Stripe API key error: -``` -Erreur de traitement du paiement : No API key provided. Set your API key using "Stripe.api_key = ". -``` - -## Root Cause -The error occurred because Stripe code was being called without the API key being properly set. This could happen in development environments or when environment variables were not properly configured. - -## Solution Implemented - Lazy Initialization - -1. **Deferred Stripe Initialization** (`config/initializers/stripe.rb`): - - Stripe configuration is loaded at startup but API key is NOT set - - Stripe.api_key is only set during the checkout process when needed - -2. **Stripe Concern** (`app/controllers/concerns/stripe_concern.rb`): - - Created `StripeConcern` module with `stripe_configured?` and `initialize_stripe` methods - - Included in `EventsController` to provide access to Stripe functionality - -3. **Direct Configuration Checks**: - - Updated `process_payment` and `payment_success` methods to directly check Stripe configuration - - Added proper error handling for initialization failures - -4. **Benefits of This Approach**: - - Stripe is only initialized when actually needed (during checkout) - - Application startup is not dependent on Stripe service availability - - Payment-related issues are isolated and don't affect other application features - - More efficient resource usage (Stripe library only fully loaded during checkout) - -5. **Verification**: - - Created test scripts to verify the lazy initialization approach - - Confirmed that Stripe is not initialized at startup but can be initialized during checkout - -## Code Changes - -### config/initializers/stripe.rb -- Removed automatic Stripe.api_key initialization -- Added informational log message - -### app/controllers/concerns/stripe_concern.rb -- Created new concern with `stripe_configured?` and `initialize_stripe` methods - -### app/controllers/events_controller.rb -- Added direct Stripe configuration checks in `process_payment` method -- Added direct Stripe configuration checks in `payment_success` method -- Added comprehensive logging for debugging - -### app/helpers/stripe_helper.rb -- Kept `safe_stripe_call` method with updated logic - -## Testing -The new approach has been verified to work correctly: -- Stripe is not initialized at application startup -- Stripe is properly initialized during the checkout process -- All Stripe functionality works as expected -- Error handling is improved - -This approach provides better isolation of payment functionality and ensures that issues with Stripe configuration don't affect the rest of the application. \ No newline at end of file diff --git a/test/models/application_record_test.rb b/test/models/application_record_test.rb index c891411..c0d84c8 100755 --- a/test/models/application_record_test.rb +++ b/test/models/application_record_test.rb @@ -11,4 +11,4 @@ class ApplicationRecordTest < ActiveSupport::TestCase assert_kind_of Class, ApplicationRecord assert ApplicationRecord < ActiveRecord::Base end -end \ No newline at end of file +end diff --git a/test/models/ticket_test.rb b/test/models/ticket_test.rb index 86f986a..e3cd09e 100755 --- a/test/models/ticket_test.rb +++ b/test/models/ticket_test.rb @@ -254,4 +254,3 @@ class TicketTest < ActiveSupport::TestCase assert ticket.save end end - diff --git a/test/models/user_test.rb b/test/models/user_test.rb index b2f7ce5..1110eef 100755 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -63,4 +63,4 @@ class UserTest < ActiveSupport::TestCase refute user.valid?, "User with last_name longer than 12 chars should be invalid" assert_not_nil user.errors[:last_name], "No validation error for too long last_name" end -end \ No newline at end of file +end