30 Commits

Author SHA1 Message Date
kbe
82f0fab1f5 Disable authentication for API
Some checks failed
Ruby on Rails Test / rails-test (push) Has been cancelled
2025-09-15 17:15:49 +02:00
kbe
91e6425c1e feat: Settings page to update profile 2025-09-11 16:07:25 +02:00
kbe
f54742b041 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>
2025-09-11 08:49:01 +02:00
kbe
21919c813e Merge branch 'develop' into feat/promoters 2025-09-11 08:41:23 +02:00
kbe
1a7fb818df feat: Implement the promoter event creation
- Promoter can now create an event in draft mode
- Place is found based on address and long/lat are automatically
  deducted from it
- Slug is forged using the *slug* npm package instead of custom code
2025-09-10 20:46:31 +02:00
kbe
9b5d8fcf97 feat: Add comprehensive address-first geolocation system for events
This implementation provides automatic geocoding and map integration:

- **Event Model Enhancements:**
  - Automatic geocoding callback using OpenStreetMap Nominatim API
  - 3-tier fallback system: exact coordinates → city-based → country default
  - Fallback coordinates for major French cities (Paris, Lyon, Marseille, etc.)
  - Robust error handling that prevents event creation failures

- **User-Friendly Event Forms:**
  - Address-first approach - users just enter addresses
  - Hidden coordinate fields (auto-generated behind scenes)
  - Real-time geocoding with 1.5s debounce
  - "Ma position" button for current location with reverse geocoding
  - "Prévisualiser" button to show map links
  - Smart feedback system (loading, success, warnings, errors)

- **Enhanced Event Show Page:**
  - Map provider links (OpenStreetMap, Google Maps, Apple Plans)
  - Warning badges when approximate coordinates are used
  - Address-based URLs for better map integration

- **Comprehensive JavaScript Controller:**
  - Debounced auto-geocoding to minimize API calls
  - Multiple geocoding strategies (manual vs automatic)
  - Promise-based geolocation with proper error handling
  - Dynamic map link generation with address + coordinates

- **Failure Handling:**
  - Events never fail to save due to missing coordinates
  - Fallback to city-based coordinates when exact geocoding fails
  - User-friendly warnings when approximate locations are used
  - Maintains existing coordinates on update failures

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

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2025-09-10 19:36:47 +02:00
kbe
748f839346 feat: Implement comprehensive promoter system with dashboard and role-based access
This commit implements a complete promoter system that allows professional users
(is_professionnal: true) to manage events with advanced analytics and controls.

## Key Features Added:

### Role-Based Access Control
- Update User#can_manage_events? to use is_professionnal field
- Add promoter? alias method for semantic clarity
- Restrict event management to professional users only

### Enhanced Navigation
- Add conditional "Créer un événement" and "Mes événements" links
- Display promoter navigation only for professional users
- Include responsive mobile navigation with appropriate icons
- Maintain clean UI for regular users

### Comprehensive Promoter Dashboard
- Revenue metrics with total earnings calculation
- Tickets sold counter across all events
- Published vs draft events statistics
- Monthly revenue trend chart (6 months)
- Recent events widget with quick management actions
- Recent orders table with customer information

### Advanced Analytics
- Real-time revenue calculations from order data
- Monthly revenue trends with visual progress bars
- Event performance metrics and status tracking
- Customer order history and transaction details

### Event Management Workflow
- Verified existing event CRUD operations are comprehensive
- Maintains easy-to-use interface for event creation/editing
- State management system (draft → published → cancelled)
- Quick action buttons for common operations

### Documentation
- Comprehensive implementation guide in docs/
- Technical details and architecture explanations
- Future enhancement recommendations
- Testing and deployment considerations

## Technical Implementation:

