Login Panel

preview (image)
  • template
<div class="login-panel fixed inset-0 z-[80]"
x-data="initLoginPanel()"
@keydown.escape.window="close()"
:class="{
'block' : modalOpen,
'hidden' : !modalOpen
}"
x-cloak
>
<div class="login-panel__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="login-panel__content absolute z-10 right-0 top-0 bottom-0 flex flex-col transition duration-[350ms] w-full lg:max-w-[390px]"
:class="{
'translate-x-0' : isOpen,
'translate-x-full' : !isOpen
}"
>
<div class="h-screen bg-white ml-auto w-full flex flex-col">
<div class="bg-primary text-white relative">
<img <?= renderImageUrl('background-login.png', 'backgrounds') ?> class="absolute top-0 left-1/2 -translate-x-1/2 h-full w-full object-cover opacity-20" alt="">
<div class="relative">
<div>
<button class="link flex items-center gap-3 text-smaller font-semibold pt-6.25 pb-8 px-7.5"><?= icon('chevron-left', 'w-2'); ?> Revenir</button>
</div>
<hr class="border-b border-ui border-t-0 opacity-30">
<div class="px-7.5 pt-5.5 pb-7 text-center">
<img <?= renderImageUrl('logo-white.svg', 'logo') ?> alt="" class="mx-auto">
<p class="pt-2.5">
Merci de vous identifier ou de créer votre compte pour sauvegarder
<span x-show="typeConnection === 'favorite'">vos favoris</span>
<span x-show="typeConnection === 'save-search'">votre recherche</span>
</p>
</div>
</div>
</div>

<div class="text-center text-text-light p-7.5 pt-14 lg:pt-7.5 overflow-y-auto" x-data="initLoginPanelForm()">
<div class="text-2xl font-bold mb-7">
<span x-show="step !== 6">Connexion/inscription</span>
<span x-show="step === 6" x-cloak>Créer mon compte</span>
</div>
<div class="tracking-tightened text-smaller font-medium">
<span x-show="step === 1">Saisissez votre adresse e-mail ou votre numéro de téléphone mobile, nous nous connaissons peut-être déjà 👋</span>
<span x-cloak x-show="step === 2 || step === 3 ">Vous avez un compte, saisissez votre mot de passe 🔒</span>
<span x-cloak x-show="step === 4 || step === 5">Nous nous connaissons déjà 😊 
Renseignez quelques informations complémentaires pour récupérer vos informations et documents</span>
<span x-cloak x-show="step === 6">Vous n’avez pas encore de compte, créez le votre gratuitement 👇 </span>
<span x-cloak x-show="step === 7 && modeRecognized === 'mail'">Merci, nous vous avons envoyé un e-mail pour valider votre adresse e-mail, merci de saisir le code reçu ci-dessous</span>
<span x-cloak x-show="step === 7 && modeRecognized === 'tel'">Merci, nous vous avons envoyé un SMS pour valider votre numéro de téléphone, merci de saisir le code reçu ci-dessous</span>
</div>
<div class="mt-7 lg:mt-10 mb-9">

<div>

<div x-show="step === 1" class="form-element text w-full">
<input name="name" title="Adresse e-mail ou numéro de tél." class="form-input w-full"
id="login_panel_email_tel"
type="text" placeholder=" " pattern="\S+" required
@keydown.enter="nextStep()"
>
<label class="label" for="login_panel_email_tel">
<span>Adresse e-mail ou numéro de tél.</span>
</label>
</div>

