feat: Complete email notifications system with comprehensive functionality

- Implement comprehensive email notification system for ticket purchases and event reminders
- Add event reminder job with configurable scheduling
- Enhance ticket mailer with QR code generation and proper formatting
- Update order model with email delivery tracking
- Add comprehensive test coverage for all email functionality
- Configure proper mailer settings and disable annotations
- Update backlog to reflect completed email features

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
kbe
2025-09-06 20:21:01 +02:00
parent e983b68834
commit ce0752bbda
19 changed files with 462 additions and 270 deletions

View File

@@ -45,6 +45,7 @@
<div>
<p style="margin: 0 0 5px; font-weight: bold; color: #212529;">🎫 Billet #<%= index + 1 %></p>
<p style="margin: 0; color: #6c757d; font-size: 14px;"><%= ticket.ticket_type.name %></p>
<p style="margin: 5px 0 0;"><a href="<%= ticket_url(ticket) %>" style="color: #4c1d95; text-decoration: none; font-size: 14px;">📱 Voir le détail et le code QR</a></p>
</div>
<div style="text-align: right;">
<span style="background-color: #d4edda; color: #155724; padding: 4px 8px; border-radius: 4px; font-size: 12px; font-weight: bold;">ACTIF</span>

View File

@@ -1,12 +1,16 @@
<div style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8f9fa; border-radius: 8px;">
<div style="text-align: center; padding: 20px 0; border-bottom: 1px solid #e9ecef;">
<h1 style="color: #4c1d95; margin: 0; font-size: 28px;">ApéroNight</h1>
<h1 style="color: #4c1d95; margin: 0; font-size: 28px;"><%= ENV.fetch("APP_NAME", "Aperonight") %></h1>
<p style="color: #6c757d; margin: 10px 0 0;">Confirmation de votre achat</p>
</div>
<div style="background-color: white; border-radius: 8px; padding: 30px; margin: 20px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<h2 style="color: #212529; margin-top: 0;">Bonjour <%= @user.email.split('@').first %>,</h2>
<% if user.first_name %>
<h2 style="color: #212529; margin-top: 0;">Bonjour <%= @user.first_name %>,</h2>
<% else %>
<h2 style="color: #212529; margin-top: 0;">Bonjour <%= @user.email.split('@').first %>,</h2>
<% end %>
<p style="color: #495057; line-height: 1.6;">
<% if defined?(@order) && @order.present? %>
Merci pour votre achat ! Nous avons le plaisir de vous confirmer votre commande pour l'événement <strong><%= @event.name %></strong>.
@@ -14,11 +18,11 @@
Merci pour votre achat ! Nous avons le plaisir de vous confirmer votre billet pour l'événement <strong><%= @event.name %></strong>.
<% end %>
</p>
<div style="background-color: #f8f9fa; border-radius: 6px; padding: 20px; margin: 25px 0;">
<% if defined?(@order) && @order.present? %>
<h3 style="color: #4c1d95; margin-top: 0; border-bottom: 1px solid #e9ecef; padding-bottom: 10px;">Détails de votre commande</h3>
<div style="margin-bottom: 20px;">
<div style="display: flex; justify-content: space-between; margin-bottom: 15px;">
<div>
@@ -30,7 +34,7 @@
<p style="margin: 5px 0 0; font-weight: bold; color: #212529;"><%= @event.start_time.strftime("%d %B %Y à %H:%M") %></p>
</div>
</div>
<div style="display: flex; justify-content: space-between;">
<div>
<p style="margin: 0; color: #6c757d; font-size: 14px;">Nombre de billets</p>
@@ -50,6 +54,7 @@
<div>
<p style="margin: 0 0 5px; font-weight: bold; color: #212529;">Billet #<%= index + 1 %></p>
<p style="margin: 0; color: #6c757d; font-size: 14px;"><%= ticket.ticket_type.name %></p>
<p style="margin: 5px 0 0;"><a href="<%= ticket_url(ticket) %>" style="color: #4c1d95; text-decoration: none; font-size: 14px;">📱 Voir le détail et le code QR</a></p>
</div>
<div style="text-align: right;">
<p style="margin: 0; font-weight: bold; color: #212529;"><%= number_to_currency(ticket.price_cents / 100.0, unit: "€") %></p>
@@ -59,7 +64,7 @@
<% end %>
<% else %>
<h3 style="color: #4c1d95; margin-top: 0; border-bottom: 1px solid #e9ecef; padding-bottom: 10px;">Détails de votre billet</h3>
<div style="display: flex; justify-content: space-between; margin-bottom: 15px;">
<div>
<p style="margin: 0; color: #6c757d; font-size: 14px;">Événement</p>
@@ -70,7 +75,7 @@
<p style="margin: 5px 0 0; font-weight: bold; color: #212529;"><%= @ticket.ticket_type.name %></p>
</div>
</div>
<div style="display: flex; justify-content: space-between;">
<div>
<p style="margin: 0; color: #6c757d; font-size: 14px;">Date & heure</p>
@@ -81,9 +86,13 @@
<p style="margin: 5px 0 0; font-weight: bold; color: #212529;"><%= number_to_currency(@ticket.price_cents / 100.0, unit: "€") %></p>
</div>
</div>
<div style="margin-top: 15px; text-align: center;">
<a href="<%= ticket_url(@ticket) %>" style="color: #4c1d95; text-decoration: none; font-size: 14px; display: inline-block; padding: 10px 15px; border: 1px solid #4c1d95; border-radius: 6px; background-color: #f8f9fa;">📱 Voir le détail et le code QR</a>
</div>
<% end %>
</div>
<div style="text-align: center; margin: 30px 0;">
<% if defined?(@order) && @order.present? %>
<p style="color: #495057; margin-bottom: 20px;">Vos billets sont attachés à cet email en format PDF.</p>
@@ -93,10 +102,10 @@
<p style="color: #495057; margin-bottom: 20px;">Présentez-le à l'entrée de l'événement pour y accéder.</p>
<% end %>
</div>
<div style="background-color: #fff3cd; border-radius: 6px; padding: 15px; border-left: 4px solid #ffc107;">
<p style="margin: 0; color: #856404; font-size: 14px;">
<strong>Important :</strong>
<strong>Important :</strong>
<% if defined?(@order) && @order.present? %>
Ces billets sont valables pour une seule entrée chacun. Conservez-les précieusement.
<% else %>
@@ -105,9 +114,9 @@
</p>
</div>
</div>
<div style="text-align: center; color: #6c757d; font-size: 14px; padding: 20px 0;">
<p style="margin: 0;">Si vous avez des questions, contactez-nous à <a href="mailto:support@aperonight.com" style="color: #4c1d95; text-decoration: none;">support@aperonight.com</a></p>
<p style="margin: 10px 0 0;">&copy; <%= Time.current.year %> ApéroNight. Tous droits réservés.</p>
</div>
</div>
</div>

View File

@@ -1,4 +1,8 @@
Bonjour <%= @user.email.split('@').first %>,
<% if @user.first_name %>
Bonjour <%= @user.first_name %>,
<% else %>
Bonjour <%= @user.email.split('@').first %>,
<% end %>
<% if defined?(@order) && @order.present? %>
Merci pour votre achat ! Nous avons le plaisir de vous confirmer votre commande pour l'événement "<%= @event.name %>".
@@ -37,4 +41,4 @@ Important : Ce billet est valable pour une seule entrée. Conservez-le précieus
Si vous avez des questions, contactez-nous à support@aperonight.com
© <%= Time.current.year %> ApéroNight. Tous droits réservés.
© <%= Time.current.year %> <%= ENV.fetch("APP_NAME", "Aperonight") %>. Tous droits réservés.