Réserver : Formulaire

preview (image)
  • template
<div class="booking-modal fixed inset-0 z-[100]"
x-data="initBookingFormModal()"
@keydown.escape.window="close()"
:class="{
'block' : modalOpen,
'hidden' : !modalOpen
}"
x-cloak
>
<div class="booking-modal__overlay absolute inset-0 bg-primary opacity-40 cursor-pointer transition duration-300 translate-x-full"
:class="{
'translate-x-0' : isOpen,
'translate-x-full' : !isOpen
}"
@click="close()"
></div>
<div class="booking-modal__content absolute z-10 right-0 top-0 bottom-0 flex flex-col transition duration-[350ms] lg:w-[920px]"
:class="{
'translate-x-0' : isOpen,
'translate-x-full' : !isOpen
}"
>
<div class="flex flex-col bg-ui-light h-full" x-data="initBookingForm()">
<div class="booking-modal__header bg-white border-b border-ui flex justify-between items-center pl-7 md:pl-7.5 relative">
<div class="text-base md:text-smaller font-semibold">Réserver mon véhicule</div>
<div class="numbered-steps flex items-center mx-auto sm:w-fit absolute bottom-[-15px] left-0 right-0 px-7.5 md:px-0 md:static">
<div class="numbered-steps__step"
:class="{
'numbered-steps__step--fill': mainStep > 1
}"
>
<span>1</span>
<?= icon('check', 'w-3.5'); ?>
</div>
<div class="numbered-steps__separator"></div>
<div class="numbered-steps__step numbered-steps__step--inactive"
:class="{
'numbered-steps__step--fill': mainStep > 2,
'numbered-steps__step--inactive': mainStep < 2
}"
>
<span>2</span>
<?= icon('check', 'w-3.5'); ?>
</div>
<div class="numbered-steps__separator numbered-steps__separator--inactive" :class="{'numbered-steps__separator--inactive': mainStep < 2}"></div>
<div class="numbered-steps__step numbered-steps__step--inactive"
:class="{
'numbered-steps__step--fill': mainStep > 3,
'numbered-steps__step--inactive': mainStep < 3
}"
>
<span>3</span>
<?= icon('check', 'w-3.5'); ?>
</div>
</div>
<div>
<button @click="$dispatch('closeBookingModal')" class="link text-primary-accent hover:text-secondary px-7.5 pt-8.75 pb-9.5"><?= icon('close', 'w-3.25'); ?></button>
</div>
</div>

<div class="flex-1 overflow-y-auto flex flex-col">
<div class="flex-1 py-12 md:py-8.75 px-7 md:px-7.5 flex-1 flex flex-col md:flex-row gap-7.5"
:class="{
'bg-bookings-success-step': mainStep === 3,
'md:flex-row': mainStep !== 3
}"
>
<div class="md:w-1/2"
:class="{
'md:w-1/2': mainStep !== 3,
'md:max-w-[559px] mx-auto': mainStep === 3
}">
<div class="text-lg md:text-xl font-semibold tracking-tightened mb-5 md:min-h-[60px]" x-show="mainStep !== 3">
Récapitulatif
</div>
<div class="sm-mobile:text-lg md:text-xl tracking-tightened font-semibold text-white md:text-center leading-[1.2] mb-5 md:mb-7.5 md:flex md:flex-col md:items-center"
x-show="mainStep === 3" x-cloak>
<div class="float-left md:float-none mr-2.5 md:mr-0 md:mt-3.25 md:mb-5 md:order-1"><?= icon('label-check-success-colored', 'w-13'); ?></div>
<div>Merci</div>
<div class="font-medium leading-[1] md:order-2">Votre réservation a bien été prise en compte</div>
</div>
<?php include renderTemplate('side-panels/booking/booking-summary', 'step'); ?>
</div>
<div class="md:w-1/2"
:class="{
'md:w-1/2': mainStep !== 3,
'md:max-w-[559px] mx-auto': mainStep === 3
}">
<div x-ref="steps">
<?php /* Step : création/login */ ?>
<div x-show="mainStep === 1 && subStep === 1" x-transition.opacity>
<div class="text-lg md:text-xl font-semibold tracking-tightened mb-5 md:min-h-[60px]">Connexion / Inscription</div>
<?php include renderTemplate('side-panels/booking/booking-steps', 'login-create'); ?>
</div>

