Add duplication options with JavaScript modal and conditional ticket type cloning

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
kbe
2025-09-15 21:20:22 +02:00
parent aa68885b84
commit a34eb7aa38
5 changed files with 189 additions and 10 deletions

View File

@@ -95,7 +95,8 @@ class Promoter::EventsController < ApplicationController
# Duplicate an event and all its ticket types # Duplicate an event and all its ticket types
def duplicate def duplicate
@new_event = @event.duplicate clone_ticket_types = params[:clone_ticket_types] == "true"
@new_event = @event.duplicate(clone_ticket_types: clone_ticket_types)
if @new_event if @new_event
redirect_to edit_promoter_event_path(@new_event), notice: "Événement dupliqué avec succès! Vous pouvez maintenant modifier les détails de l'événement copié." redirect_to edit_promoter_event_path(@new_event), notice: "Événement dupliqué avec succès! Vous pouvez maintenant modifier les détails de l'événement copié."

View File

@@ -103,7 +103,7 @@ class Event < ApplicationRecord
end end
# Duplicate an event with all its ticket types # Duplicate an event with all its ticket types
def duplicate def duplicate(clone_ticket_types: true)
# Duplicate the event # Duplicate the event
new_event = self.dup new_event = self.dup
new_event.name = "Copie de #{name}" new_event.name = "Copie de #{name}"
@@ -114,11 +114,13 @@ class Event < ApplicationRecord
Event.transaction do Event.transaction do
if new_event.save if new_event.save
# Duplicate all ticket types # Duplicate all ticket types if requested
ticket_types.each do |ticket_type| if clone_ticket_types
new_ticket_type = ticket_type.dup ticket_types.each do |ticket_type|
new_ticket_type.event = new_event new_ticket_type = ticket_type.dup
new_ticket_type.save! new_ticket_type.event = new_event
new_ticket_type.save!
end
end end
new_event new_event
else else

View File

