<template> <div> <div class="signup-container"> <div class="app-header"> <img alt="logo" :src="logoPath"/> </div> <b-overlay id="overlay-background" :show="loading" :variant="'white'" :opacity="0.7" :blur="'2px'" rounded="sm" no-wrap > </b-overlay> <div class="signup-form"> <h4 class="title"> {{ $t('signup.title') }} <span class="sub-title">{{ $t('signup.subtitle') }}</span> </h4> <hr class="divider"> <h5>{{ $t('signup.secondTitle') }}</h5> <ValidationObserver ref="form" v-slot="{ handleSubmit }"> <form> <h5>{{ $t('signup.form.personalDetails.title') }}</h5> <hr class="divider"> <!-- FIRST NAME --> <div v-if="isFieldVisible('first_name', formConfig.hiddenFields)" class="row g-2 align-items-center" > <div class="col-3"> <label :class="isFieldRequired('first_name', formConfig.requiredFields)" class="col-form-label" > {{ $t('words.firstname') }} </label> </div> <div class="col"> <ValidationProvider ref="first_name" :rules="isFieldRequired('first_name', formConfig.requiredFields)" v-slot="{ classes, errors }" > <div class="control" :class="classes"> <input data-test="signUp-firstname" v-model="form.first_name" type="text" class="form-control" :placeholder="$t('words.firstname')" > <span class="form-errors">{{ errors[0] }}</span> </div> </ValidationProvider> </div> </div> <!-- LAST NAME --> <div data-test="signUp-lastname" v-if="isFieldVisible('last_name', formConfig.hiddenFields)" class="row g-2 align-items-center" > <div class="col-3"> <label :class="isFieldRequired('last_name', formConfig.requiredFields)" class="col-form-label" > {{ $t('words.lastname') }} </label> </div> <div class="col"> <ValidationProvider ref="last_name" :rules="isFieldRequired('last_name', formConfig.requiredFields)" v-slot="{ classes, errors }" > <div class="control" :class="classes"> <input v-model="form.last_name" type="text" class="form-control" :placeholder="$t('words.lastname')" > <span class="form-errors">{{ errors[0] }}</span> </div> </ValidationProvider> </div> </div> <!-- EMAIL --> <div v-if="isFieldVisible('email', formConfig.hiddenFields)" class="row g-2 align-items-center" > <div class="col-3"> <label :class="isFieldRequired('email', formConfig.requiredFields)" class="col-form-label" > {{ $t('words.email') }} </label> </div> <div class="col"> <ValidationProvider ref="email" :rules="`${isFieldRequired('email', formConfig.requiredFields)}|email`" v-slot="{ classes, errors }" > <div class="control" :class="classes"> <input data-test="signUp-email" v-model="form.email" type="mail" class="form-control" :placeholder="$t('words.email')" > <span class="form-errors">{{ errors[0] }}</span> </div> </ValidationProvider> </div> </div> <!-- PHONE NUMBER --> <div v-if="isFieldVisible('phone_number', formConfig.hiddenFields)" class="row g-2 align-items-center" > <div class="col-3"> <label :class="isFieldRequired('phone_number', formConfig.requiredFields)" class="col-form-label" > {{ $t('words.phone') }} </label> </div> <div class="col"> <ValidationProvider ref="phone_number" :rules="isFieldRequired('phone_number', formConfig.requiredFields)" v-slot="{ classes, errors }" > <div class="control" :class="classes"> <input data-test="signUp-phone" v-model="form.phone_number" type="text" class="form-control" placeholder="" > <span class="form-errors">{{ errors[0] }}</span> </div> </ValidationProvider> </div> </div> <!-- COMMENTS --> <div v-if="isFieldVisible('comments', formConfig.hiddenFields)" class="row g-2 align-items-center" > <div class="col-3"> <label class="col-form-label"> {{ $t('signup.form.personalDetails.reason') }} </label> </div> <div class="col"> <textarea data-test="signUp-comments" v-model="form.comments" class="form-control" /> </div> </div> <h5>{{ $t('signup.form.loginDetails.title') }}</h5> <hr class="divider"> <!-- USERNAME --> <div class="row g-2 align-items-center"> <div class="col-3"> <label class="col-form-label">{{ $t('words.username') }}</label> </div> <div class="col"> <ValidationProvider ref="username" rules="required" v-slot="{ classes, errors }"> <div class="control" :class="classes"> <div class="input-group flex-nowrap"> <input data-test="signUp-username" v-model="form.username" type="text" class="form-control" disabled > <span class="input-group-text"> <b-icon icon="person-fill" /> </span> </div> <span class="form-errors">{{ errors[0] }}</span> </div> </ValidationProvider> </div> </div> <div class="row g-2 align-items-center" style="margin-bottom"> <div class="col-3"> </div> <div class="col"> <p class="infos"> {{ $t('signup.form.loginDetails.usernameHelp') }} </p> </div> </div> <!-- PASSWORD --> <div class="row g-2 align-items-center"> <div class="col-3"> <label class="col-form-label required" style="padding: 0;" >{{ $t('words.password') }} </label> </div> <div class="col"> <ValidationProvider ref="password" :rules="{ required: true, regex: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d$&+,:;=?@#|'<>.^*()%!-]{8,}$/ }" v-slot="{ classes, errors }" vid="confirmation" > <div class="control" :class="classes"> <div class="input-group flex-nowrap"> <input data-test="signUp-password1" v-model="form.password1" class="form-control" :type="showPassword ? 'text' : 'password'" :placeholder="$t('words.password')" > <span class="input-group-text"> <b-icon data-test="signUp-toggleShowPassword" :icon="showPassword?'eye-slash-fill':'eye-fill'" @click="showPassword = !showPassword" /> </span> </div> <span class="form-errors">{{ errors[0] }}</span> </div> </ValidationProvider> </div> </div> <div class="row g-2 align-items-center"> <div class="col-3"></div> <div class="col"> <ValidationProvider ref="password" rules="required|confirmed:confirmation" v-slot="{ classes, errors }" > <div class="control" :class="classes"> <div class="input-group flex-nowrap"> <input data-test="signUp-password2" v-model="form.password2" class="form-control" :type="showPassword ? 'text' : 'password'" :placeholder="$t('words.password')" > <span class="input-group-text"> <b-icon data-test="signUp-toggleShowPassword2" :icon="showPassword?'eye-slash-fill':'eye-fill'" @click="showPassword = !showPassword" /> </span> </div> <span class="form-errors">{{ errors[0] }}</span> </div> </ValidationProvider> </div> </div> <div class="row g-2 align-items-center"> <div class="col-3"> </div> <div class="col"> <div class="infos"> <ul> <li>{{ $t('signup.form.loginDetails.passwordHelp')[0] }}</li> <li>{{ $t('signup.form.loginDetails.passwordHelp')[1] }}</li> <li>{{ $t('signup.form.loginDetails.passwordHelp')[2] }}</li> <li>{{ $t('signup.form.loginDetails.passwordHelp')[3] }}</li> </ul> </div> </div> </div> <OrganisationSelector @select="handleOrganisationSelection"/> <b-button :disabled="(!form.username || !form.password1 || !form.password2 || !form.first_name || !form.last_name || !form.email)" :pressed="btnPressed" data-test="signUp-submit" @click.prevent="handleSubmit(submit)" variant="primary" > {{ $t('words.validate') }} </b-button> <b-button type="button" variant="outline-primary" data-test="signUp-signIn" @click.prevent="$router.push({ name: 'SignIn' })"> {{ $t('words.cancel') }} </b-button> </form> </ValidationObserver> </div> </div> <small class="footer"> <p> {{ $t('footer') }} <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a> </p> </small> </div> </template> <script> import { mapState, mapActions, mapMutations, } from 'vuex'; const signUpActions = [ 'POST_SIGNUP', ]; const signOutActions = [ 'GET_SIGNOUT', ]; import OrganisationSelector from '@/components/OrganisationSelector'; // import usersAPI from '@/api/usersAPI.js'; // import Swal from "sweetalert2"; // import "sweetalert2/dist/sweetalert2.min.css"; import { deburr } from 'lodash'; import i18n from '@/i18n'; import { ValidationObserver, ValidationProvider, extend, configure, } from 'vee-validate'; import { required, email, confirmed, regex } from 'vee-validate/dist/rules'; extend('required', { ...required, message: () => i18n.t('errors.required'), }); extend('email', { ...email, message: () => i18n.t('errors.email'), }); extend('confirmed', { ...confirmed, message: () => i18n.t('errors.confirmPassword'), }); extend('regex', { ...regex, message: () => i18n.t('errors.regexPassword'), }); configure({ classes: { valid: 'is-valid', invalid: 'is-invalid', }, }); export default { name: 'SignUp', components: { ValidationObserver, ValidationProvider, OrganisationSelector, }, data() { return { loading: false, form: { first_name: null, last_name: null, email: null, phone_number: null, comments: null, username: null, password1: null, password2: null }, isOrganisationSelected: false, organisation: null, organisationThumbnail: null, organisationSpheres: [], btnPressed: false, showPassword: false, }; }, computed: { ...mapState('sign-up', [ 'error', 'signed' ]), ...mapState('sign-out', [ 'logged' ]), formConfig() { return this.$config.forms.signup; }, logoPath() { return require(process.env.VUE_APP_LOGO); } }, watch: { '$i18n.locale': function(newValue, oldValue) { if (newValue !== oldValue) { this.$refs.form.validate(); } }, 'form.first_name': { deep: true, handler(newValue) { if (newValue !== null && this.form.last_name !== null) { this.form.username = deburr( newValue .replace(/\s/g, '') .charAt(0) .toLowerCase() .concat('', this.form.last_name.replace(/\s/g, '').toLowerCase()) ).normalize('NFD').replace(/[\u0300-\u036f]|[^A-Z0-9]/ig, ''); } } }, 'form.last_name': { deep: true, handler(newValue) { if (this.form.first_name !== null && newValue !== null) { this.form.username = deburr( this.form.first_name .replace(/\s/g, '') .charAt(0) .toLowerCase() .concat('', newValue.replace(/\s/g, '').toLowerCase()) ).normalize('NFD').replace(/[\u0300-\u036f]|[^A-Z0-9]/ig, ''); } } }, signed() { this.$router.push({ name: 'SignUpSuccess' }); }, error(newValue) { if (newValue) { for (const [key, value] of Object.entries(newValue)) { if (this.$refs[key]) { this.$refs[key].applyResult({ errors: value, valid: false, failedRules: {}, }); } } } }, }, created() { if (this.logged) { this.GET_SIGNOUT(); } if (this.signed) { this.SET_SIGNED(true); } }, methods: { ...mapActions('sign-out', signOutActions), ...mapActions('sign-up', signUpActions), ...mapMutations('sign-up', [ 'SET_FORM', 'SET_SIGNED' ]), handleOrganisationSelection(e) { this.isOrganisationSelected = e.selected; this.organisation = e.orga; this.organisationThumbnail = e.thumbnail; this.organisationSpheres = e.spheres; }, submit() { this.loading = true; this.btnPressed = true; // const isUsernameAvailable = await usersAPI.findUsername(this.form.username); // if (isUsernameAvailable.results.length > 0) { // const currentUsername = this.form.username; // let newUsername; // let suffix = '1'; // // Check if username last character is a number already // // and apply suffix accordingly // if (!isNaN(parseInt(currentUsername.slice(-1)))) { // suffix = (parseInt(currentUsername.slice(-1)) + 1).toString(); // newUsername = currentUsername.replace(/.$/, suffix); // } else { // newUsername = currentUsername.concat(suffix); // } // Swal.fire({ // position: 'center', // heightAuto: false, // icon: 'warning', // html: `Le nom d'utilisateur <b>${currentUsername}</b> est déjà pris. // Le compte sera créé avec le nom d'utilisateur suivant: <b>${newUsername}</b>`, // showCancelButton: true, // cancelButtonText: 'Annuler', // showConfirmButton: true, // confirmButtonText: 'Confirmer', // confirmButtonColor: '#187CC6' // }).then((result) => { // if (result.isConfirmed) { // this.form.username = newUsername; // this.submit(); // } // }); // } else { this.signUp(); // } // this.loading = false; }, async signUp() { // this.loading = true; this.SET_FORM({ form: { first_name: this.form.first_name, last_name: this.form.last_name, email: this.form.email, username: this.form.username, password: this.form.password1, phone_number: this.form.phone_number ? this.form.phone_number : '', comments: this.form.comments ? this.form.comments : '', ...this.isOrganisationSelected && { usergroup_roles: [ { organisation: this.organisation, }, ], } }, thumbnail: this.organisationThumbnail, spheres: this.organisationSpheres }); await this.POST_SIGNUP(); this.loading = false; } } }; </script> <style lang="less" scoped> .signup-container { margin: 1rem auto auto; width: 800px; height: fit-content; .signup-form { margin: 5rem 1rem; h4.title { color: #373b3d; .sub-title { font-size: 75%; color: #6b7479; } } hr.solid { border-top: 2px solid #373b3d; } h5 { color: #6b7479; } form { margin-top: 32px; h5 { margin-bottom: 20px; margin-top: 40px; color: #373b3d; } .row { margin-bottom: 1.75rem; } .input-group { span { cursor: pointer; border-bottom-left-radius: 0; border-top-left-radius: 0; border-left: none } } button { float: right; position: relative; margin-left: 7px; margin-top: 30px; } .infos { font-size: 0.8em; font-style: italic; margin-right: 1em; ul { padding-left: 1rem; } } } } } .form-errors { color: #EB0600 !important; margin-right: 2em; line-height: 1; } .form-success { color: #30C963 !important; } .footer { position: relative; bottom: 0; font-size: small; margin-top: 2rem; } .footer a { text-decoration: none; } </style>