- Optimized database queries to prevent N+1 problems
- Proper eager loading for dashboard performance
- Responsive design with Tailwind CSS components
- Clean separation of promoter vs regular user features
- Maintainable code structure following Rails conventions

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 18:12:04 +02:00
kbe
83e76f71bf Move increment_payment_attempt to API namespace and update JavaScript
- Add API route for increment_payment_attempt in config/routes.rb
- Update API OrdersController to handle increment_payment_attempt and skip API key authentication
- Update JavaScript code in checkout view to use API endpoint without CSRF tokens
- Remove CSRF token from API requests as it's not required for API endpoints
- Maintain backward compatibility by keeping original method in OrdersController
2025-09-10 16:27:05 +02:00
kbe
20ae3de7a3 Link to homepage on and more comments in controller 2025-09-10 15:20:29 +02:00
kbe
6d2a6ed027 chore: Remove links to non working themes 2025-09-10 15:11:33 +02:00
kbe
60b7bc6aa7 feat: Update from breadcrumb on the current page 2025-09-10 14:31:48 +02:00
kbe
8d2127fce2 feat: Use invoice emitter details from env var 2025-09-10 10:21:32 +02:00
kbe
2fb0e1fdbb Make invoice emitter configurable via environment variables
Add environment variables for invoice company details to allow customization without code changes. Update invoice view and Stripe service to use these configurable values.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 10:16:24 +02:00
kbe
ca35abe01d Fix link_to helper syntax error in orders checkout page
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2025-09-08 18:45:41 +02:00
kbe
f2448383d4 Setup container system for all pages with max-width constraint while maintaining full-width backgrounds
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2025-09-08 18:34:00 +02:00
kbe
9be7a01d93 Update all views to use new design system components and styling
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2025-09-08 17:59:12 +02:00
kbe
569303b631 Update breadcrumbs to use dynamic component with new design system colors
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2025-09-08 17:51:11 +02:00
kbe
259837622a Update design system to match Aperonight design guidelines - Replace gradient buttons with solid color buttons
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2025-09-08 17:31:19 +02:00
kbe
cf34c9c7a6 Fix i18n load path configuration that was causing translation helper issues
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2025-09-08 16:09:03 +02:00
kbe
1261efc4c8 Improve design system 2025-09-08 15:52:19 +02:00
kbe
a101885d87 Restyle of the homepage 2025-09-08 15:19:57 +02:00
kbe
0b6eec0c7b In mailer use application name
Some checks failed
CI / scan_ruby (pull_request) Successful in 5m46s
CI / lint (pull_request) Failing after 39s
CI / test (pull_request) Failing after 1m22s
2025-09-08 12:38:40 +02:00
kbe
8f9795d773 Fix failing tests and improve email template consistency
- Fix onboarding controller test by using consistent application name
- Fix ticket mailer template error by correcting variable reference (@user.first_name)
- Update event reminder template to use configurable app name
- Refactor mailer tests to properly handle multipart email content
- Update test assertions to match actual template content
- Remove duplicate migration for onboarding field
- Add documentation for test fixes and solutions
2025-09-08 12:36:33 +02:00
kbe
d1308bc988 Remove company information section from onboarding
Completely remove the enterprise/company information functionality from
the onboarding flow to simplify the user experience:

- Remove company information toggle section and form fields from view
- Delete unused Stimulus toggle controller (toggle_section_controller.js)
- Update onboarding controller to only process first/last name parameters
- Remove company_name from permitted parameters and validation logic
- Update tests to remove company name assertions and test cases
- Simplify onboarding to only collect essential personal information

The onboarding now focuses solely on collecting required first and last
names, providing a cleaner and faster user experience.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-08 11:41:43 +02:00
kbe
758d461c1a feat: Implement comprehensive onboarding system for new users
Add complete user onboarding flow that redirects new users to complete their
profile before accessing the application:

- Add onboarding_completed boolean field to users with migration
- Create OnboardingController with form validation and completion logic
- Design professional onboarding UI with progressive disclosure for company info
- Implement Stimulus controller for toggling company information section
- Add application-wide redirect middleware for incomplete users
- Create comprehensive test suite for all onboarding functionality
- Update test fixtures and helpers to support onboarding in existing tests