<div x-show="step === 6" class="form-element form-box-radio-slider form-box-radio-slider--full-double"
x-data="boxCheckboxSlider()"
x-cloak>
<div x-ref="controler" class="controler"></div>
<div class="label-wrapper" x-ref="wrapper">
<label for="login_panel_particulier">
<input @click="changeInput()" type="radio" value="particulier" name="login_panel_type" id="login_panel_particulier" checked>
<span>Particulier</span>
</label>
<label for="login_panel_pro">
<input @click="changeInput()" type="radio" value="professionnel" name="login_panel_type" id="login_panel_pro">
<span>Professionnel</span>
</label>
</div>
</div>
<div x-show="step === 6" class="flex flex-col md:flex-row gap-2.5 mt-4">
<div class="form-element text md:w-1/2" :class="hasError.firstname_create && 'error'">
<input name="firstname" title="Prénom" class="form-input w-full"
id="login_panel_firstname" type="text"
placeholder=" " pattern="\S+" required
@keydown.enter="$dispatch('booking-form-next-step', { main: 1, subStep: 5 })"
>
<label class="label" for="login_panel_firstname">
<span>Prénom</span>
</label>
<div class="error-message" :class="hasError.login_panel_firstname_required && 'flex'"><?= icon('label-error-colored'); ?> Ce champs est requis.</div>
</div>
<div class="form-element text md:w-1/2" :class="hasError.lastname_create && 'error'">
<input name="lastname" title="Nom" class="form-input w-full"
id="login_panel_lastname" type="text"
placeholder=" " pattern="\S+" required
@focus="delete hasError.lastname_create"
@keydown.enter="$dispatch('booking-form-next-step', { main: 1, subStep: 5 })"
>
<label class="label" for="login_panel_lastname">
<span>Nom</span>
</label>
<div class="error-message" :class="hasError.login_panel_lastname_required && 'flex'"><?= icon('label-error-colored'); ?>Ce champs est requis.</div>
</div>
</div>

<div x-show="step === 2 || step === 4 || step === 5 || step === 6" class="form-element text w-full mt-4" :class="hasError.login_panel_email_required && 'error'">
<input name="name" class="form-input w-full" title="Votre e-mail"
id="login_panel_email" type="text" x-model="datas.email"
placeholder=" " pattern="\S+" required
>
<label class="label" for="login_panel_email">
<span>Votre e-mail</span>
</label>
<div class="error-message" :class="hasError.login_panel_email_required && 'flex'"><?= icon('label-error-colored'); ?>Ce champs est requis.</div>
</div>

<div x-show="step === 3 || step === 4 || step === 5 || step === 6" class="form-element text w-full mt-4">
<input name="phone" class="form-input w-full" title="Téléphone"
id="login_panel_phone" type="text" x-model="datas.phone"
placeholder=" " pattern="\S+" required
x-data x-init="new Cleave('#login_panel_phone', { phone: true, phoneRegionCode: 'FR' });">
<label class="label" for="login_panel_phone">
<span>Votre téléphone</span>
</label>
<div class="error-message" :class="hasError.login_panel_phone_required && 'flex'"><?= icon('label-error-colored'); ?>Ce champs est requis.</div>
</div>

<div x-show="step === 2 || step === 3" class="form-element password w-full mt-4" x-data="{ show: false }">
<input name="password" class="form-input w-full" title="Mot de passe "
id="login_panel_password"
type="password" :type="show ? 'text' : 'password'"
placeholder=" " pattern="\S+"
>
<label class="label" for="login_panel_password">
<span>Mot de passe</span>
</label>
<span @click="show = !show">
<span :class="{'hidden' : show}" ><?= icon('password-see', 'text-icon'); ?></span>
<span :class="{'hidden' : !show}" class="hidden"><?= icon('password-unsee', 'text-icon'); ?></span>
</span>
</div>
<div class="error-message" :class="hasError.login_panel_password_required && 'flex'"><?= icon('label-error-colored'); ?>Ce champs est requis.</div>


<div x-show="step === 4 || step === 5" class="form-element text w-full mt-4">
<input name="postal_code" class="form-input w-full" id="login_panel_postal_code" type="text" title="Code postal" placeholder=" " pattern="\S+">
<label class="label" for="login_panel_postal_code">
<span>Code postal</span>
</label>
</div>