@@ -1,5 +1,63 @@
<% content_for(:title, @event.name) %> <% content_for(:title, @event.name) %>
<script>
document.addEventListener('DOMContentLoaded', function() {
const showDuplicateModalBtn = document.getElementById('showDuplicateModal');
const duplicateModal = document.getElementById('duplicateModal');
const cancelDuplicateBtn = document.getElementById('cancelDuplicate');
const confirmDuplicateBtn = document.getElementById('confirmDuplicate');
const cloneTicketTypesCheckbox = document.getElementById('cloneTicketTypes');
// Show modal when duplicate button is clicked
showDuplicateModalBtn.addEventListener('click', function() {
duplicateModal.classList.remove('hidden');
});
// Hide modal when cancel button is clicked
cancelDuplicateBtn.addEventListener('click', function() {
duplicateModal.classList.add('hidden');
});
// Hide modal when clicking outside the modal
window.addEventListener('click', function(event) {
if (event.target === duplicateModal) {
duplicateModal.classList.add('hidden');
}
});
// Handle duplication when confirm button is clicked
confirmDuplicateBtn.addEventListener('click', function() {
const cloneTicketTypes = cloneTicketTypesCheckbox.checked;
// Create form data
const formData = new FormData();
formData.append('clone_ticket_types', cloneTicketTypes);
formData.append('authenticity_token', document.querySelector('meta[name="csrf-token"]').getAttribute('content'));
// Send request to duplicate endpoint
fetch('<%= duplicate_promoter_event_path(@event) %>', {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => {
if (response.redirected) {
window.location.href = response.url;
} else {
return response.json();
}
})
.catch(error => {
console.error('Error:', error);
alert('Erreur lors de la duplication de l\'événement.');
duplicateModal.classList.add('hidden');
});
});
});
</script>
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Breadcrumb --> <!-- Breadcrumb -->
@@ -39,10 +97,58 @@
Modifier Modifier
<% end %> <% end %>
<%= button_to duplicate_promoter_event_path(@event), method: :post, class: "w-full sm:w-auto inline-flex items-center justify-center px-4 py-2 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 transition-colors duration-200" do %> <button id="showDuplicateModal" type="button" class="w-full sm:w-auto inline-flex items-center justify-center px-4 py-2 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 transition-colors duration-200">
<i data-lucide="copy" class="w-4 h-4 mr-2"></i> <i data-lucide="copy" class="w-4 h-4 mr-2"></i>
Dupliquer Dupliquer
<% end %> </button>
<!-- Duplication Modal -->
<div id="duplicateModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<!-- Background overlay -->
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<!-- Modal container -->
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
<i data-lucide="copy" class="h-6 w-6 text-blue-600"></i>
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">
Dupliquer l'événement
</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">
Choisissez les options de duplication pour "<%= @event.name %>".
</p>
<div class="mt-4">
<div class="flex items-center">
<input id="cloneTicketTypes" type="checkbox" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" checked>
<label for="cloneTicketTypes" class="ml-2 block text-sm text-gray-900">
Dupliquer également les types de billets (<%= @event.ticket_types.count %> type(s))
</label>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button id="confirmDuplicate" type="button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
Dupliquer
</button>
<button id="cancelDuplicate" type="button" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
Annuler
</button>
</div>
</div>
</div>
</div>
<% if @event.draft? %> <% if @event.draft? %>
<% if @event.ticket_types.blank? %> <% if @event.ticket_types.blank? %>

View File

@@ -56,7 +56,7 @@ class Promoter::EventsControllerTest < ActionDispatch::IntegrationTest
# Duplicate the event # Duplicate the event
assert_difference('Event.count', 1) do assert_difference('Event.count', 1) do
post duplicate_promoter_event_path(@event) post duplicate_promoter_event_path(@event), params: { clone_ticket_types: "true" }
end end
# Check that the new event was created # Check that the new event was created
@@ -76,5 +76,53 @@ class Promoter::EventsControllerTest < ActionDispatch::IntegrationTest
assert_equal "VIP Ticket", new_event.ticket_types.find_by(name: "VIP Ticket").name assert_equal "VIP Ticket", new_event.ticket_types.find_by(name: "VIP Ticket").name
end end
test "should duplicate an event without ticket types" do
sign_in @promoter
# Create ticket types for the event
ticket_type1 = TicketType.create!(
name: "Standard Ticket",
description: "A standard ticket for the event with all the basic access",
price_cents: 2000,
quantity: 100,
sale_start_at: 1.day.ago,
sale_end_at: @event.start_time - 1.hour,
event: @event
)
ticket_type2 = TicketType.create!(
name: "VIP Ticket",
description: "A VIP ticket for the event with special access",
price_cents: 5000,
quantity: 50,
sale_start_at: 1.day.ago,
sale_end_at: @event.start_time - 1.hour,
event: @event
)
# Verify that ticket types were created successfully
assert ticket_type1.valid?
assert ticket_type2.valid?
# Duplicate the event without ticket types
assert_difference('Event.count', 1) do
post duplicate_promoter_event_path(@event), params: { clone_ticket_types: "false" }
end
# Check that the new event was created
assert_redirected_to edit_promoter_event_path(Event.last)
assert_equal "Événement dupliqué avec succès! Vous pouvez maintenant modifier les détails de l'événement copié.", flash[:notice]
# Check that the new event has the correct attributes
new_event = Event.last
assert_equal "Copie de #{@event.name}", new_event.name
assert_equal "draft", new_event.state
assert_equal @event.venue_name, new_event.venue_name
assert_equal @event.venue_address, new_event.venue_address
# Check that ticket types were NOT duplicated
assert_equal 0, new_event.ticket_types.count
end
# Add tests for new, create, etc. as needed # Add tests for new, create, etc. as needed
end end

View File

@@ -295,4 +295,26 @@ class EventTest < ActiveSupport::TestCase
assert_equal "Standard", duplicated_event.ticket_types.find_by(name: "Standard").name assert_equal "Standard", duplicated_event.ticket_types.find_by(name: "Standard").name
assert_equal "VIP", duplicated_event.ticket_types.find_by(name: "VIP").name assert_equal "VIP", duplicated_event.ticket_types.find_by(name: "VIP").name
end end
test "should duplicate event without ticket types" do
user = User.create!(email: "test@example.com", password: "password123", password_confirmation: "password123")
event = Event.create!(name: "Original Event", slug: "original", description: "A description that is sufficiently long", venue_name: "v", venue_address: "a", user: user, latitude: 48.0, longitude: 2.0, start_time: 1.week.from_now, state: :published)
# Create ticket types
ticket_type1 = TicketType.create!(name: "Standard", description: "A standard ticket for the event", price_cents: 2000, quantity: 100, sale_start_at: 1.day.ago, sale_end_at: event.start_time - 1.hour, event: event)
ticket_type2 = TicketType.create!(name: "VIP", description: "A VIP ticket for the event", price_cents: 5000, quantity: 50, sale_start_at: 1.day.ago, sale_end_at: event.start_time - 1.hour, event: event)
# Duplicate the event without ticket types
duplicated_event = event.duplicate(clone_ticket_types: false)
# Check that duplication was successful
assert_not_nil duplicated_event
assert_equal "Copie de #{event.name}", duplicated_event.name
assert_equal "draft", duplicated_event.state
assert_equal event.venue_name, duplicated_event.venue_name
assert_equal event.venue_address, duplicated_event.venue_address
# Check that ticket types were NOT duplicated
assert_equal 0, duplicated_event.ticket_types.count
end
end end