The onboarding collects required first/last name and optional company information.
Users are redirected to onboarding after login until profile is completed.
Features smooth animations, full-width form button, and clean UX design.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-08 11:38:28 +02:00
kbe
67d3bcde5b Fix service fee missing from Stripe invoices
The StripeInvoiceService was only creating line items for tickets but missing
the 1€ service fee, causing a discrepancy where customers paid 26€ via Stripe
checkout but the generated invoice only showed 25€.

- Add service fee line item to Stripe invoices in StripeInvoiceService
- Update all related tests to expect two line items (tickets + service fee)
- Fix order controller test to account for service fee in total calculation

Now Stripe invoices properly match the amount paid: tickets + 1€ service fee.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-08 11:15:36 +02:00
kbe
bc214867b0 Add invoice functionality for orders with Stripe integration 2025-09-08 10:55:36 +02:00
kbe
4bc40967c8 feat: Internal invoice generation
- TODO: make use of Stripe invoice
2025-09-08 09:42:22 +02:00
kbe
039ae7d1f8 Add 1€ service fee to all order-related pages and Stripe integration
- Added 1€ service fee to order total calculation in Order model
- Updated checkout page to display fee breakdown (subtotal + 1€ fee = total)
- Updated payment success page to show fee breakdown
- Updated order show page to display fee breakdown
- Updated payment cancel page to show fee breakdown
- Modified Stripe session creation to include service fee as separate line item
- Updated order model tests to account for the 1€ service fee
- Enhanced overall pricing transparency for users

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2025-09-07 01:29:24 +02:00
kbe
f285d689b4 Make the flash message use the page width 2025-09-07 01:22:44 +02:00
17 changed files with 248 additions and 4470 deletions

View File

@@ -43,6 +43,8 @@
- [ ] feat: Fraud prevention and bot protection
- [ ] feat: Social login options
- [ ] feat: Event recommendations system
- [ ] feat: Invitation link. As organizer or promoter, you can invite people
### Design & Infrastructure

View File

@@ -211,6 +211,8 @@ GEM
racc (~> 1.4)
nokogiri (1.18.9-arm-linux-musl)
racc (~> 1.4)
nokogiri (1.18.9-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.18.9-x86_64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.9-x86_64-linux-musl)
@@ -360,6 +362,7 @@ GEM
sqlite3 (2.7.3-aarch64-linux-musl)
sqlite3 (2.7.3-arm-linux-gnu)
sqlite3 (2.7.3-arm-linux-musl)
sqlite3 (2.7.3-x86_64-darwin)
sqlite3 (2.7.3-x86_64-linux-gnu)
sqlite3 (2.7.3-x86_64-linux-musl)
sshkit (1.24.0)
@@ -376,6 +379,7 @@ GEM
thor (1.4.0)
thruster (0.1.15)
thruster (0.1.15-aarch64-linux)
thruster (0.1.15-x86_64-darwin)
thruster (0.1.15-x86_64-linux)
timeout (0.4.3)
ttfunk (1.8.0)
@@ -412,6 +416,7 @@ PLATFORMS
aarch64-linux-musl
arm-linux-gnu
arm-linux-musl
x86_64-darwin-24
x86_64-linux-gnu
x86_64-linux-musl

View File

@@ -4,7 +4,9 @@
module Api
module V1
class OrdersController < ApiController
before_action :authenticate_user!
# Skip API key authentication for store_cart action (used by frontend forms)
skip_before_action :authenticate_api_key, only: [ :store_cart ]
before_action :set_order, only: [ :show, :checkout, :retry_payment, :increment_payment_attempt ]
before_action :set_event, only: [ :new, :create ]

View File

@@ -47,7 +47,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
# If you have extra params to permit, append them to the sanitizer.
def configure_account_update_params
devise_parameter_sanitizer.permit(:account_update, keys: [ :last_name, :first_name ])
devise_parameter_sanitizer.permit(:account_update, keys: [ :last_name, :first_name, :is_professionnal ])
end
# The path used after sign up.

