feat: Add booking control during events

- Add allow_booking_during_event boolean field to events (defaults to false)
- Implement booking_allowed? method to check if tickets can be purchased
- Add event_started? and event_ended? helper methods
- Include new option in event edit form with clear explanation
- Display booking policy status on event show page
- Add visual indicator when booking is disabled during ongoing events
- Update controller to permit new parameter

This allows promoters to control whether attendees can purchase tickets
after an event has started, providing flexibility for different event types.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
kbe
2025-09-11 08:49:01 +02:00
parent 21919c813e
commit f54742b041
7 changed files with 73 additions and 17 deletions

View File

@@ -71,23 +71,12 @@ class PagesController < ApplicationController
@monthly_revenue = (0..5).map do |months_ago|
start_date = months_ago.months.ago.beginning_of_month
end_date = months_ago.months.ago.end_of_month
<<<<<<< HEAD
revenue = current_user.events
.joins(:orders)
.where(orders: { status: ['paid', 'completed'] })
.where(orders: { created_at: start_date..end_date })
.sum('orders.total_amount_cents') / 100.0
=======
revenue = current_user.events
.joins(:orders)
.where(orders: { status: [ "paid", "completed" ] })
.where(orders: { created_at: start_date..end_date })
.sum("orders.total_amount_cents") / 100.0
>>>>>>> develop
{
month: start_date.strftime("%B %Y"),
revenue: revenue

View File

@@ -111,7 +111,7 @@ class Promoter::EventsController < ApplicationController
params.require(:event).permit(
:name, :slug, :description, :image,
:venue_name, :venue_address, :latitude, :longitude,
:start_time, :end_time, :featured
:start_time, :end_time, :featured, :allow_booking_during_event
)
end
end

View File

@@ -70,6 +70,32 @@ class Event < ApplicationRecord
"Les coordonnées exactes n'ont pas pu être déterminées automatiquement. Une localisation approximative a été utilisée."
end
# Check if ticket booking is currently allowed for this event
def booking_allowed?
return false unless published?
return false if sold_out?
return false if canceled?
# Check if event has started and if booking during event is disabled
if event_started? && !allow_booking_during_event?
return false
end
true
end
# Check if the event has already started
def event_started?
return false if start_time.blank?
Time.current >= start_time
end
# Check if the event has ended
def event_ended?
return false if end_time.blank?
Time.current >= end_time
end
private
# Determine if we should perform server-side geocoding

View File

@@ -163,11 +163,24 @@
<div class="bg-white rounded-lg border border-gray-200 p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-6">Options</h3>
<div class="flex items-center">
<%= form.check_box :featured, class: "h-4 w-4 text-purple-600 border-gray-300 rounded focus:ring-purple-500" %>
<%= form.label :featured, "Mettre en avant sur la page d'accueil", class: "ml-2 text-sm text-gray-700" %>
<div class="space-y-4">
<div class="flex items-center">
<%= form.check_box :featured, class: "h-4 w-4 text-purple-600 border-gray-300 rounded focus:ring-purple-500" %>
<%= form.label :featured, "Mettre en avant sur la page d'accueil", class: "ml-2 text-sm text-gray-700" %>
</div>
<p class="text-sm text-gray-500">Les événements mis en avant apparaissent en premier sur la page d'accueil.</p>
<div class="flex items-start">
<%= form.check_box :allow_booking_during_event, class: "h-4 w-4 text-purple-600 border-gray-300 rounded focus:ring-purple-500 mt-1" %>
<div class="ml-2">
<%= form.label :allow_booking_during_event, "Autoriser la réservation pendant l'événement", class: "text-sm text-gray-700 font-medium" %>
<p class="text-sm text-gray-500 mt-1">
Si activé, les participants pourront acheter des billets même après le début de l'événement.
Si désactivé, la vente de billets s'arrêtera automatiquement à l'heure de début.
</p>
</div>
</div>
</div>
<p class="mt-2 text-sm text-gray-500">Les événements mis en avant apparaissent en premier sur la page d'accueil.</p>
</div>
<!-- Actions -->

View File

@@ -144,6 +144,18 @@
</div>
</div>
<% end %>
<% if @event.published? && @event.event_started? && !@event.allow_booking_during_event? %>
<div class="bg-orange-50 border border-orange-200 rounded-2xl p-4 mt-4">
<div class="flex items-center">
<i data-lucide="clock" class="w-5 h-5 text-orange-400 mr-3"></i>
<div>
<h3 class="text-sm font-medium text-orange-900">Réservations fermées</h3>
<p class="text-sm text-orange-700">L'événement a commencé et les nouvelles réservations sont désactivées.</p>
</div>
</div>
</div>
<% end %>
</div>
<!-- Event details -->
@@ -219,6 +231,18 @@
<span class="text-sm text-gray-500">Modifié le</span>
<p class="text-sm"><%= @event.updated_at.strftime("%d/%m/%Y à %H:%M") %></p>
</div>
<div>
<span class="text-sm text-gray-500">Réservation pendant l'événement</span>
<p class="text-sm flex items-center">
<% if @event.allow_booking_during_event? %>
<i data-lucide="check-circle" class="w-4 h-4 text-green-500 mr-1"></i>
Autorisée
<% else %>
<i data-lucide="x-circle" class="w-4 h-4 text-red-500 mr-1"></i>
Interdite
<% end %>
</p>
</div>
<% if @event.start_time %>
<div>
<span class="text-sm text-gray-500">Début</span>

View File

@@ -11,6 +11,7 @@
"qrcode": "^1.5.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"slug": "^11.0.0",
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.4",
@@ -545,6 +546,8 @@
"slash": ["slash@5.1.0", "", {}, "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="],
"slug": ["slug@11.0.0", "", { "bin": { "slug": "cli.js" } }, "sha512-71pb27F9TII2dIweGr2ybS220IUZo1A9GKZ+e2q8rpUr24mejBb6fTaSStM0SE1ITUUOshilqZze8Yt1BKj+ew=="],
"smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
"socks": ["socks@2.8.7", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="],

3
db/schema.rb generated
View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_08_23_171354) do
ActiveRecord::Schema[8.0].define(version: 2025_09_11_063815) do
create_table "events", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t|
t.string "name", null: false
t.string "slug", null: false
@@ -27,6 +27,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_08_23_171354) do
t.bigint "user_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "allow_booking_during_event", default: false, null: false
t.index ["featured"], name: "index_events_on_featured"
t.index ["latitude", "longitude"], name: "index_events_on_latitude_and_longitude"
t.index ["state"], name: "index_events_on_state"