{% extends 'vitrine/base.html.twig' %}
{% import 'vitrine/macros/svg_illustration_macros.twig' as svg %}
{% block title "Carte cadeau" %}
{% block description %}{% trans %}Offrez un moment de détente{% endtrans %}{% endblock %}
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />
{{ encore_entry_link_tags('tunnel') }}
{% endblock %}
{% block javascripts %}
{{ parent() }}
{{ encore_entry_script_tags('tunnel') }}
<script src="https://unpkg.com/swiper/swiper-bundle.min.js"></script>
<script type="text/javascript" src="https://api.payplug.com/js/1/form.latest.js"></script>
<script src="{{ asset('js/form/postage-price.js') }}"></script>
<script src="{{ asset('js/form/global.js') }}"></script>
<script async defer src="https://api.mapbox.com/mapbox-assembly/v1.5.1/assembly.js"></script>
<script id="search-js" defer="" src="https://api.mapbox.com/search-js/v1.0.0-beta.21/web.js"></script>
{% if app.environment == "prod" and facebook_pixel_id %}
<!-- Facebook Pixel Code -->
<script data-cookieconsent="marketing">
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '{{ facebook_pixel_id }}');
fbq('track', 'PageView');
// Événement ViewContent - Landing page carte cadeau
fbq('track', 'ViewContent', {
content_type: 'product',
content_name: 'Carte Cadeau Taaka',
content_category: 'gift_card',
content_ids: ['gift_card'],
currency: 'EUR'
});
// Fonction de tracking pour les étapes du tunnel
window.trackFacebookEvent = function(eventName, params) {
if (typeof fbq !== 'undefined') {
fbq('track', eventName, params);
}
};
</script>
<noscript>
<img height="1" width="1" style="display:none"
src="https://www.facebook.com/tr?id={{ facebook_pixel_id }}&ev=PageView&noscript=1"/>
</noscript>
<!-- End Facebook Pixel Code -->
{% endif %}
<script>
const ACCESS_TOKEN = '{{ mapbox_access_token }}';
const COUNTRIES = '{{ mapbox_countries }}'.split(',');
const script = document.getElementById('search-js');
script.onload = () => {
// Function to close autofill popups when necessary
const closeMapboxPopups = () => {
const popups = document.querySelectorAll('.mapboxgl-popup');
popups.forEach(popup => popup.remove());
};
// Helper function to wrap an input field with Mapbox autofill
const setupAutofillForInput = (inputId) => {
const inputElement = document.getElementById(inputId);
if (!inputElement) return;
// Store the original input's attributes and parent
const originalParent = inputElement.parentNode;
const style = window.getComputedStyle(inputElement);
const width = style.width;
// Create a container with the original styles to avoid layout shifts
const container = document.createElement('div');
container.style.width = width;
container.style.position = 'relative';
container.style.display = 'inline-block';
// Create Mapbox autofill element
const autofill = document.createElement('mapbox-address-autofill');
autofill.setAttribute('access-token', ACCESS_TOKEN);
// Set options as a JSON string attribute
autofill.setAttribute('options', JSON.stringify({
country: COUNTRIES,
language: 'fr'
}));
// Replace input with the autofill + input
originalParent.replaceChild(container, inputElement);
container.appendChild(autofill);
autofill.appendChild(inputElement);
// Add event listeners to handle popups
inputElement.addEventListener('blur', () => {
setTimeout(closeMapboxPopups, 200);
});
};
// Setup autofill for specific fields - contact address
setupAutofillForInput('carte_cadeau_contact_adresse');
// Setup autofill for delivery address if it exists
setupAutofillForInput('carte_cadeau_deliveryAddress_adresse');
// Initialize postage price display for physical card checkbox
document.addEventListener('DOMContentLoaded', function() {
// Only initialize if the physical card price display element exists
if (document.querySelector('.is-physical-card')) {
// Update price display on page load
updatePhysicalCardPriceDisplay('.is-physical-card');
}
});
};
</script>
{% include 'form/components/js-translations.html.twig' %}
{% endblock %}
{% block main %}
<div id="background-override">
<video data-step="start" class="background-video" src="https://i.gyazo.com/d9d0bd5636691cfdf72d4db0a4132c09.mp4" muted playsinline autoplay loop></video>
<div data-step="second-step" class="tunnel-hidden background-image swiper-full">
<div class="swiper swiper-bath">
<div class="swiper-wrapper">
<div class="swiper-slide">
<img src="https://i.gyazo.com/b7d32e07063d6a51fc24bfe3b70da6ca.jpg" alt="Baignoire 1">
</div>
<div class="swiper-slide">
<img src="https://i.gyazo.com/b7d32e07063d6a51fc24bfe3b70da6ca.jpg" alt="Baignoire 1">
</div>
<div class="swiper-slide">
<img src="https://i.gyazo.com/b7d32e07063d6a51fc24bfe3b70da6ca.jpg" alt="Baignoire 3">
</div>
</div>
<div class="swiper-pagination"></div>
</div>
</div>
<div data-step="fourth-step" class="tunnel-hidden background-image swiper-full">
<div class="swiper swiper-extra">
<div class="swiper-wrapper">
<div class="swiper-slide">
<img src="https://i.gyazo.com/84c7b90fc6ff6c31478eae19f38bd03d.jpg" alt="Extra 1">
</div>
<div class="swiper-slide">
<img src="https://i.gyazo.com/84c7b90fc6ff6c31478eae19f38bd03d.jpg" alt="Extra 2">
</div>
<div class="swiper-slide">
<img src="https://i.gyazo.com/84c7b90fc6ff6c31478eae19f38bd03d.jpg" alt="Extra 3">
</div>
</div>
<div class="swiper-pagination"></div>
</div>
</div>
</div>
{% form_theme form 'form/theme_tunnel.html.twig' %}
{{ form_start(form, { 'attr': { 'class': 'to-submit w-full' }}) }}
<div class="not-animate-childs pt-32 justify-items-center">
<div id="progress-bar" class="progress-bar-container tunnel-hidden">
<p class="progress-bar-steps">
{% trans %}Etape{% endtrans %} <span id="step-count">1</span> {% trans %}sur 6{% endtrans %}
</p>
<div class="progress-bar">
<div class="progress-bar-fill"></div>
</div>
</div>
<section id="start" class="tunnel-hidden content-column-center-48 tunnel-step">
<h2 class="title text-center mt-24 text-4xl">
{% trans %}Offrez un moment de détente à vos proches{% endtrans %}
</h2>
<div class="tunnel-footer m-auto">
{% include "tunnel/components/primary-button.html.twig" with {'action': 'nextStep', 'label': 'Commencer' } %}
</div>
</section>
<section id="first-step" class="tunnel-hidden tunnel-step">
<div class="content-column-center-48">
<div class="content-column-8-center">
<h2 class="title text-center">
{% trans %}Pour combien de personnes ?{% endtrans %}
</h2>
<p class="text-opacity-70 text-center">
{% trans %}Les heureux élus profiteront de leur espace en toute intimité, quel que soit leur nombre{% endtrans %}
</p>
</div>
<div>
{{ form_widget(form.nbParticipants) }}
</div>
<div class="tunnel-footer">
<div class="infoContainer w-full text-center" style="display: none">
<p class="stepInfo">
</p>
</div>
{% include "tunnel/components/primary-button.html.twig" with {'action': 'nextStep', 'label': 'Suivant' } %}
{% include "tunnel/components/secondary-button.html.twig" with {'action': 'goToPrivatisation', 'label': 'Privatiser' } %}
</div>
</div>
</section>
<section id="second-step" class="tunnel-hidden tunnel-step">
<div class="swiper-button-prev-bath swiper-button-prev"></div>
<div class="swiper-button-next-bath swiper-button-next"></div>
<div class="content-column-center-48 ">
<div class="content-column-8-center">
<h2 class="title text-center">
{% trans %}Combien de baignoires voulez-vous inclure ?{% endtrans %}
</h2>
<p class="text-opacity-70 text-center">
{% trans %}Les heureux élus profiteront de leur espace en toute intimité, quel que soit leur nombre.{% endtrans %}
</p>
</div>
{# <div class="swiper mySwiper">#}
{# <div class="swiper-wrapper">#}
{# <div class="swiper-slide"></div>#}
{# <div class="swiper-slide"></div>#}
{# <div class="swiper-slide"></div>#}
{# </div>#}
{# <div class="swiper-pagination"></div>#}
{# </div>#}
<div class="max-w-400">
{{ form_widget(form.nbBath) }}
</div>
<div class="tunnel-footer">
<div class="totalContainer w-full flex-between" style="display: none">
<p class="small-text-opacity-70">
{% trans %}Total pour{% endtrans %} <span class="totalPerson">1</span> {% trans %}personne(s){% endtrans %}
</p>
<p>
<b class="totalPrice"></b>
</p>
</div>
{% include "tunnel/components/primary-button.html.twig" with {'action': 'nextStep', 'label': 'Suivant' } %}
{% include "tunnel/components/secondary-button.html.twig" with {'action': 'previousStep', 'label': 'Précédent' } %}
</div>
</div>
</section>
<section id="third-step" class="tunnel-hidden tunnel-step">
<div class="content-column-center-48 ">
<h2 class="title text-center">
{% trans %}Choisissez une offre{% endtrans %}
</h2>
<div class="w-full">
{{ form_widget(form.offer) }}
</div>
<div class="tunnel-footer">
<div class="totalContainer w-full flex-between" style="display: none">
<p class="small-text-opacity-70">
{% trans %}Total pour{% endtrans %} <span class="totalPerson">1</span> {% trans %}personne(s){% endtrans %}
</p>
<p>
<b class="totalPrice"></b>
</p>
</div>
{% include "tunnel/components/primary-button.html.twig" with {'action': 'nextStep', 'label': 'Suivant' } %}
{% include "tunnel/components/secondary-button.html.twig" with {'action': 'previousStep', 'label': 'Précédent' } %}
</div>
</div>
</section>
<section id="fourth-step" class="tunnel-hidden tunnel-step">
<div class="swiper-button-prev-extra swiper-button-prev"></div>
<div class="swiper-button-next-extra swiper-button-next"></div>
<div class="content-column-center-48 ">
<div class="content-column-8-center">
<h2 class="title text-center">
{% trans %}Un petit plaisir à ajouter ?{% endtrans %}
</h2>
</div>
<div class="extra-container max-w-400">
{{ form_row(form.extraReservations) }}
</div>
<div class="tunnel-footer">
<div class="totalContainer w-full flex-between" style="display: none">
<p class="small-text-opacity-70">
{% trans %}Total pour{% endtrans %} <span class="totalPerson">1</span> {% trans %}personne(s){% endtrans %}
</p>
<p>
<b class="totalPrice"></b>
</p>
</div>
{% include "tunnel/components/primary-button.html.twig" with {'action': 'nextStep', 'label': 'Suivant' } %}
{% include "tunnel/components/secondary-button.html.twig" with {'action': 'previousStep', 'label': 'Précédent' } %}
</div>
</div>
</section>
<section id="fifth-step" class="tunnel-hidden tunnel-step">
<div class="content-column-center-48 ">
<div class="content-column-8-center">
<h2 class="title text-center">
{% trans %}Personnalisez votre carte cadeau{% endtrans %}
</h2>
</div>
{# <div class="swiper mySwiper">#}
{# <div class="swiper-wrapper">#}
{# <div class="swiper-slide"></div>#}
{# <div class="swiper-slide"></div>#}
{# <div class="swiper-slide"></div>#}
{# </div>#}
{# <div class="swiper-pagination"></div>#}
{# </div>#}
<div class="max-w-400 content-column-16">
<p class="text-opacity-70 text-center">
{% trans %}Choisissez votre modèle{% endtrans %}
</p>
{{ form_widget(form.templateCarteCadeau) }}
<p class="text-opacity-70 text-center" style="margin-top: 32px;">
{% trans %}Ajoutez un message{% endtrans %}
</p>
<div class="relative bg-white h-max">
{{ form_widget(form.attention) }}
<span id="max-length-text">
{% trans %}Caractères restants{% endtrans %} : <span id="textarea-remaining-characters">196</span>/196
</span>
</div>
</div>
<div class="tunnel-footer">
<div class="totalContainer w-full flex-between" style="display: none">
<p class="small-text-opacity-70">
{% trans %}Total pour{% endtrans %} <span class="totalPerson">1</span> {% trans %}personne(s){% endtrans %}
</p>
<p>
<b class="totalPrice"></b>
</p>
</div>
{% include "tunnel/components/primary-button.html.twig" with {'action': 'nextStep', 'label': 'Suivant' } %}
{% include "tunnel/components/secondary-button.html.twig" with {'action': 'previousStep', 'label': 'Précédent' } %}
</div>
</div>
</section>
<section id="sixth-step" class="tunnel-hidden tunnel-step">
<div class="content-column-center-48">
<div class="content-column-8 ">
<h2 class="title text-center">
{% trans %}Récapitulons{% endtrans %}
</h2>
</div>
<div class="content-row-48-center max-w-1200">
<div class="gsap-sticky">
{% include "tunnel/components/recapitulatif.html.twig" %}
</div>
<div class="contact-container wrapper-form-right flex-1">
<div class="content-column-16">
<h2 class="p-16-0">
{% trans %}Votre carte cadeau{% endtrans %}
</h2>
<div class="content-row-16 is-physical-card">
<div class="input-wrapper">
<label class="content-row-8">
{{ form_widget(form.physicalCard) }}
<div class="checkbox-label">
<p>{% trans %}Impression + envoi postal{% endtrans %}</p>
<p class="small-text-opacity-70 sublabel">{% trans %}Frais de livraison{% endtrans %}</p>
</div>
</label>
</div>
<div id="postagePriceResponse" data-price="0"></div>
</div>
<p class="green-light-text">{{ 'email.carte_cadeau.physical_delivery_france'|trans }}</p>
<p class="green-light-text">{{ 'email.carte_cadeau.physical_delivery_text'|trans }}</p>
<div>
<h3 class="p-16-0">
{% trans %}Offert par{% endtrans %}
</h3>
<p class="green-light-text">{% trans %}La carte cadeau sera envoyée sur votre email{% endtrans %}</p>
</div>
{{ form_row(form.contact) }}
<div class="use-facturation-checkbox hidden">
<div class="input-wrapper">
<label class="content-row-8">
{{ form_widget(form.deliveryAddress.useAdresseFacturation) }}
<p>{% trans %}Utiliser cette adresse pour la livraison{% endtrans %}</p>
</label>
</div>
</div>
<div class="adresse-livraison container-contact-physical-card hidden">
<h3 class="p-16-0">
{% trans %}Adresse de livraison{% endtrans %}
</h3>
<div class="content-column-16 form-contact-physical-card">
<div class="input-wrapper">
<label class="content-column-8">
{% trans %}Prénom{% endtrans %}
{{ form_widget(form.deliveryAddress.prenom) }}
</label>
<hr >
<label class="content-column-8">
{% trans %}Nom{% endtrans %}
{{ form_widget(form.deliveryAddress.nom) }}
</label>
</div>
<div class="input-wrapper">
<label class="content-column-8">
{% trans %}Rue{% endtrans %}
{{ form_widget(form.deliveryAddress.adresse) }}
</label>
<hr >
<label class="content-column-8">
{% trans %}Ville{% endtrans %}
{{ form_widget(form.deliveryAddress.ville) }}
</label>
</div>
<div class="input-wrapper">
<label class="content-column-8">
{% trans %}Code postal{% endtrans %}
{{ form_widget(form.deliveryAddress.codePostal) }}
</label>
<hr >
<label class="content-column-8">
{% trans %}Pays{% endtrans %}
{{ form_widget(form.deliveryAddress.pays) }}
</label>
</div>
</div>
</div>
{{ form_row(form.cgv) }}
<p style="font-size: 14px;">
(*) {% trans %}Champs obligatoires{% endtrans %}
</p>
</div>
</div>
</div>
<div class="content-column-8 max-w-400">
<div class="infoContainer w-full text-center my-2">
<p class="stepInfo">
</p>
</div>
{% include 'form/components/rgpd.html.twig' with {'sujet': 'achat'} %}
{% include "tunnel/components/primary-button.html.twig" with {'action': 'createPayment', 'label': 'payment.pay'|trans } %}
{% include "tunnel/components/secondary-button.html.twig" with {'action': 'goToFirstStep', 'label': 'Modifier' } %}
</div>
</div>
</section>
</div>
{{ form_end(form) }}
<script src="{{ asset('js/tunnel.js') }}"></script>
<script src="{{ asset('js/tunnel/swiper.js') }}"></script>
<script src="{{ asset('js/textarea_length.js') }}"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Fix the selector to find the actual physicalCard checkbox
const physicalCardCheckbox = document.querySelector('input[name$="[physicalCard]"]');
const deliveryAddressSections = document.querySelectorAll('.adresse-livraison');
const useAdresseFacturationContainer = document.querySelector('.use-facturation-checkbox');
const useAdresseFacturationCheckbox = document.querySelector('.use-facturation-checkbox input');
const contactCountrySelect = document.querySelector('select[name$="[contact][pays]"]');
// Get all delivery address form fields
const deliveryAddressFields = document.querySelectorAll(
'.adresse-livraison input[required], .adresse-livraison select[required]'
);
// Function to toggle required attribute on delivery address fields
function toggleDeliveryFieldsRequired(required) {
deliveryAddressFields.forEach(field => {
if (required) {
field.setAttribute('required', 'required');
} else {
field.removeAttribute('required');
}
});
}
// Check if contact country is France
function checkContactCountry() {
if (contactCountrySelect && useAdresseFacturationCheckbox) {
const isContactInFrance = contactCountrySelect.value === 'FR';
if (!isContactInFrance) {
// Disable checkbox and add warning
useAdresseFacturationCheckbox.disabled = true;
useAdresseFacturationCheckbox.checked = false;
// Add warning if not already present
let warningMsg = useAdresseFacturationContainer.querySelector('.country-warning');
if (!warningMsg) {
warningMsg = document.createElement('p');
warningMsg.className = 'country-warning small-text text-error';
warningMsg.textContent = "{% trans %}L'adresse de contact doit être en France pour utiliser la même adresse{% endtrans %}";
useAdresseFacturationContainer.appendChild(warningMsg);
}
// Show delivery address if physical card is checked
if (physicalCardCheckbox && physicalCardCheckbox.checked) {
deliveryAddressSections.forEach(section => {
section.classList.remove('hidden');
});
toggleDeliveryFieldsRequired(true);
}
} else {
// Enable checkbox and remove warning
useAdresseFacturationCheckbox.disabled = false;
const warningMsg = useAdresseFacturationContainer.querySelector('.country-warning');
if (warningMsg) {
warningMsg.remove();
}
// Update delivery address visibility based on useAdresseFacturation state
updateDeliveryAddressVisibility();
}
}
}
// Function to update delivery address visibility based on both checkboxes
function updateDeliveryAddressVisibility() {
if (!physicalCardCheckbox) return;
const physicalCardChecked = physicalCardCheckbox.checked;
const useFacturationChecked = useAdresseFacturationCheckbox && useAdresseFacturationCheckbox.checked && !useAdresseFacturationCheckbox.disabled;
// Show/hide use facturation checkbox based on physical card
if (useAdresseFacturationContainer) {
useAdresseFacturationContainer.classList.toggle('hidden', !physicalCardChecked);
}
// Show/hide delivery address and set required fields
deliveryAddressSections.forEach(section => {
if (physicalCardChecked && !useFacturationChecked) {
section.classList.remove('hidden');
toggleDeliveryFieldsRequired(true);
} else {
section.classList.add('hidden');
toggleDeliveryFieldsRequired(false);
}
});
}
// Initialize form state
if (physicalCardCheckbox) {
// Set initial state - by default everything should be hidden and not required
toggleDeliveryFieldsRequired(false);
updateDeliveryAddressVisibility();
// Add event listener for physical card checkbox
physicalCardCheckbox.addEventListener('change', function() {
updateDeliveryAddressVisibility();
});
}
// Check country on load and when country changes
if (contactCountrySelect) {
checkContactCountry();
contactCountrySelect.addEventListener('change', checkContactCountry);
}
// Handle useAdresseFacturation checkbox if it exists
if (useAdresseFacturationCheckbox) {
useAdresseFacturationCheckbox.addEventListener('change', function() {
updateDeliveryAddressVisibility();
});
}
});
let submitButton = document.querySelector(".submit-btn");
window.addEventListener('message', function (event) {
// Check if the message is coming from the PayPlug origin
if (event.origin === "https://secure.payplug.com") {
// Check if the message data indicates the lightbox was closed
if (event.data === "closePayPlugFrame") {
// Re-enable the submit button
submitButton.removeAttribute("disabled");
submitButton.querySelector(".spinner").style.display = "none";
}
}
});
function createPayment() {
let form = document.querySelector("form.to-submit");
let formExtras = new FormData(form);
let formData = {};
submitButton.removeAttribute("disabled");
displayStepInfo(submitButton, "");
for (let entry of formExtras.entries()) {
formData[entry[0]] = entry[1];
}
// get the template select option value
formData['template'] = document.querySelector('input[name="carte_cadeau[templateCarteCadeau]"]:checked').value;
// Check for delivery address confirmation if physical card is selected
if (!confirmDeliveryAddress()) {
// User cancelled, reset button state and don't proceed with payment
submitButton.disabled = false;
submitButton.removeAttribute('disabled');
// Remove loading states
submitButton.classList.remove('loading', 'disabled', 'processing');
// Reset any inline styles
submitButton.style.pointerEvents = '';
submitButton.style.opacity = '';
// Hide spinner if it exists
const spinner = submitButton.querySelector('.spinner');
if (spinner) {
spinner.style.display = 'none';
}
// Clear any info messages
displayStepInfo(submitButton, "");
return;
}
// Facebook Pixel - InitiateCheckout avec le montant
// Server-side event is also sent via Meta Conversions API
if (typeof window.trackFacebookEvent !== 'undefined' && typeof synthesisRefreshPrice === 'function') {
const totalPrice = synthesisRefreshPrice();
window.trackFacebookEvent('InitiateCheckout', {
content_type: 'product',
content_name: 'Carte Cadeau Taaka',
content_category: 'gift_card',
content_ids: ['gift_card'],
num_items: 1,
currency: 'EUR',
value: totalPrice
});
}
// Custom validation - check only visible required fields
const visibleRequiredFields = form.querySelectorAll('input[required]:not([style*="display: none"]), select[required]:not([style*="display: none"])');
let isValid = true;
let firstInvalidField = null;
visibleRequiredFields.forEach(field => {
// Skip validation for fields that are in hidden containers
const isFieldHidden = field.closest('.hidden') !== null;
if (!isFieldHidden && !field.value.trim()) {
isValid = false;
if (!firstInvalidField) {
firstInvalidField = field;
}
}
});
if (!isValid) {
displayStepInfo(submitButton, "Veuillez remplir tous les champs obligatoires");
if (firstInvalidField) {
firstInvalidField.focus();
}
return;
}
submitButton.disabled = true;
submitButton.querySelector(".spinner").style.display = "block";
$.ajax({
url: "{{ path('carte_cadeau_paiement') }}",
dataType: "json",
type: "Post",
async: true,
data: formData,
success: function (data) {
Payplug.showPayment(data);
},
error: function (data) {
alert('{% trans %}Nous sommes désolés, une erreur est survenue, nous avons besoin que vous recommenciez. Aucun paiement n\'a été émis.{% endtrans %}');
submitButton.removeAttribute("disabled");
submitButton.querySelector(".spinner").style.display = "none";
// window.location.reload();
}
});
}
</script>
{% endblock %}