View File

@@ -71,7 +71,6 @@ 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
revenue = current_user.events
.joins(:orders)
.where(orders: { status: [ "paid", "completed" ] })

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

@@ -0,0 +1,26 @@
class SettingsController < ApplicationController
before_action :authenticate_user!
before_action :set_user
def show
# Show settings page
end
def update
if @user.update(user_params)
redirect_to settings_path, notice: "Vos informations ont été mises à jour avec succès."
else
render :show, status: :unprocessable_entity
end
end
private
def set_user
@user = current_user
end
def user_params
params.require(:user).permit(:first_name, :last_name, :is_professionnal)
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
@@ -124,8 +150,8 @@ class Event < ApplicationRecord
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['User-Agent'] = 'AperoNight Event Platform/1.0 (https://aperonight.com)'
request['Accept'] = 'application/json'
request["User-Agent"] = "AperoNight Event Platform/1.0 (https://aperonight.com)"
request["Accept"] = "application/json"
response = http.request(request)

View File

@@ -58,9 +58,14 @@
<i data-lucide="calendar" class="w-4 h-4 mr-3"></i>
Réservations
<% end %>
<%= link_to settings_path,
class: "flex items-center px-4 py-3 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-200" do %>
<i data-lucide="user" class="w-4 h-4 mr-3"></i>
Profil
<% end %>
<%= link_to edit_user_registration_path,
class: "flex items-center px-4 py-3 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-200" do %>
<i data-lucide="settings" class="w-4 h-4 mr-3"></i>
<i data-lucide="key" class="w-4 h-4 mr-3"></i>
Sécurité
<% end %>
<div class="border-t border-gray-100">
@@ -149,9 +154,14 @@
<i data-lucide="calendar" class="w-4 h-4 mr-3"></i>
Réservations
<% end %>
<%= link_to settings_path,
class: "flex items-center px-3 py-2 rounded-lg text-base font-medium text-gray-700 hover:text-brand-primary hover:bg-gray-50" do %>
<i data-lucide="user" class="w-4 h-4 mr-3"></i>
Profil
<% end %>
<%= link_to edit_user_registration_path,
class: "flex items-center px-3 py-2 rounded-lg text-base font-medium text-gray-700 hover:text-brand-primary hover:bg-gray-50" do %>
<i data-lucide="settings" class="w-4 h-4 mr-3"></i>
<i data-lucide="key" class="w-4 h-4 mr-3"></i>
Sécurité
<% end %>
<%= link_to destroy_user_session_path,

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="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="mt-2 text-sm text-gray-500">Les événements mis en avant apparaissent en premier sur la page d'accueil.</p>
<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>
</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

