Skip to content
Snippets Groups Projects
UserProfile.vue 23.4 KiB
Newer Older
<template>
   <div>
    <b-button
      id="back-button"
      variant="primary"
      data-test="userProfile-goBackToNext"
      @click="goBackToNext"
    >
      <b-icon-arrow-bar-left/>
Florent Lavelle's avatar
Florent Lavelle committed
      {{ $t('words.goBack') }}
    </b-button>
    <div class="signup-container">
      <div class="app-header">
Florent Lavelle's avatar
Florent Lavelle committed
        <img alt="logo" :src="logoPath"/>
      </div>
      <div class="signup-form">
        <h4 class="title">
Florent Lavelle's avatar
Florent Lavelle committed
          {{ $t('profile.title') }}
          <span class="sub-title">{{ $t('profile.subtitle') }}</span>
        </h4>
        <hr class="divider">
Florent Lavelle's avatar
Florent Lavelle committed
        <ValidationObserver ref="form" v-slot="{ handleSubmit }">
          <b-overlay
            :show="loadingUserInformation"
            rounded="lg"
            :style="'padding: 5px;'"
            variant="white"
          >
            <form>
Florent Lavelle's avatar
Florent Lavelle committed
              <h5>{{ $t('profile.form.personalDetails.title') }}</h5>
              <hr class="divider">
              <div class="row g-2 align-items-center">
                <div class="col-3">
Florent Lavelle's avatar
Florent Lavelle committed
                  <label class="col-form-label">{{ $t('words.username') }}</label>
                </div>
                <div class="col">
                  <div class="input-group flex-nowrap">
                    <input data-test="userProfile-username"
                      v-model="formUser.username"
                      type="text"
                      class="form-control"
Florent Lavelle's avatar
Florent Lavelle committed
                      :placeholder="$t('words.username')"
                      disabled
                    >
                  </div>
                </div>
              </div>
              <div class="row g-2 align-items-center">
                <div class="col-3">
Florent Lavelle's avatar
Florent Lavelle committed
                  <label class="col-form-label required">{{ $t('words.firstname') }}</label>
                </div>
                <div class="col">
                  <ValidationProvider ref="first_name" rules="required" v-slot="{ classes, errors }">
                    <div class="control" :class="classes">
                      <input data-test="userProfile-firstname"
                        v-model="formUser.first_name"
                        type="text"
                        class="form-control"
Florent Lavelle's avatar
Florent Lavelle committed
                        :placeholder="$t('words.firstname')"
                      >
                      <span class="form-errors">{{ errors[0] }}</span>
                    </div>
                  </ValidationProvider>
                </div>
              </div>
              <div class="row g-2 align-items-center">
                <div class="col-3">
Florent Lavelle's avatar
Florent Lavelle committed
                  <label class="col-form-label required">{{ $t('words.lastname') }}</label>
                </div>
                <div class="col">
                  <ValidationProvider ref="last_name" rules="required" v-slot="{ classes, errors }">
                    <div class="control" :class="classes">
                      <input data-test="userProfile-lastname"
                        v-model="formUser.last_name"
                        type="text"
                        class="form-control"
Florent Lavelle's avatar
Florent Lavelle committed
                        :placeholder="$t('words.lastname')"
                      >
                      <span class="form-errors">{{ errors[0] }}</span>
                    </div>
                  </ValidationProvider>
                </div>
              </div>
              <div class="row g-2 align-items-center">
                <div class="col-3">
                  <label class="col-form-label">
Florent Lavelle's avatar
Florent Lavelle committed
                    {{ $t('words.email') }}
                  </label>
                </div>
                <div class="col">
                  <ValidationProvider ref="email" rules="required|email" v-slot="{ classes, errors }">
                    <div class="control" :class="classes">
                      <input data-test="userProfile-email"
                        v-model="formUser.email"
                        type="mail"
                        class="form-control"
Florent Lavelle's avatar
Florent Lavelle committed
                        :placeholder="$t('words.email')"
                        disabled
                      >
                      <span class="form-errors">{{ errors[0] }}</span>
                    </div>
                  </ValidationProvider>
                </div>
              </div>
              <div class="row g-2 align-items-center">
                <div class="col-3">