<?php /* Step : création */ ?>
<div x-show="mainStep === 1 && subStep === 2" x-cloak x-transition.opacity>
<div class="text-lg md:text-xl font-semibold tracking-tightened mb-5 md:min-h-[60px]">
<div>Créez votre compte</div>
<div class="text-sm md:text-smaller text-text-light">Déjà un compte ? <button class="link text-primary-accent hover:text-secondary" @click="$dispatch('booking-form-next-step', { main: 1, subStep: 3, skipCheck: true })">Identifiez-vous</button></div>
</div>
<?php include renderTemplate('side-panels/booking/booking-steps', 'create-account'); ?>
</div>

<?php /* Step : login */ ?>
<div x-show="mainStep === 1 && subStep === 3" x-cloak x-transition.opacity>
<div class="text-lg md:text-xl font-semibold tracking-tightened mb-5 md:min-h-[60px]">Identifiez-vous</div>
<?php include renderTemplate('side-panels/booking/booking-steps', 'login'); ?>
</div>

<?php /* Step : create with address know */ ?>
<div x-show="mainStep === 1 && subStep === 4" x-cloak x-transition.opacity>
<div class="text-lg md:text-xl font-semibold tracking-tightened mb-5 md:min-h-[60px]">Connexion / Inscription</div>
<?php include renderTemplate('side-panels/booking/booking-steps', 'create-email-know'); ?>
</div>

<?php /* Step : code confirmation */ ?>
<div x-show="mainStep === 1 && subStep === 5" x-cloak x-transition.opacity>
<div class="text-lg md:text-xl font-semibold tracking-tightened mb-5 md:min-h-[60px]">Saisissez le code reçu</div>
<?php include renderTemplate('side-panels/booking/booking-steps', 'code-confirm'); ?>
</div>

<?php /* Step : création mdp */ ?>
<div x-show="mainStep === 1 && subStep === 6" x-cloak x-transition.opacity>
<div class="text-lg md:text-xl font-semibold tracking-tightened mb-5 md:min-h-[60px]">Créez votre mot de passe</div>
<?php include renderTemplate('side-panels/booking/booking-steps', 'password-create'); ?>
</div>

<?php /* Step : information */ ?>
<div x-show="mainStep === 1 && subStep === 7" x-cloak x-transition.opacity>
<div class="text-lg md:text-xl font-semibold tracking-tightened mb-5 md:min-h-[60px]">
<div>Complétez vos informations</div>
<div class="text-sm md:text-smaller font-medium text-text-light">Important pour mieux vous servir</div>
</div>
<?php include renderTemplate('side-panels/booking/booking-steps', 'postal-address'); ?>
</div>

<?php /* Step : CB */ ?>
<div x-show="mainStep === 2 && subStep === 1" x-cloak x-transition.opacity>
<div class="text-lg md:text-xl font-semibold tracking-tightened mb-5 md:min-h-[60px]">
<div>Régler votre acompte de 500€</div>
<div class="text-sm md:text-smaller font-medium text-text-light">15 jours pour changer d’avis</div>
</div>
<?php include renderTemplate('side-panels/booking/booking-steps', 'payment-card'); ?>
</div>

<?php /* Step : Validation */ ?>
<div x-show="mainStep === 3 && subStep === 1" x-cloak x-transition.opacity>
<div class="text-xl text-white font-medium tracking-tightened mb-5 text-center">Et maintenant ?</div>
<?php include renderTemplate('side-panels/booking/booking-steps', 'success'); ?>
<div class="text-xl text-white font-medium tracking-tightened mb-5 text-center mt-7 md:mt-9.5 md:mb-13">
Toute notre équipe vous remercie pour votre confiance et se tient à votre disposition
</div>
</div>
</div>
</div>
</div>
<div class="booking-modal__footer">
<?php include renderTemplate('side-panels/booking/booking-footer'); ?>
</div>
</div>
</div>
</div>
</div>

<script>
function initBookingFormModal() {
return {
modalOpen: false,
isOpen: false,
init() {
const current = this;
document.addEventListener('openBookingModal', function (e) {
current.open();
});
document.addEventListener('closeBookingModal', function (e) {
current.close();
});
},
open() {
document.body.classList.add('overflow-hidden');
this.modalOpen = true;
this.$nextTick(() => {
this.isOpen = true;
})
},
close() {
const current = this;
document.body.classList.remove('overflow-hidden');
this.isOpen = false;
setTimeout(function () {
current.modalOpen = false;
}, 350)
}
}
}