@@ -0,0 +1,115 @@
<div class="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div class="max-w-2xl mx-auto space-y-8">
<!-- Header -->
<div class="text-center">
<%= link_to "/" do %>
<div class="inline-flex items-center justify-center w-16 h-16 bg-gradient-to-br from-purple-600 to-blue-600 rounded-2xl mb-6 mx-auto">
<i data-lucide="calendar" class="w-8 h-8 text-white"></i>
</div>
<% end %>
<h2 class="text-3xl font-bold text-gray-900">Paramètres du profil</h2>
<p class="mt-2 text-gray-600">
Gérez vos informations personnelles et préférences
</p>
</div>
<!-- Profile Form -->
<div class="bg-white py-8 px-6 shadow-xl rounded-2xl">
<h3 class="text-xl font-semibold text-gray-900 mb-6">Informations personnelles</h3>
<%= form_with model: @user, url: settings_path, method: :patch, local: true, html: { class: "space-y-6" } do |f| %>
<% if @user.errors.any? %>
<div class="bg-red-50 border border-red-200 rounded-lg p-4">
<div class="flex">
<i data-lucide="alert-circle" class="w-5 h-5 text-red-400 mt-0.5 mr-3"></i>
<div>
<h3 class="text-sm font-medium text-red-800">Erreurs :</h3>
<ul class="mt-2 text-sm text-red-700 list-disc list-inside">
<% @user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</div>
</div>
<% end %>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<%= f.label :first_name, "Prénom", class: "block text-sm font-semibold text-gray-700 mb-2" %>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i data-lucide="user" class="w-5 h-5 text-gray-400"></i>
</div>
<%= f.text_field :first_name,
class: "block w-full pl-10 pr-3 py-3 border border-gray-300 rounded-xl shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition-colors",
placeholder: "Votre prénom" %>
</div>
</div>
<div>
<%= f.label :last_name, "Nom de famille", class: "block text-sm font-semibold text-gray-700 mb-2" %>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i data-lucide="user" class="w-5 h-5 text-gray-400"></i>
</div>
<%= f.text_field :last_name,
class: "block w-full pl-10 pr-3 py-3 border border-gray-300 rounded-xl shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition-colors",
placeholder: "Votre nom de famille" %>
</div>
</div>
</div>
<!-- Professional Account Toggle -->
<div class="border-t pt-6">
<h4 class="text-lg font-medium text-gray-900 mb-4">Type de compte</h4>
<div class="bg-gray-50 p-4 rounded-xl">
<div class="flex items-start space-x-3">
<%= f.check_box :is_professionnal,
class: "mt-1 h-5 w-5 text-purple-600 border-gray-300 rounded focus:ring-purple-500" %>
<div class="flex-1">
<%= f.label :is_professionnal, "Compte professionnel",
class: "block text-sm font-medium text-gray-900 cursor-pointer" %>
<p class="mt-1 text-sm text-gray-600">
Les comptes professionnels peuvent créer et gérer des événements.
Cette option vous permet d'accéder aux fonctionnalités de promotion d'événements.
</p>
</div>
</div>
</div>
</div>
<div class="pt-4">
<%= f.button type: "submit", class: "group relative w-full flex justify-center items-center py-3 px-4 border border-transparent text-sm font-semibold rounded-xl text-white bg-gray-900 hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 transition-all duration-200 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5" do %>
<i data-lucide="save" class="w-4 h-4 mr-2"></i>
Enregistrer les modifications
<% end %>
</div>
<% end %>
</div>
<!-- Navigation Links -->
<div class="bg-white py-6 px-6 shadow-xl rounded-2xl">
<h3 class="text-lg font-semibold text-gray-900 mb-4">Gestion du compte</h3>
<div class="space-y-3">
<%= link_to edit_user_registration_path,
class: "flex items-center p-3 text-gray-700 hover:bg-gray-50 rounded-lg transition-colors" do %>
<i data-lucide="key" class="w-5 h-5 mr-3 text-gray-400"></i>
<div>
<div class="font-medium">Sécurité du compte</div>
<div class="text-sm text-gray-500">Modifier l'email et le mot de passe</div>
</div>
<i data-lucide="chevron-right" class="w-5 h-5 ml-auto text-gray-400"></i>
<% end %>
</div>
</div>
<!-- Back Link -->
<div class="text-center">
<%= link_to :back, class: "inline-flex items-center text-purple-600 hover:text-purple-500 transition-colors" do %>
<i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i>
Retour
<% end %>
</div>
</div>
</div>

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=="],

View File

@@ -39,6 +39,10 @@ Rails.application.routes.draw do
# === Pages ===
get "dashboard", to: "pages#dashboard", as: "dashboard"
# === Settings ===
get "settings", to: "settings#show", as: "settings"
patch "settings", to: "settings#update"
# === Events ===
get "events", to: "events#index", as: "events"
get "events/:slug.:id", to: "events#show", as: "event"

View File

@@ -0,0 +1,5 @@
class AddAllowBookingDuringEventToEvents < ActiveRecord::Migration[8.0]
def change
add_column :events, :allow_booking_during_event, :boolean, default: false, null: false
end
end

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"

4457
package-lock.json generated

File diff suppressed because it is too large Load Diff