Skip to content
Snippets Groups Projects
SignUp.vue 19.3 KiB
Newer Older
<template>
  <div>
    <div class="signup-container">
      <div class="app-header">
Florent Lavelle's avatar
Florent Lavelle committed
        <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">
Florent Lavelle's avatar
Florent Lavelle committed
          {{ $t('signup.title') }}
          <span class="sub-title">{{ $t('signup.subtitle') }}</span>
        </h4>
        <hr class="divider">
Florent Lavelle's avatar
Florent Lavelle committed
        <h5>{{ $t('signup.secondTitle') }}</h5>
Florent Lavelle's avatar
Florent Lavelle committed
        <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"
                      type="text"
                      class="form-control"
                      :placeholder="$t('words.firstname')"
                    <span class="form-errors">{{ errors[0] }}</span>
            <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">
                      v-model="form.last_name"
                      type="text"
                      class="form-control"
                       :placeholder="$t('words.lastname')"
                    <span class="form-errors">{{ errors[0] }}</span>

            <!-- 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"
                      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>
                </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 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>

            <!-- 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 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>
Florent Lavelle's avatar
Florent Lavelle committed
        {{ $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';

Florent Lavelle's avatar
Florent Lavelle committed
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,
Florent Lavelle's avatar
Florent Lavelle committed
  message: () => i18n.t('errors.required'),
});

extend('email', {
  ...email,
Florent Lavelle's avatar
Florent Lavelle committed
  message: () => i18n.t('errors.email'),
});

extend('confirmed', {
  ...confirmed,
Florent Lavelle's avatar
Florent Lavelle committed
  message: () => i18n.t('errors.confirmPassword'),
});

extend('regex', {
  ...regex,
Florent Lavelle's avatar
Florent Lavelle committed
  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'
Florent Lavelle's avatar
Florent Lavelle committed
    ]),

    formConfig() {
      return this.$config.forms.signup;
    },

Florent Lavelle's avatar
Florent Lavelle committed
    logoPath() {
      return require(process.env.VUE_APP_LOGO);
    }
Florent Lavelle's avatar
Florent Lavelle committed
    '$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())
Florent's avatar
Florent committed
            ).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())
Florent's avatar
Florent committed
          ).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 {
      }

      .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>