2 Commits

Author SHA1 Message Date
kbe
c1dde7914c refactor: remove 1€ fees on payment 2025-09-15 21:09:57 +02:00
kbe
dbb972e490 feat: Add countdown when order expire in less than 5 minutes 2025-09-15 21:09:19 +02:00
5 changed files with 89 additions and 18 deletions

View File

@@ -0,0 +1,71 @@
import { Controller } from "@hotwired/stimulus"
// Countdown controller for displaying remaining time until order expiration
export default class extends Controller {
static values = {
expiresAt: String, // ISO timestamp when the order expires
orderId: Number // Order ID for identification
}
connect() {
// Parse the expiration timestamp
this.expirationTime = new Date(this.expiresAtValue).getTime()
// Find the countdown element
this.countdownElement = this.element.querySelector('.countdown-timer')
if (this.countdownElement && !isNaN(this.expirationTime)) {
// Start the countdown
this.updateCountdown()
this.timer = setInterval(() => this.updateCountdown(), 1000)
}
}
disconnect() {
// Clean up the interval when the controller disconnects
if (this.timer) {
clearInterval(this.timer)
}
}
updateCountdown() {
const now = new Date().getTime()
const distance = this.expirationTime - now
// If the countdown is finished
if (distance < 0) {
this.countdownElement.innerHTML = "EXPIRÉ"
this.countdownElement.classList.add("text-red-600", "font-bold")
this.countdownElement.classList.remove("text-orange-600")
// Add a more urgent visual indicator
this.element.classList.add("bg-red-50", "border-red-200")
this.element.classList.remove("bg-orange-50", "border-orange-200")
// Stop the timer
if (this.timer) {
clearInterval(this.timer)
}
return
}
// Calculate time components
const seconds = Math.floor(distance / 1000)
// Display the result
this.countdownElement.innerHTML = `${seconds} secondes`
// Add urgency styling when time is running low
if (seconds < 60) {
this.countdownElement.classList.add("text-red-600", "font-bold")
this.countdownElement.classList.remove("text-orange-600")
// Add background warning for extra urgency
this.element.classList.add("bg-red-50", "border-red-200")
this.element.classList.remove("bg-orange-50", "border-orange-200")
} else if (seconds < 300) { // Less than 5 minutes
this.countdownElement.classList.add("text-orange-600", "font-bold")
this.element.classList.add("bg-orange-50", "border-orange-200")
}
}
}

View File

@@ -24,3 +24,6 @@ application.register("qr-code", QrCodeController);
import EventFormController from "./event_form_controller"; import EventFormController from "./event_form_controller";
application.register("event-form", EventFormController); application.register("event-form", EventFormController);
import CountdownController from "./countdown_controller";
application.register("countdown", CountdownController);

View File

@@ -135,10 +135,6 @@
<span class="text-gray-600">Sous-total</span> <span class="text-gray-600">Sous-total</span>
<span class="text-gray-900"><%= @order.total_amount_euros - 1.0 %>€</span> <span class="text-gray-900"><%= @order.total_amount_euros - 1.0 %>€</span>
</div> </div>
<div class="flex items-center justify-between">
<span class="text-gray-600">Frais de service</span>
<span class="text-gray-900">1.00€</span>
</div>
<div class="flex items-center justify-between text-lg pt-2 border-t border-gray-200"> <div class="flex items-center justify-between text-lg pt-2 border-t border-gray-200">
<span class="font-medium text-gray-900">Total à payer</span> <span class="font-medium text-gray-900">Total à payer</span>
<span class="font-bold text-2xl text-red-600"> <span class="font-bold text-2xl text-red-600">

View File

@@ -124,16 +124,8 @@
</div> </div>
<!-- Total --> <!-- Total -->
<div class="border-t border-gray-200 pt-6 mt-6"> <div class="mt-6">
<div class="space-y-2"> <div class="space-y-2">
<div class="flex items-center justify-between">
<span class="text-gray-600">Sous-total</span>
<span class="text-gray-900"><%= @order.total_amount_euros - 1.0 %>€</span>
</div>
<div class="flex items-center justify-between">
<span class="text-gray-600">Frais de service</span>
<span class="text-gray-900">1.00€</span>
</div>
<div class="flex items-center justify-between text-lg pt-2 border-t border-gray-200"> <div class="flex items-center justify-between text-lg pt-2 border-t border-gray-200">
<span class="font-medium text-gray-900">Total payé</span> <span class="font-medium text-gray-900">Total payé</span>
<span class="font-bold text-2xl text-green-600"> <span class="font-bold text-2xl text-green-600">
@@ -205,7 +197,7 @@
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
</svg> </svg>
Retour au Tableau de Bord Retour au tableau de bord
</div> </div>
<% end %> <% end %>
<%= link_to event_path(@order.event.slug, @order.event), class: "block w-full text-center py-3 px-4 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition-colors" do %> <%= link_to event_path(@order.event.slug, @order.event), class: "block w-full text-center py-3 px-4 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition-colors" do %>
@@ -213,7 +205,7 @@
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
</svg> </svg>
Voir l'Événement Complet Voir la fiche de l'événement
</div> </div>
<% end %> <% end %>
</div> </div>

View File

@@ -129,7 +129,10 @@
<% @promoter_events.each do |event| %> <% @promoter_events.each do |event| %>
<div class="border border-gray-200 rounded-xl p-4 hover:shadow-md transition-shadow"> <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 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> <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' %>"> <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 %> <%= event.state.humanize %>
</span> </span>
@@ -246,7 +249,12 @@
Tentatives: <%= order.payment_attempts %>/3 Tentatives: <%= order.payment_attempts %>/3
</div> </div>
<% if order.expiring_soon? %> <% if order.expiring_soon? %>
<span class="text-orange-600 font-medium">⚠️ Expire dans <%= time_ago_in_words(order.expires_at) %></span> <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 %> <% else %>
<span class="text-gray-500">Expire dans <%= time_ago_in_words(order.expires_at) %></span> <span class="text-gray-500">Expire dans <%= time_ago_in_words(order.expires_at) %></span>
<% end %> <% end %>
@@ -385,6 +393,7 @@
<% @upcoming_preview_events.each do |event| %> <% @upcoming_preview_events.each do |event| %>
<div class="bg-gray-50 rounded-xl p-4 hover:shadow-md transition-shadow"> <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> <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="text-sm text-gray-600 space-y-1">
<div class="flex items-center"> <div class="flex items-center">
<i data-lucide="calendar" class="w-4 h-4 mr-2"></i> <i data-lucide="calendar" class="w-4 h-4 mr-2"></i>