Florent Lavelle's avatar
Florent Lavelle committed
                  <label class="col-form-label">{{ $t('words.phone') }}</label>
                </div>
                <div class="col">
                  <ValidationProvider ref="phone_number" v-slot="{ classes, errors }">
                    <div class="control" :class="classes">
                      <input data-test="userProfile-phone"
                        v-model="formUser.phone_number"
                        type="text"
                        class="form-control"
                        placeholder=""
                      >
                      <span class="form-errors">{{ errors[0] }}</span>
                    </div>
                  </ValidationProvider>
                </div>
              </div>
              <div class="row g-2 align-items-center">
                <div class="col-3">
Florent Lavelle's avatar
Florent Lavelle committed
                  <label class="col-form-label">
                    {{  $t('profile.form.personalDetails.reason') }}
                  </label>
                </div>
                <div class="col">
                  <textarea data-test="userProfile-comments"
                    v-model="formUser.comments"
                    class="form-control"
                  />
                </div>
              </div>
              <div class="row g-2 align-items-center" style="margin-bottom: 0.5em;">
                <div class="col-3">
Florent Lavelle's avatar
Florent Lavelle committed
                  <label
                    class="col-form-label"
                    style="font-size: 0.87em;"
                  >
                    {{ $t('profile.form.personalDetails.organisation.label') }}
                  </label>
                </div>
                <div v-if="userData" class="col">
                  <div v-if="userData.usergroup_roles.length === 0">
Florent Lavelle's avatar
Florent Lavelle committed
                    {{ $t('profile.form.personalDetails.organisation.noOrganisation') }}
                  </div>
                  <b-list-group
                    v-else
                  >
                    <b-list-group-item
                      v-for="usergroup of userData.usergroup_roles"
                      <b>{{ usergroup.usergroup.display_name }}</b>
Florent Lavelle's avatar
Florent Lavelle committed
                      <em> {{ $t('as') }} </em>
                      <b>{{ organisationsRoles.find(el => el.choice === usergroup.role).label }}</b>
                    </b-list-group-item>
                  </b-list-group>
                </div>
              </div>
              <div class="row g-2 align-items-center">
                <div class="col-3">
                </div>
                <div v-if="userData && $config.client.mail" class="col">
                  <div
                    style="margin: 0 0.5em 0.5em 0.1em; font-size: 0.9em; font-style: italic;"
                  >
                    {{ $t('profile.form.personalDetails.organisation.help') }} 
                    <a :href="`mailto:${$config.client.mail}`">
                      {{ $config.client.mail }}
                    </a>.
                  </div>
                </div>
              </div>
            </form>
            <div class="form-footer">
              <b-button
                :disabled="(!formUser.first_name || !formUser.last_name || !formUser.email)"
                data-test="userProfile-submitUserInformations"
                @click.prevent="handleSubmit(submitUserInformations)"
                variant="primary"
              >
Florent Lavelle's avatar
Florent Lavelle committed
                {{ $t('words.validate') }}
              </b-button>
            </div>
          </b-overlay>
        </ValidationObserver>

        <ValidationObserver v-slot="{ handleSubmit }">
          <b-overlay
            :show="loadingUserEmail"
            rounded="lg"
            variant="white"
            :style="'padding: 5px;'"
          >
            <form>
Florent Lavelle's avatar
Florent Lavelle committed
              <h5>{{ $t('profile.form.emailChange.title') }}</h5>
              <hr class="divider">
              <div class="row g-2 align-items-center">
                <div class="col-3" style="padding-right: 0;">
Florent Lavelle's avatar
Florent Lavelle committed
                  <label
                    class="col-form-label required"
                    style="font-size: 0.9em;"
                  >
                    {{ $t('profile.form.emailChange.label') }}
                  </label>
                </div>
                <div class="col">
                 <ValidationProvider ref="email" rules="email" v-slot="{ classes, errors }">
                    <div class="control" :class="classes">
                      <input data-test="userProfile-newEmail"
                        v-model="formEmail.new_email"
                        type="mail"
                        class="form-control"