/*
====== Mapping des étapes

Main 1 : Connexion / Création de compte
Sub 1 : Création/Login (détection adresse mail)
Sub 2 : Création (compte inexistant et adresse non reconnue)
Sub 3 : Login (compte existant) (mdp pour test validation : azertY6!)
Sub 4 : Création (adresse reconnue, compte non existant)
Sub 5 : Confirmation code (envoyé par SMS/Email) (nb code test pour validation : 123456)
Sub 6 : Création mot de passe
Sub 7 : Information (adresse postale)

Main 2 : Paiement
Sub 1 : Carte bancaire

Main 3 : Validation
Sub 1 : Validé
*/

function initBookingForm() {
return {
scenario: 1, /* À supprimer lors de l'intégration, sert uniquement pour le styleguide. 1 : mail non reconnu, 2 : email reconnu, pas de compte, 3 : compte existant */
mainStep: 1,
subStep: 1,
isAddressFill: false,
isExistingAccount: false,
hasError: {},
datas: {
civility: [],
concession: [],
status: []
},
messagesError: {
required: "Ce champ est obligatoire.",
validMail: "Merci de remplir un format d'email valide.",
cguChecked: "Veuillez accepter les CGU pour continuer.",
cgvChecked: "Veuillez accepter les CGV pour continuer.",
wrongCode: "Le code est incorrect.",
wrongPassword: "Le mot de passe est incorrect.",
missingExpirationDate: "Merci de saisir la date d’expiration",
missingSecureCode: "Merci de saisir votre code de sécurité"
},
init() {
const current = this;
document.addEventListener('booking-form-next-step', function (e) {
current.nextStep(e);
})
document.addEventListener('password-validity', function () {
current.datas.isPasswordValid = true;
})
},
nextStep(e) {
if(e.detail.skipCheck) {
this.changeStep(e.detail.main, e.detail.subStep)
} else {
let isStepOk = this.checkStepOk(e.detail.main, e.detail.subStep);
}
},
checkStepOk(mainStep, subStep) {
let isValid = true;
this.hasError = {};

/* ======================= Check Main : 1 =========================== */
if(this.mainStep === 1) {

/* === Check Sub : 1 === */
if(this.subStep === 1) {

isValid = this.checkError('email_login_create', 'required');
isValid = this.checkError('email_login_create', 'checkEmailFormat');
if(!isValid) return false;

this.setData('email', 'email_login_create')
console.log(this.datas)

/* condition switch à supprimer à l'inté, mettre votre check ajax ici et renvoyer vers la bonne étape suivante en fonction de votre résultat (voir le commentaire plus haut pour les correspondances d'étapes) */
if(this.scenario === 1) {

}
switch (this.scenario) {
case 2:
this.changeStep(1, 3);
break;
case 3:
this.changeStep(1, 4);
break;
default:
this.changeStep(1, 2);
}
return true;
}

/* === Check Sub : 2 === */
if(this.subStep === 2) {

isValid = this.checkError('email_create', 'required');
isValid = this.checkError('email_create', 'checkEmailFormat');
isValid = this.checkError('firstname_create', 'required');
isValid = this.checkError('lastname_create', 'required');
isValid = this.checkError('phone_create', 'required');
isValid = this.checkError('cgu_create', 'requiredCheckbox');

if(!isValid) return false;

this.setData('type', 'input[name="type_create"]', 'radio')
this.setData('email', 'email_create')
this.setData('firstname', 'firstname_create')
this.setData('lastname', 'lastname_create')
this.setData('phone', 'phone_create')
this.datas.cgu = true;
this.changeStep(1, 5);
return true;
}

/* === Check Sub : 3 === */
if(this.subStep === 3) {
isValid = this.checkError('email_login', 'required');
isValid = this.checkError('email_login', 'checkEmailFormat');
isValid = this.checkError('password_login', 'required');

/* CONDITION A CHANGER AVEC VOTRE VERIF */
if(document.getElementById('password_login').value !== "azertY6!") {
this.hasError.password_login = this.messagesError.wrongPassword;
isValid = false;
}
if(!isValid) return false;

/* === DATAS A INSÉRER AVEC VOTRE RETOUR AJAX */

this.datas.civility = ["mme", "Mme"];
this.datas.firstname = "Jeanne";
this.datas.lastname = "Dupont"
this.datas.phone = "0622004499";
this.datas.street= "25 rue de Aiguebelle";
this.datas.postalCode= "83230";
this.datas.city= "Bormes les mimosas";
this.datas.concession = ["no", "Pas de préférence"];
this.datas.status = ["administrateur-de-compte", "Administrateur de compte"];
this.isAddressFill = true;
/* === fin des fakes datas */
const civilityEvt = new CustomEvent('set-selects-value', {
detail: {
id: 'civility',
value: this.datas.civility[0],
label: this.datas.civility[1]
}
});

this.$nextTick(() => {
document.dispatchEvent(civilityEvt);
})

const concessionEvt = new CustomEvent('set-selects-value', {
detail: {
id: 'concession',
value: this.datas.concession[0],
label: this.datas.concession[1]
}
});

this.$nextTick(() => {
document.dispatchEvent(concessionEvt);
})
const statusEvt = new CustomEvent('set-selects-value', {
detail: {
id: 'status',
value: this.datas.status[0],
label: this.datas.status[1]
}
});

this.$nextTick(() => {
document.dispatchEvent(statusEvt);
})

this.isExistingAccount = true;
this.changeStep(1, 5);
return true;
}

/* === Check Sub : 3 === */
if(this.subStep === 4) {
isValid = this.checkError('email_know', 'required');
isValid = this.checkError('email_know', 'checkEmailFormat');
isValid = this.checkError('phone_email_know', 'required');
isValid = this.checkError('postal_code_email_know', 'required');

console.log(this.hasError)

if(!isValid) return false;

this.datas.email = document.getElementById('email_create').value;
console.log('4')
return true;
}

/* === Check Sub : 5 === */
if(this.subStep === 5) {
isValid = this.checkError('code_confirm', 'required');
isValid = this.checkError('cgu_code', 'requiredCheckbox');
if(document.getElementById('code_confirm').value !== "123456" && !document.getElementById('code_confirm').validity.valueMissing) { /* CONDITION A CHANGER ICI PAR LA VÉRIF */
this.hasError.code_confirm = this.messagesError.wrongCode;
isValid = false;
}

if(!isValid) return false;

this.datas.isCodeConfirm = true;
if(this.isExistingAccount) {
this.changeStep(1, 7);
} else {
this.changeStep(1, 6);
}
return true;
}

/* === Check Sub : 6 === */
if(this.subStep === 6) {
this.changeStep(1, 7);
return this.datas.isPasswordValid;
}

/* === Check Sub : 7 === */
if(this.subStep === 7) {

isValid = this.checkError('firstname', 'required');
isValid = this.checkError('lastname', 'required');
isValid = this.checkError('phone', 'required');
isValid = this.checkError('street', 'required');
isValid = this.checkError('postal_code', 'required');
isValid = this.checkError('city', 'required');
if(!isValid) return false;

this.setData('civility', 'civility', 'select')
this.setData('firstname', 'firstname')
this.setData('lastname', 'lastname')
this.setData('phone', 'phone')
this.setData('street', 'street')
this.setData('postal_code', 'postal_code')
this.setData('city', 'city')

console.log(this.datas)

this.changeStep(2, 1);
return true;
}
}

/* ======================= Check Main : 2 =========================== */

/* === Check Sub : 1 === */
if(this.mainStep === 2) {
if(!document.getElementById('cgv_payment').checked) {
this.hasError.cgv_payment = this.messagesError.cgvChecked;
isValid = false;
}
if(document.getElementById('card_code').validity.valueMissing) {
this.hasError.card_code = this.messagesError.required;
isValid = false;
}
if(document.getElementById('expiration_date').validity.valueMissing) {
this.hasError.expiration_date = this.messagesError.missingExpirationDate;
isValid = false;
}
if(document.getElementById('security_code').validity.valueMissing) {
this.hasError.security_code = this.messagesError.missingSecureCode;
isValid = false;
}

/* Ici l'appel de la vérif de paiement */


if(!isValid) return false;

this.changeStep(3, 1);
return true;
}
return true;
},
checkEmailFormat(value) {
return !!value.match(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g);
},
changeStep(main, sub) {
this.mainStep = main;
this.subStep = sub;
},
checkError(id, error) {
if(error === 'required') {
if(document.getElementById(id).validity.valueMissing) {
this.hasError[id] = this.messagesError.required;
return false;
}
}
if(error === "checkEmailFormat") {
if(!this.checkEmailFormat(document.getElementById(id).value)) {
this.hasError[id] = this.messagesError.validMail;
return false;
}
}
if(error === 'requiredCheckbox') {
if(!document.getElementById(id).checked) {
this.hasError[id] = this.messagesError.cguChecked;
return false
}
}
return true;
},
setData(data, id, type = 'input') {
if(type === 'checkbox') {

} else if(type === 'radio') {
this.datas[data] = document.querySelector(id).value;
} else {
this.datas[data] = document.getElementById(id).value;
}
}
}
}
</script>