<div x-show="step === 6" x-cloak class="mt-4" x-data="initCheckPasswordCreateLoginPanel()">
<div class="form-element password w-full" x-data="{ show: false }"
:class="!isValid && 'error'">
<input class="form-input w-full"
id="login_panel_create_password" type="password" :type="show ? 'text' : 'password'"
name="password" title="Mot de passe" placeholder=" " pattern="\S+"
minlength="8" required
x-on:input="$parent.checkPasswordStrength(); $parent.checkPasswordsMatch()"
>
<label class="label" for="login_panel_create_password">Votre mot de passe</label>
<span @click="show = !show">
<span :class="{'hidden' : show}" ><?= icon('password-see', 'text-icon'); ?></span>
<span :class="{'hidden' : !show}" class="hidden"><?= icon('password-unsee', 'text-icon'); ?></span>
</span>
</div>
<p class="text-error text-sm invisible" :class="{'invisible' : !error}" x-text="error"></p>
<div class="form-element password w-full mt-4" x-data="{ show: false }">
<input class="form-input w-full"
id="login_panel_create_password_confirm"
type="password" :type="show ? 'text' : 'password'"
name="password_confirm" title="Mot de passe" placeholder=" " pattern="\S+" required
x-on:change="$parent.checkPasswordsMatch()"
x-on:keyup="$parent.checkPasswordsMatch()"
>
<label class="label" for="login_panel_create_password_confirm">Confirmer votre mot de passe</label>
<span @click="show = !show">
<span x-show="!show"><?= icon('password-see', 'text-icon'); ?></span>
<span x-show="show" x-cloak><?= icon('password-unsee', 'text-icon'); ?></span>
</span>
</div>
<span class="block mt-1.5 text-tiny leading-4 text-red-lighter" x-text="errorMessages.invalid"></span>
</div>

<div x-show="step === 7" class="form-element text w-full">
<input name="code" class="form-input w-full" id="login_panel_code" type="text" title="Code" placeholder=" " pattern="\S+">
<label class="label" for="login_panel_code">
<span>Saisissez votre code reçu par e-mail</span>
</label>
</div>
</div>
<button @click="nextStep()" class="btn btn-primary btn-full-width mt-4">
<span x-show="step === 1">Valider</span>
<span x-cloak x-show="step === 2 || step === 3">M’identifier</span>
<span x-cloak x-show="step >= 4">Créer mon compte</span>
</button>

<div x-show="step === 6">
<div class="form-element form-checkbox pt-5 pb-7.5">
<label for="login_panel_cgu">
<input class="checkbox"
type="checkbox"
id="login_panel_cgu"
>
<span class="controler"></span>
<span class="text-sm">J’accepte les CGU de Tressol Chabrier</span>
</label>
</div>
</div>
</div>
<div class="text-smaller tracking-tightened font-medium">
<p x-show="step === 1">ℹ️ Si vous êtes déjà client des concessions Tressol Chabrier, nous pourrons rapatrier vos informations, documents dans votre garage en ligne</p>
<p x-cloak x-show="step === 7 && modeRecognized === 'mail'">La validation de votre adresse e-mail permettra de vous idenfier pour associer toutes vos informations sur votre compte Garage en ligne</p>
<p x-cloak x-show="step === 7 && modeRecognized === 'tel'">La validation de votre numéro de téléphone permettra de vous idenfier pour associer toutes vos informations sur votre compte Garage en ligne</p>
</div>
</div>
</div>
</div>
</div>

<script>
function initLoginPanel() {
return {
modalOpen: false,
isOpen: false,
typeConnection: null,
init() {
const current = this;
document.addEventListener('openLoginPanel', function (e) {
current.typeConnection = e.detail;
current.open();
});
document.addEventListener('closeLoginPanel', function (e) {
current.close();
});
},
open() {
document.body.classList.add('overflow-hidden');
this.modalOpen = true;
this.$nextTick(() => {
this.isOpen = true;
})
},
close() {
console.log('close')
const current = this;
document.body.classList.remove('overflow-hidden');
this.isOpen = false;
setTimeout(function () {
current.modalOpen = false;
}, 350)
}
}
}

/*
Step 1 : Entrer email/tel
Step 2 : Login email
Step 3 : Login tel
Step 4 : Email reconnu (pas de compte)
Step 5 : Tel reconnu (pas de compte)
Step 6 : Email/tel pas reconnu (création de compte)
Step 7 : Vérification code
*/