Florent Lavelle's avatar
Florent Lavelle committed
                        :placeholder="$t('words.email')"
                      >
                      <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
                    style="margin: 0 0.5em 0.5em 0.5em; font-size: 0.8em; font-style: italic;"
                  >
Florent Lavelle's avatar
Florent Lavelle committed
                    {{ $t('profile.form.emailChange.help') }}
                  </div>
                </div>
              </div>
            </form>
            <div class="form-footer">
              <b-button
                :disabled="!formEmail.new_email"
                :pressed="btnPressed"
                data-test="userProfile-submitNewEmail"
                @click.prevent="handleSubmit(submitNewEmail)"
                variant="primary"
              >
Florent Lavelle's avatar
Florent Lavelle committed
                {{ $t('words.validate') }}
              </b-button>
            </div>
          </b-overlay>
        </ValidationObserver>

        <ValidationObserver v-slot="{ handleSubmit }">
          <b-overlay
            :show="loadingUserPassword"
            rounded="lg"
            variant="white"
            :style="'padding: 5px;'"
          >
            <form>
Florent Lavelle's avatar
Florent Lavelle committed
              <h5>{{ $t('profile.form.passwordChange.title') }}</h5>
              <hr class="divider">
              <div class="row g-2 align-items-center">
                <div class="col-3" style="padding-right: 0;">
Florent Lavelle's avatar
Florent Lavelle committed
                  <label class="col-form-label required" style="font-size: 0.9em;">
                    {{ $t('profile.form.passwordChange.oldLabel') }}
                  </label>
                </div>
                <div class="col">
                  <ValidationProvider ref="password" rules="required" v-slot="{ classes, errors }">
                    <div class="control" :class="classes">
                      <div class="input-group flex-nowrap">
                        <input data-test="userProfile-password"
                          v-model="formPassword.password"
                          class="form-control"
                          :type="showPassword ? 'text' : 'password'"
Florent Lavelle's avatar
Florent Lavelle committed
                          :placeholder="$t('profile.form.passwordChange.oldLabel')"
                        <span class="input-group-text">
                          <b-icon data-test="userProfile-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" style="padding-right: 0;">
                  <label
                    class="col-form-label required"
Florent Lavelle's avatar
Florent Lavelle committed
                    style="font-size: 0.9em;">{{ $t('profile.form.passwordChange.newLabel') }}</label>
                </div>
                <div class="col">
                  <ValidationProvider ref="newPassword1" v-slot="{ classes, errors }" vid="confirmation">
                    <div class="control" :class="classes">
                      <div class="input-group flex-nowrap">
                        <input data-test="userProfile-newPassword1"
                          v-model="formPassword.newPassword1"
                          :type="showNewPassword2 ? 'text' : 'password'"
                          class="form-control"
Florent Lavelle's avatar
Florent Lavelle committed
                          :placeholder="$t('profile.form.passwordChange.newPlaceholder')"
                        <span class="input-group-text">
                          <b-icon data-test="userProfile-toggleShowNewPassword"
                            :icon="showNewPassword2?'eye-slash-fill':'eye-fill'"
                            @click="showNewPassword2 = !showNewPassword2"
                          />
                        </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" style="padding-right: 0;">
                  <label
                    class="col-form-label"
                    style="font-size: 0.9em;"></label>
                </div>
                <div class="col">
                  <ValidationProvider ref="newPassword2" rules="confirmed:confirmation" v-slot="{ classes, errors }">
                    <div class="control" :class="classes">
                      <div class="input-group flex-nowrap">
                        <input data-test="userProfile-newPassword2"
                          v-model="formPassword.newPassword2"
                          class="form-control"
                          :type="showNewPassword2 ? 'text' : 'password'"
Florent Lavelle's avatar
Florent Lavelle committed
                          :placeholder="$t('profile.form.passwordChange.confirmPlaceholder')"
                        <span class="input-group-text">
                          <b-icon data-test="userProfile-toggleShowNewPassword2"
                            :icon="showNewPassword2?'eye-slash-fill':'eye-fill'"
                            @click="showNewPassword2 = !showNewPassword2"
                          />
                        </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>
