Files
aperonight/app/views/pages/dashboard.html.erb
kbe e4509b1c43 feat: enhance dashboard and event pages with ended events payout functionality
- Replace 'Recent Ticket Sales' section with 'Événements Terminés' on dashboard
- Add dedicated section for ended events with payout request capabilities
- Display event revenue, ticket sales count, and payout status for ended events
- Implement payout request buttons with status indicators (pending, processing, completed, failed)
- Translate payout section in individual event page to French
- Add visual indicators and confirmations for payout requests
- Improve UX with color-coded status badges and action buttons

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-17 01:37:36 +02:00

522 lines
27 KiB
Plaintext
Executable File

<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Breadcrumb -->
<!-- Breadcrumb -->
<%= render 'components/breadcrumb', crumbs: [
{ name: 'Accueil', path: root_path },
{ name: 'Tableau de bord', path: dashboard_path }
] %>
<!-- Page Header -->
<div class="mb-8">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div>
<h1 class="text-3xl font-bold text-gray-900">Mon tableau de bord</h1>
<p class="text-gray-600 mt-1">Gérez vos commandes et accédez à vos billets</p>
</div>
<!-- Promoter Actions -->
<% if current_user.promoter? %>
<div class="flex flex-col xs:flex-row items-stretch xs:items-center gap-2">
<%= link_to promoter_events_path, class: "inline-flex items-center justify-center px-4 py-2 bg-purple-600 text-white font-medium rounded-lg hover:bg-purple-700 transition-colors duration-200" do %>
<i data-lucide="calendar-plus" class="w-4 h-4 mr-2"></i>
Mes Événements
<% end %>
<%= link_to new_promoter_event_path, class: "inline-flex items-center justify-center px-4 py-2 bg-gray-900 text-white font-medium rounded-lg hover:bg-gray-800 transition-colors duration-200" do %>
<i data-lucide="plus" class="w-4 h-4 mr-2"></i>
Créer un Événement
<% end %>
</div>
<% else %>
<%= link_to events_path, class: "inline-flex items-center justify-center px-4 py-2 bg-purple-600 text-white font-medium rounded-lg hover:bg-purple-700 transition-colors duration-200" do %>
<i data-lucide="search" class="w-4 h-4 mr-2"></i>
Découvrir des Événements
<% end %>
<% end %>
</div>
</div>
<!-- Promoter Dashboard Section -->
<% if current_user.promoter? && @promoter_events.present? %>
<!-- Promoter Metrics -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6 mb-8">
<div class="bg-gradient-to-br from-green-50 to-green-100 rounded-2xl p-6 border border-green-200">
<div class="flex items-center justify-between">
<div>
<p class="text-green-600 text-sm font-medium">Revenus Total</p>
<p class="text-2xl font-bold text-green-900">€<%= number_with_delimiter(@total_revenue, delimiter: ' ') %></p>
</div>
<div class="bg-green-200 rounded-full p-3">
<i data-lucide="euro" class="w-6 h-6 text-green-700"></i>
</div>
</div>
</div>
<div class="bg-gradient-to-br from-blue-50 to-blue-100 rounded-2xl p-6 border border-blue-200">
<div class="flex items-center justify-between">
<div>
<p class="text-blue-600 text-sm font-medium">Billets Vendus</p>
<p class="text-2xl font-bold text-blue-900"><%= @total_tickets_sold %></p>
</div>
<div class="bg-blue-200 rounded-full p-3">
<i data-lucide="ticket" class="w-6 h-6 text-blue-700"></i>
</div>
</div>
</div>
<div class="bg-gradient-to-br from-purple-50 to-purple-100 rounded-2xl p-6 border border-purple-200">
<div class="flex items-center justify-between">
<div>
<p class="text-purple-600 text-sm font-medium">Événements Publiés</p>
<p class="text-2xl font-bold text-purple-900"><%= @active_events_count %></p>
</div>
<div class="bg-purple-200 rounded-full p-3">
<i data-lucide="calendar-check" class="w-6 h-6 text-purple-700"></i>
</div>
</div>
</div>
<div class="bg-gradient-to-br from-orange-50 to-orange-100 rounded-2xl p-6 border border-orange-200">
<div class="flex items-center justify-between">
<div>
<p class="text-orange-600 text-sm font-medium">Brouillons</p>
<p class="text-2xl font-bold text-orange-900"><%= @draft_events_count %></p>
</div>
<div class="bg-orange-200 rounded-full p-3">
<i data-lucide="edit-3" class="w-6 h-6 text-orange-700"></i>
</div>
</div>
</div>
<!-- Payout Summary -->
<div class="bg-gradient-to-br from-indigo-50 to-indigo-100 rounded-2xl p-6 border border-indigo-200">
<div class="flex items-center justify-between">
<div>
<p class="text-indigo-600 text-sm font-medium">Paiements en Attente</p>
<p class="text-2xl font-bold text-indigo-900"><%= current_user.payouts.pending.count %></p>
</div>
<div class="bg-indigo-200 rounded-full p-3">
<i data-lucide="dollar-sign" class="w-6 h-6 text-indigo-700"></i>
</div>
</div>
</div>
</div>
<!-- Revenue Chart & Recent Events -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-8">
<!-- Monthly Revenue Chart -->
<div class="lg:col-span-2 bg-white rounded-2xl shadow-lg">
<div class="border-b border-gray-100 p-6">
<h2 class="text-xl font-bold text-gray-900">Revenus Mensuels</h2>
<p class="text-gray-600 mt-1">Derniers 6 mois</p>
</div>
<div class="p-6">
<div class="space-y-3">
<% @monthly_revenue.each do |month_data| %>
<div class="flex items-center justify-between">
<span class="text-sm font-medium text-gray-700"><%= month_data[:month] %></span>
<div class="flex items-center space-x-2">
<div class="w-32 bg-gray-200 rounded-full h-3 relative">
<div class="bg-green-500 h-3 rounded-full" style="width: <%= [month_data[:revenue] / ([@monthly_revenue.max_by{|m| m[:revenue]}[:revenue], 1].max) * 100, 5].max %>%"></div>
</div>
<span class="text-sm font-bold text-gray-900 w-16 text-right">€<%= number_with_delimiter(month_data[:revenue], delimiter: ' ') %></span>
</div>
</div>
<% end %>
</div>
</div>
</div>
<!-- Recent Events -->
<div class="bg-white rounded-2xl shadow-lg">
<div class="border-b border-gray-100 p-6">
<div class="flex items-center justify-between">
<h2 class="text-xl font-bold text-gray-900">Mes Événements</h2>
<%= link_to promoter_events_path, class: "text-purple-600 hover:text-purple-800 font-medium text-sm" do %>
Voir tout →
<% end %>
</div>
</div>
<div class="p-6">
<div class="space-y-4">
<% @promoter_events.each do |event| %>
<div class="border border-gray-200 rounded-xl p-4 hover:shadow-md transition-shadow">
<div class="flex items-start justify-between mb-2">
<%= link_to promoter_event_path(event) do %>
<h4 class="font-semibold text-gray-900 text-sm"><%= event.name %></h4>
<% end %>
<span class="text-xs px-2 py-1 rounded-full <%= event.state == 'published' ? 'bg-green-100 text-green-800' : event.state == 'draft' ? 'bg-yellow-100 text-yellow-800' : 'bg-red-100 text-red-800' %>">
<%= event.state.humanize %>
</span>
</div>
<div class="text-xs text-gray-600 space-y-1">
<div class="flex items-center">
<i data-lucide="calendar" class="w-3 h-3 mr-2"></i>
<%= event.start_time&.strftime("%d %B %Y") || "Non programmé" %>
</div>
<div class="flex items-center">
<i data-lucide="ticket" class="w-3 h-3 mr-2"></i>
<%= event.tickets.where(status: 'active').count %> billets vendus
</div>
</div>
<div class="mt-3 flex flex-wrap gap-2">
<%= link_to promoter_event_path(event), class: "text-purple-600 hover:text-purple-800 text-xs font-medium" do %>
Gérer →
<% end %>
<% if event.event_ended? && event.can_request_payout? %>
<% if event.payout_status == "not_requested" %>
<%= link_to "Demander le paiement", promoter_payouts_path(event_id: event.id), method: :post,
class: "text-green-600 hover:text-green-800 text-xs font-medium inline-flex items-center",
data: { confirm: "Êtes-vous sûr de vouloir demander le paiement de €#{event.net_earnings_cents / 100.0} ?" } do %>
<i data-lucide="dollar-sign" class="w-3 h-3 mr-1"></i>
Paiement
<% end %>
<% elsif event.payout_status == "requested" %>
<%= link_to "Paiement demandé", promoter_payouts_path,
class: "text-yellow-600 hover:text-yellow-800 text-xs font-medium inline-flex items-center" do %>
<i data-lucide="clock" class="w-3 h-3 mr-1"></i>
En attente
<% end %>
<% elsif event.payout_status == "processing" %>
<%= link_to "Paiement en cours", promoter_payouts_path,
class: "text-blue-600 hover:text-blue-800 text-xs font-medium inline-flex items-center" do %>
<i data-lucide="refresh-cw" class="w-3 h-3 mr-1"></i>
Traitement
<% end %>
<% elsif event.payout_status == "completed" %>
<%= link_to "Paiement effectué", promoter_payouts_path,
class: "text-green-600 hover:text-green-800 text-xs font-medium inline-flex items-center" do %>
<i data-lucide="check-circle" class="w-3 h-3 mr-1"></i>
Complété
<% end %>
<% elsif event.payout_status == "failed" %>
<%= link_to "Paiement échoué", promoter_payouts_path(event_id: event.id), method: :post,
class: "text-red-600 hover:text-red-800 text-xs font-medium inline-flex items-center",
data: { confirm: "Êtes-vous sûr de vouloir redemander le paiement ?" } do %>
<i data-lucide="x-circle" class="w-3 h-3 mr-1"></i>
Réessayer
<% end %>
<% end %>
<% elsif event.event_ended? %>
<span class="text-gray-500 text-xs font-medium inline-flex items-center">
<i data-lucide="dollar-sign" class="w-3 h-3 mr-1"></i>
Aucun revenu
</span>
<% end %>
</div>
</div>
<% end %>
</div>
<div class="mt-4 text-center">
<%= link_to new_promoter_event_path, class: "inline-flex items-center px-4 py-2 bg-gray-900 text-white text-sm font-medium rounded-lg hover:bg-gray-800 transition-colors" do %>
<i data-lucide="plus" class="w-4 h-4 mr-2"></i>
Nouvel Événement
<% end %>
</div>
</div>
</div>
</div>
<!-- Ended Events Requiring Payout -->
<% ended_events = @promoter_events.select(&:event_ended?) %>
<% if ended_events.any? %>
<div class="bg-white rounded-2xl shadow-lg mb-8">
<div class="border-b border-gray-100 p-6">
<div class="flex items-center justify-between">
<h2 class="text-xl font-bold text-gray-900">Événements Terminés</h2>
<%= link_to "Voir tous les paiements", promoter_payouts_path, class: "text-purple-600 hover:text-purple-800 font-medium text-sm" %>
</div>
</div>
<div class="p-6">
<div class="space-y-4">
<% ended_events.each do |event| %>
<div class="border border-gray-200 rounded-xl p-4 hover:shadow-md transition-shadow">
<div class="flex items-start justify-between mb-2">
<div>
<h4 class="font-semibold text-gray-900 text-sm"><%= event.name %></h4>
<p class="text-xs text-gray-500 mt-1">Terminé le <%= event.end_time&.strftime("%d %B %Y") || event.start_time&.strftime("%d %B %Y") %></p>
</div>
<span class="text-xs px-2 py-1 rounded-full bg-gray-100 text-gray-800">
Terminé
</span>
</div>
<div class="text-xs text-gray-600 space-y-1">
<div class="flex items-center">
<i data-lucide="ticket" class="w-3 h-3 mr-2"></i>
<%= event.tickets.where(status: 'active').count %> billets vendus
</div>
<div class="flex items-center">
<i data-lucide="euro" class="w-3 h-3 mr-2"></i>
Revenus: €<%= event.net_earnings_cents / 100.0 %>
</div>
</div>
<div class="mt-3 flex items-center justify-between">
<%= link_to promoter_event_path(event), class: "text-purple-600 hover:text-purple-800 text-xs font-medium" do %>
Voir l'événement →
<% end %>
<% if event.can_request_payout? %>
<% if event.payout_status == "not_requested" %>
<%= link_to "Demander le paiement", promoter_payouts_path(event_id: event.id), method: :post,
class: "inline-flex items-center px-3 py-1 bg-green-600 text-white text-xs font-medium rounded-lg hover:bg-green-700 transition-colors",
data: { confirm: "Êtes-vous sûr de vouloir demander le paiement de €#{event.net_earnings_cents / 100.0} ?" } do %>
<i data-lucide="dollar-sign" class="w-3 h-3 mr-1"></i>
Demander paiement
<% end %>
<% elsif event.payout_status == "requested" %>
<%= link_to promoter_payouts_path, class: "inline-flex items-center px-3 py-1 bg-yellow-600 text-white text-xs font-medium rounded-lg" do %>
<i data-lucide="clock" class="w-3 h-3 mr-1"></i>
En attente
<% end %>
<% elsif event.payout_status == "processing" %>
<%= link_to promoter_payouts_path, class: "inline-flex items-center px-3 py-1 bg-blue-600 text-white text-xs font-medium rounded-lg" do %>
<i data-lucide="refresh-cw" class="w-3 h-3 mr-1"></i>
En traitement
<% end %>
<% elsif event.payout_status == "completed" %>
<%= link_to promoter_payouts_path, class: "inline-flex items-center px-3 py-1 bg-green-600 text-white text-xs font-medium rounded-lg" do %>
<i data-lucide="check-circle" class="w-3 h-3 mr-1"></i>
Payé
<% end %>
<% elsif event.payout_status == "failed" %>
<%= link_to "Réessayer", promoter_payouts_path(event_id: event.id), method: :post,
class: "inline-flex items-center px-3 py-1 bg-red-600 text-white text-xs font-medium rounded-lg hover:bg-red-700 transition-colors",
data: { confirm: "Êtes-vous sûr de vouloir redemander le paiement ?" } do %>
<i data-lucide="x-circle" class="w-3 h-3 mr-1"></i>
Réessayer
<% end %>
<% end %>
<% else %>
<span class="text-gray-500 text-xs font-medium inline-flex items-center">
<i data-lucide="dollar-sign" class="w-3 h-3 mr-1"></i>
Aucun revenu
</span>
<% end %>
</div>
</div>
<% end %>
</div>
</div>
</div>
<% end %>
<% end %>
<!-- Draft orders needing payment -->
<% if @draft_orders.any? %>
<div class="bg-orange-50 border border-orange-200 rounded-2xl shadow-lg mb-8">
<div class="bg-orange-100 rounded-t-2xl p-4 sm:p-6">
<h2 class="text-xl sm:text-2xl font-bold text-orange-900 flex items-center">
<i data-lucide="alert-triangle" class="w-5 h-5 sm:w-6 sm:h-6 mr-2 text-orange-600"></i>
Commandes en Attente de Paiement
</h2>
<p class="text-gray-700 mt-1">Vous avez des commandes qui nécessitent un paiement</p>
</div>
<div class="p-4 sm:p-6">
<div class="space-y-4">
<% @draft_orders.each do |order| %>
<div class="bg-white rounded-xl p-4 border border-orange-200">
<div class="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3 mb-3">
<div>
<h3 class="font-semibold text-gray-900 text-base sm:text-lg"><%= order.event.name %></h3>
<p class="text-sm text-gray-600 mt-1 flex items-center">
<i data-lucide="calendar" class="w-4 h-4 mr-2"></i>
<%= order.event.start_time.strftime("%d %B %Y à %H:%M") %>
</p>
</div>
<span class="text-sm font-medium text-orange-600 bg-orange-100 px-3 py-1 rounded-full whitespace-nowrap">
Order #<%= order.id %>
</span>
</div>
<div class="grid gap-2 mb-4">
<% order.tickets.each do |ticket| %>
<div class="flex flex-col sm:flex-row sm:items-center justify-between text-sm bg-gray-50 rounded-lg p-3 gap-2">
<div>
<span class="font-medium"><%= ticket.ticket_type.name %></span>
<span class="text-gray-600">- <%= ticket.first_name %> <%= ticket.last_name %></span>
</div>
<div class="flex items-center space-x-2">
<span class="font-medium text-gray-900"><%= number_to_currency(ticket.price_euros, unit: "€") %></span>
</div>
</div>
<% end %>
</div>
<div class="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3">
<div class="text-sm text-gray-600">
<div class="mb-1 sm:mb-0">
Tentatives: <%= order.payment_attempts %>/3
</div>
<% if order.expiring_soon? %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-orange-50 border border-orange-200 text-orange-600"
data-controller="countdown"
data-countdown-expires-at-value="<%= order.expires_at.iso8601 %>"
data-countdown-order-id-value="<%= order.id %>">
⚠️ Expire dans <span class="countdown-timer ml-1 font-bold"></span>
</span>
<% else %>
<span class="text-gray-500">Expire dans <%= time_ago_in_words(order.expires_at) %></span>
<% end %>
</div>
<%= link_to retry_payment_order_path(order), method: :post,
class: "inline-flex items-center px-4 py-2 bg-orange-600 text-white text-sm font-medium rounded-lg hover:bg-orange-700 transition-colors duration-200 whitespace-nowrap" do %>
<i data-lucide="credit-card" class="w-4 h-4 mr-2"></i>
Reprendre le Paiement (€<%= order.total_amount_euros %>)
<% end %>
</div>
</div>
<% end %>
</div>
</div>
</div>
<% end %>
<!-- User's Orders Section -->
<div class="bg-white rounded-2xl shadow-lg mb-8">
<div class="border-b border-gray-100 p-4 sm:p-6">
<div class="flex items-center justify-between">
<h2 class="text-xl sm:text-2xl font-bold text-gray-900">Mes Commandes</h2>
<span class="text-sm text-gray-600 bg-gray-100 px-3 py-1 rounded-full">
<%= pluralize(@user_orders.count, 'commande') %>
</span>
</div>
</div>
<div class="p-4 sm:p-6">
<% if @user_orders.any? %>
<div class="space-y-4">
<% @user_orders.each do |order| %>
<div class="bg-gray-50 rounded-xl p-4 hover:shadow-md transition-shadow">
<div class="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3 mb-3">
<div class="flex-1">
<div class="flex flex-wrap items-center gap-2 mb-2">
<h3 class="font-semibold text-gray-900 text-base sm:text-lg"><%= order.event.name %></h3>
<span class="text-xs px-2 py-1 rounded-full <%= order.status == 'paid' ? 'bg-green-100 text-green-800' : order.status == 'completed' ? 'bg-blue-100 text-blue-800' : 'bg-yellow-100 text-yellow-800' %>">
<%= order.status.humanize %>
</span>
</div>
<div class="flex flex-wrap items-center gap-3 text-sm text-gray-600 mb-2">
<div class="flex items-center">
<i data-lucide="calendar" class="w-4 h-4 mr-2"></i>
<%= order.event.start_time.strftime("%d %B %Y à %H:%M") %>
</div>
<div class="flex items-center">
<i data-lucide="map-pin" class="w-4 h-4 mr-2"></i>
<%= order.event.venue_name %>
</div>
<div class="flex items-center">
<i data-lucide="shopping-bag" class="w-4 h-4 mr-2"></i>
<%= pluralize(order.tickets.count, 'billet') %>
</div>
</div>
<div class="text-sm text-gray-500 mt-2">
Order #<%= order.id %> • <%= order.created_at.strftime("%m/%d/%Y") %> • €<%= order.total_amount_euros %>
</div>
</div>
<div class="flex items-center">
<%= link_to order_path(order),
class: "inline-flex items-center px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white text-sm font-medium rounded-lg transition-colors duration-200 whitespace-nowrap" do %>
<i data-lucide="eye" class="w-4 h-4 mr-2"></i>
Voir les Détails
<% end %>
</div>
</div>
<!-- Quick tickets preview -->
<div class="border-t border-gray-200 pt-3">
<div class="grid gap-2">
<% order.tickets.limit(3).each do |ticket| %>
<div class="flex flex-col sm:flex-row sm:items-center justify-between text-sm bg-white rounded-lg p-3 gap-2">
<div class="flex items-center space-x-2">
<span class="w-2 h-2 bg-green-500 rounded-full"></span>
<span class="font-medium"><%= ticket.ticket_type.name %></span>
<span class="text-gray-500 text-sm">- <%= ticket.first_name %> <%= ticket.last_name %></span>
</div>
<div class="flex items-center space-x-2">
<%= link_to ticket_download_path(ticket.qr_code),
class: "text-purple-600 hover:text-purple-800" do %>
<i data-lucide="download" class="w-4 h-4"></i>
<% end %>
</div>
</div>
<% end %>
<% if order.tickets.count > 3 %>
<div class="text-xs text-gray-500 text-center">
et <%= pluralize(order.tickets.count - 3, 'autre billet') %>
</div>
<% end %>
</div>
</div>
</div>
<% end %>
</div>
<% if @user_orders.count >= 10 %>
<div class="mt-6 text-center">
<%= link_to "Voir Toutes Mes Commandes", orders_path, class: "text-purple-600 hover:text-purple-800 font-medium transition-colors duration-200" %>
</div>
<% end %>
<% else %>
<div class="text-center py-12">
<div class="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4">
<i data-lucide="shopping-bag" class="w-8 h-8 text-gray-400"></i>
</div>
<h3 class="text-lg font-medium text-gray-900 mb-2">Aucune Commande</h3>
<p class="text-gray-600 mb-6">Vous n'avez pas encore passé de commandes.</p>
<%= link_to events_path, class: "inline-flex items-center px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors duration-200" do %>
<i data-lucide="search" class="w-4 h-4 mr-2"></i>
Découvrir des Événements
<% end %>
</div>
<% end %>
</div>
</div>
<!-- Quick Events Preview -->
<% if @user_orders.any? %>
<div class="bg-white rounded-2xl shadow-lg">
<div class="border-b border-gray-100 p-4 sm:p-6">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
<h2 class="text-lg sm:text-xl font-bold text-gray-900">Découvrir d'autres événements</h2>
<%= link_to events_path, class: "text-purple-600 hover:text-purple-800 font-medium transition-colors duration-200 whitespace-nowrap" do %>
Voir tout →
<% end %>
</div>
</div>
<div class="p-4 sm:p-6">
<% if @upcoming_preview_events.any? %>
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
<% @upcoming_preview_events.each do |event| %>
<div class="bg-gray-50 rounded-xl p-4 hover:shadow-md transition-shadow">
<h4 class="font-medium text-gray-900 mb-2 text-base"><%= event.name %></h4>
<div class="text-sm text-gray-600 space-y-1">
<div class="flex items-center">
<i data-lucide="calendar" class="w-4 h-4 mr-2"></i>
<%= event.start_time.strftime("%d %B") %>
</div>
<div class="flex items-center">
<i data-lucide="map-pin" class="w-4 h-4 mr-2"></i>
<%= event.venue_name %>
</div>
</div>
<div class="mt-3">
<%= link_to event_path(event.slug, event), class: "text-purple-600 hover:text-purple-800 text-sm font-medium" do %>
Voir l'Événement →
<% end %>
</div>
</div>
<% end %>
</div>
<% else %>
<p class="text-gray-600">Aucun événement à venir pour le moment.</p>
<% end %>
</div>
</div>
<% end %>
</div>