function initLoginPanelForm() {
return {
step: 1,
hasError: [],
datas: [],
modeRecognized: false,
nextStep() {
switch (this.step) {
case 1:
this.checkEmailTel()
break;
case 2 :
case 3 :
this.connectionAccount()
break;
case 4:
case 5:
this.completeAccount()
break;
case 6 :
this.createAccount();
break;
default:
console.log('now logged')
}
},
checkEmailTel() {
this.hasError = [];
this.checkError('login_panel_email_tel', 'required');
if(Object.keys(this.hasError).length > 0) return false; /* Retourne false s'il y a des erreurs dans le formulaire sinon on continue */

let inputValue = document.getElementById('login_panel_email_tel').value;

/* AJAX RECUP INFORMATIONS */

if(this.checkEmailFormat(inputValue)) {
this.checkIfEmailExist();
this.modeRecognized = 'mail';
return false;
}

if(this.checkTelFormat(inputValue)) {
this.checkIfTelExist();
this.modeRecognized = 'tel';
return false;
}
},
checkIfEmailExist() {
this.setData('email', 'login_panel_email_tel')

/* Ajax de test de l'adresse email ici qui remplacera le switch */
let scenario = 1; /* 1 : compte existant, 2 : email reconnu, pas de compte, 3 : mail non reconnu */

switch (scenario) {
case 1:
this.step = 2;
break;
case 2:
this.step = 4;
break;
default:
this.step = 6;
}
},
checkIfTelExist() {
this.setData('phone', 'login_panel_email_tel')

/* Ajax de test de l'adresse email ici qui remplacera le switch */
let scenario = 2; /* 1 : compte existant, 2 : tel reconnu, pas de compte, 3 : tel non reconnu */

switch (scenario) {
case 1:
this.step = 3;
break;
case 2:
this.step = 5;
break;
default:
this.step = 6;
}
},
completeAccount() {
this.hasError = [];
this.checkError('login_panel_email', 'required');
this.checkError('login_panel_phone', 'required');
this.checkError('login_panel_postal_code', 'required');
if(Object.keys(this.hasError).length > 0) return false; /* Retourne false s'il y a des erreurs dans le formulaire sinon on continue */

this.step = 7;
},
createAccount() {
this.hasError = [];

this.checkError('login_panel_email', 'required');
this.checkError('login_panel_email', 'checkEmailFormat');
this.checkError('login_panel_firstname', 'required');
this.checkError('login_panel_lastname', 'required');
this.checkError('login_panel_phone', 'required');
this.checkError('login_panel_cgu', 'requiredCheckbox');
console.log(this.hasError)
if(Object.keys(this.hasError).length > 0) return false; /* Retourne false s'il y a des erreurs dans le formulaire sinon on continue */

/* Set datas */
this.setData('type', 'login_panel_type', 'radio');
this.setData('email', 'login_panel_email');
this.setData('firstname', 'login_panel_firstname');
this.setData('lastname', 'login_panel_lastname');
this.setData('phone', 'login_panel_phone');

this.step = 7;
},
connectionAccount() {
this.hasError = [];

if(this.modeRecognized === 'mail') {
this.checkError('login_panel_email', 'required');
}
if(this.modeRecognized === 'tel') {
this.checkError('login_panel_phone', 'required');
}
this.checkError('login_panel_password', 'required');

/* CONDITION A CHANGER AVEC VOTRE VERIF AJAX */
if(document.getElementById('login_panel_password').value !== "" && document.getElementById('login_panel_password').value !== "azerty") {
this.hasError.password_login_wrong = true;
}
if(Object.keys(this.hasError).length > 0) return false; /* Retourne false s'il y a des erreurs dans le formulaire sinon on continue */


/* AJAX LOGIN ICI */

/* ferme la modal après la connexion */
this.$dispatch('closeLoginPanel');
},
checkCode() {
this.hasError = [];

this.checkError('code_confirm', 'required');
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 DU CODE */
this.hasError.wrongCode = true;
}
if(Object.keys(this.hasError).length > 0) return false; /* Retourne false s'il y a des erreurs dans le formulaire sinon on continue */

/* AJAX LOGIN ICI */

/* ferme la modal après la connexion */
this.$dispatch('closeLoginPanel');
},
checkError(id, error) {
if(error === 'required') {
if(document.getElementById(id).validity.valueMissing) {
this.hasError[id + '_required'] = true;
return false;
}
}
if(error === "checkEmailFormat") {
if(document.getElementById(id).value !== "" && !this.checkEmailFormat(document.getElementById(id).value)) {
this.hasError[id + '_wrong_format'] = true;
return false;
}
}
if(error === 'requiredCheckbox') {
if(!document.getElementById(id).checked) {
this.hasError[id] = true;
return false
}
}
return true;
},
checkEmailFormat(value) {
return !!value.match(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g);
},
checkTelFormat(value) {
return !!value.match(/^(?:(?:\+|00)33|0)\s*[1-9](?:[\s.-]*\d{2}){4}$/gmi);
},
setData(data, id, type = 'input') {
if(type === 'select') {
this.datas[data] = document.getElementById(id).nextElementSibling.innerText;
} else if(type === 'radio') {
this.datas[data] = document.querySelector('input[name="' + id + '"]').value;
} else if(type === 'phone') {
this.datas['phone'] = this.phoneInput.getRawValue();
}
else {
this.datas[data] = document.getElementById(id).value;
}
}
}
}