Florent Lavelle's avatar
Florent Lavelle committed
                      <li>{{ $t('profile.form.passwordChange.passwordHelp')[0] }}</li>
                      <li>{{ $t('profile.form.passwordChange.passwordHelp')[1] }}</li>
                      <li>{{ $t('profile.form.passwordChange.passwordHelp')[2] }}</li>
                      <li>{{ $t('profile.form.passwordChange.passwordHelp')[3] }}</li>
                    </ul>
                  </div>
                </div>
              </div>
            </form>
            <div class="form-footer">
              <b-button
                :disabled="(!formPassword.password || !formPassword.newPassword1 || !formPassword.newPassword2)"
                :pressed="btnPressed"
                data-test="userProfile-submitNewPassword"
                @click.prevent="handleSubmit(submitNewPassword)"
                variant="primary"
              >
Florent Lavelle's avatar
Florent Lavelle committed
                {{ $t('words.validate') }}
              </b-button>
            </div>
          </b-overlay>
        </ValidationObserver>

        <h5>{{ $t('profile.personalDataExport.title') }}</h5>
        <hr class="divider">
        <PersonalDataExport />
      </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, mapMutations, mapActions } from 'vuex';

Florent Lavelle's avatar
Florent Lavelle committed
import i18n from '@/i18n';

import Swal from "sweetalert2";
import "sweetalert2/dist/sweetalert2.min.css";

import PersonalDataExport from '../components/PersonalDataExport.vue';

import {
  ValidationObserver,
  ValidationProvider,
  extend,
  configure,
} from 'vee-validate';

import { required, email, confirmed } 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'),
});

configure({
  classes: {
    valid: 'is-valid',
    invalid: 'is-invalid',
  },
});

export default {
  name: 'UserProfile',

  components: {
    ValidationObserver,
    ValidationProvider,
  },

  data() {
    return {
      loadingUserInformation: false,
      loadingUserPassword: false,
      loadingUserEmail: false,
      formUser: {
        first_name: null,
        last_name: null,
        email: null,
        phone_number: null,
        comments: null,
        username: null
      },
      formEmail: {
        new_email: null
      },
      formPassword: {
        password: null,
        newPassword1: null,
        newPassword2: null
      },
      isOrganisationSelected: false,
      organisation: null,
      btnPressed: false,
      showPassword: false,
      showNewPassword2: false,
    };
  },

  computed: {
    ...mapState('user', [
      'userData',
      'userError',
      'success'
    ]),
    ...mapState('organisations', ['organisationsRoles']),
    ...mapState('sign-in', [
      'next'
Florent Lavelle's avatar
Florent Lavelle committed
    ]),

    logoPath() {
      return require(process.env.VUE_APP_LOGO);
    }
Florent Lavelle's avatar
Florent Lavelle committed
  watch: {
    '$i18n.locale': function(newValue, oldValue) {
      if (newValue !== oldValue) {
        this.$refs.form.validate();
      }
    },

    userError(newValue) {
      if (newValue) {
        for (const [key, value] of Object.entries(newValue)) {
          this.$refs[key].applyResult({
            errors: value,
            valid: false,
            failedRules: {}
          });
        }
      }
    }
Florent Lavelle's avatar
Florent Lavelle committed
  },

  created() {
    this.SET_NEXT(this.$route.query.next || process.env.VUE_APP_NEXT_DEFAULT);
    if (!this.userData) {
      this.loadingUserInformation = true;
      this.GET_USER_DETAIL()
      .then(() => {
        this.formUser = {
          ...this.formUser,
          ...this.userData
        };
        this.loadingUserInformation = false;
      });
    }
    if (this.organisationsRoles.length === 0) {
      this.GET_ORGANISATIONS_ROLES();
    }
  },

  methods: {
    ...mapMutations('sign-in', [
      'SET_NEXT'
    ]),
    ...mapActions('user', [
      'GET_USER_DETAIL',
      'UPDATE_USER_DETAIL'
    ]),
    ...mapActions('organisations', [
      'GET_ORGANISATIONS_ROLES'
    ]),

    goBackToNext() {
      if (
        this.$config.authorizedRedirections.some(reg => {
          return reg.test(decodeURIComponent(this.next))
        })
      ) {
        this.$router.push(this.$route.path);
        window.location.pathname = this.next;
      } else {
        this.$router.push({ name: 'NotFound' });
      }
    },

    submitUserInformations() {
      this.loadingUserInformation = true
      this.UPDATE_USER_DETAIL(this.formUser)
        .then(() => {
          this.GET_USER_DETAIL()
            .then(() => {
              this.formUser = {
                ...this.formUser,
                ...this.userData
              };
              this.loadingUserInformation = false;
            })
            .catch(() => {
              this.loadingUserInformation = false;
            });
        })
        .catch(() => {
          this.loadingUserInformation = false;
        });
    },

    submitNewEmail() {
      const data = {
        new_email: this.formEmail.new_email,
      };
      this.loadingUserEmail = true;
      this.UPDATE_USER_DETAIL(data)
      .then(() => {
        this.loadingUserEmail = false;
        Swal.fire({
          position: 'center',
          heightAuto: false,
          icon: 'success',
          text: `Un e-mail est envoyé à votre nouvelle adresse. 
            Il contient un lien de validation sur lequel vous devez 
            cliquer pour confirmer le changement. 
          `,
          showConfirmButton: true,
          confirmButtonText: 'OK',
          confirmButtonColor: '#187CC6'
        });
      });
    },

    submitNewPassword() {
      const data = {
        password: this.formPassword.password,
        new_password: this.formPassword.newPassword2
      };
      this.loadingUserPassword = true;
      this.UPDATE_USER_DETAIL(data)
      .then(() => {
        this.loadingUserPassword = false;
        if (this.success && this.success.length) {
          Swal.fire({
            position: 'center',
            heightAuto: false,
            icon: 'success',
            text: `Votre mot de passe a bien été modifié.
              Vous allez être redirigé vers la page de connexion.
            `,
            showConfirmButton: true,
            confirmButtonText: 'OK',
            confirmButtonColor: '#187CC6'
          }).then((result) => {
            if (result.isConfirmed) {
              this.$router.push({ name: 'SignIn' });
            }
          });
        }
      });
    }
  }

}
</script>

<style lang="less" scoped>

#back-button {
  font-size: 1.5em;
  position: -webkit-sticky; /* Safari */
  position: sticky;
  top: 5%;
  left: 40px;
  align-self: flex-start;
  border: 2px solid #9BD0FF;
  border-radius: 8px;
  letter-spacing: 1px;
}

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

    #export_data {
      margin-top: 24px;
    }

      margin-bottom: 20px;
      margin-top: 40px;
      color: #373b3d;

      h5 {
        margin-bottom: 20px;
        margin-top: 40px;
        color: #373b3d;
      }

      .row {
        margin-bottom: 1.6rem;
      }

      .input-group {
        span {
          cursor: pointer;
          border-bottom-left-radius: 0;
          border-top-left-radius: 0;
          border-left: none
        }
      }
    }
    .infos {
      font-size: 0.7em;
      font-style: italic;
      margin-right: 1em;
      ul {
        padding-left: 1rem;
      }
    }
    .form-footer {
      display: flex;
      justify-content: flex-end;
      margin-left: 7px;
      margin-top: 30px;
      button {
        margin-left: 2em;
      }
      button.btn-primary {
        border: 2px solid #9BD0FF;
        border-radius: 8px;
      }
      button.btn-outline-secondary {
        background-color: #F7F8FA;
        border: 2px solid #A9B2B9;
        border-radius: 8px;
        color: #2F3234;
      }
      button.btn-outline-secondary:hover {
        color: white;
        background-color: #4b4b4b;
      }
    }
  }
}

.form-errors {
  color: #EB0600 !important;
}

.form-success {
  color: #30C963 !important;
}

.footer {
  position: relative;
  bottom: 0;
  font-size: small;
  margin-top: 2rem;
}

.footer a {
  text-decoration: none;
}
</style>