function initCheckPasswordCreateLoginPanel() {
return {
passwordInput: null,
confirmInput: null,
passwordsMatching: true,
passwordStrength: 0,
passwordStrengthText: 'Faible',
isValid: true,
error: null,
errorMessages: {
invalid: 'Ce champ doit contenir au minium 8 caractères dont 1 majuscule, 1 minuscule et 1 chiffre.',
notMatch: 'Les deux mots de passe ne correspondent pas.'
},
init() {
this.passwordInput = this.$root.querySelector('#login_panel_create_password');
this.confirmInput = this.$root.querySelector('#login_panel_create_password_confirm');
const current = this;
document.addEventListener('check-password-validity', function () {
current.checkPasswordValidity();
});
},
checkPasswordsMatch() {
if (this.passwordInput.value ===
this.confirmInput.value || this.confirmInput.value === '') {
this.passwordsMatching = true;
this.error = null
return true;
}
this.passwordsMatching = false;
this.error = this.errorMessages.notMatch;
return false;
},
checkPasswordStrength() {
this.isValid = true;
let value = this.passwordInput.value;
let secureWeight = 0;
if(value.length >= 8) secureWeight = secureWeight + 1;
if(value.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/)) secureWeight = secureWeight + 1;
if(value.match(/[!@#$%^&*(),.?:{}|<>=+~_§\-]/)) secureWeight = secureWeight + 1;

this.passwordStrength = secureWeight;

switch (secureWeight) {
case 1:
this.passwordStrengthText = "Moyen";
break;
case 2:
this.passwordStrengthText = "Élevé";
break;
case 3:
this.passwordStrengthText = "Très élevé";
break;
default:
this.passwordStrengthText = "Faible";
}
},
checkPasswordValidity() {
this.isValid = true;
const value = this.passwordInput.value;

if(value === '' || value.length < 8 || !value.match(/(?=.*[a-z])/) || !value.match(/(?=.*[A-Z])/) || !value.match(/(?=.*\d)/))this.isValid = false;

if(!this.isValid) {
this.passwordInput.setCustomValidity(this.errorMessages.invalid)
this.error = this.errorMessages.invalid;
} else {
if(!this.checkPasswordsMatch()) {
this.passwordInput.setCustomValidity(this.errorMessages.notMatch)
this.error = this.errorMessages.notMatch;
} else {
this.passwordInput.setCustomValidity('');
this.error = null;
}
}

this.$dispatch('password-validity', this.isValid);

return this.isValid;
}
}
}
</script>