summaryrefslogblamecommitdiffstats
path: root/webapp/src/components/AccountModule.vue
blob: a39a414a6e1b2f1eeaf7acd33c43cd0aa4f72797 (plain) (tree)
1
2
3
4
5
6
7
8
9


         

                              


                                                                                     
                      

                                          
                           













                                                                                             

                                                    

                                                                

         

                               


                                                                                              
                      

                                           
                           













                                                                                        

                                                     

                                                                            




          
                                    
              
                                   
                                  

                                                                                   

                     

                 
                                                                  
                               





                                  



                                                     
 







                                                                                                                             
 

                                                             
                                            


                                                                                                                 
                          

                             
 
















                                                                                                                 
 


                                                                                                       
                                                                                                                                                 










                                                                                                                         
 


                          
 



                                                         
 

                                                             
                                            





























                                                                                                                              
                          

                             
 

                                                                                     
                                            





                                                                                                                                 
                          

                             
 

                                                                                                                  
                                                                                                                                                   



                                                                  
 







                                                                                                                             
 




                                                               




                                                                                           
                



                     
 



                       
                                                 

              
                                          















                                                                                


           
                                 



                      
             
                    
                  


                          








                                                                          


                                                            







                                                                                       
               

     
             
                                                                       
    
            








                                                                        


                                       





                                                                                                              
                                        


                                                                              

                                               
                                                                                                                                                             


                                                                                
                                                                 







                                            




                                      


                                  


                               





                                   
                 
                                                                                                                                                                                                                                                

                   





                                                                   

                   

 

















                             
        
<i18n>
{
  "en": {
    "account": "User Account",
    "info": "Info",
    "deleteAccount": "Delete Account",
    "deleteAccountMsg": "Are you sure you want to permnanently delete your account?",
    "deleteAccountMsg2": "This action cannot be undone",
    "email": "E-Mail",
    "emailError": "E-mail must be valid.",
    "name": "Name",
    "password": "Password",
    "passwordConfirm": "Retype password",
    "passwordChanged": "Password successfully changed.",
    "passwordCurrent": "Current password",
    "passwordEmptyError": "Password can not be empty.",
    "passwordInvalidError": "Password incorrect.",
    "passwordLengthError": "Minimum 8 characters required.",
    "passwordMatchError": "Passwords do not match.",
    "passwordNew": "New password",
    "passwordNotDifferentError": "New password must be different than the current password.",
    "passwordRequirement1": ">= 8 characters",
    "passwordRequirement2": "0+ [0-9]",
    "passwordRequirement3": "0+ [a-z]",
    "passwordRequirement4": "0+ [A-Z]",
    "passwordRequirement5": "0+ [!\"§$%&/()=?+#*.:,;]",
    "passwordRequirements": "Password requirements",
    "security": "Security",
    "username": "Username",
    "userInfoChanged": "User informations successfully changed."
  },
  "de": {
    "account": "Benutzerkonto",
    "info": "Info",
    "deleteAccount": "Account Löschen",
    "deleteAccountMsg": "Sind sie sicher, dass sie ihren Account entgültig Löschen wollen?",
    "deleteAccountMsg2": "Diese Aktion kann nicht rückgängig gemacht werden.",
    "email": "E-Mail",
    "emailError": "Keine gültige E-Mail.",
    "name": "Name",
    "password": "Passwort",
    "passwordChanged": "Passwort wurde erfolgreich geändert.",
    "passwordConfirm": "Passwort wiederholen",
    "passwordCurrent": "Aktuelles Passwort",
    "passwordEmptyError": "Passwort kann nicht leer sein.",
    "passwordInvalidError": "Falsches Passwort.",
    "passwordLengthError": "Es sind mindestens 8 Zeichen erforderlich.",
    "passwordMatchError": "Passwörter stimmen nicht überein.",
    "passwordNew": "Neues Password",
    "passwordNotDifferentError": "Das neue Passwort muss sich vom alten unterscheiden.",
    "passwordRequirement1": ">= 8 Zeichen",
    "passwordRequirement2": "0+ [0-9]",
    "passwordRequirement3": "0+ [a-z]",
    "passwordRequirement4": "0+ [A-Z]",
    "passwordRequirement5": "0+ [!\"§$%&/()=?+#*'-_.:,;]",
    "passwordRequirements": "Passwort anforderungen",
    "security": "Sicherheit",
    "username": "Benutzername",
    "userInfoChanged": "Benutzer Informationen wurden erfolgreich geändert"
  }
}
</i18n>

<template>
  <v-container fill-height class="">
    <v-layout>
      <v-flex xl10 offset-xl1 lg12>
      <v-card class="tabbar-card">
        <v-tabs :dark="tabsDark" :color="tabsColor" :slider-color="tabsSliderColor"
          v-model="tab"
          grow
          hide-slider
        >
          <v-tab>
            <v-icon class="tabbar-tabicon">assignment_ind</v-icon>
            {{ $t('account') }}
          </v-tab>
        </v-tabs>
      </v-card>
      <v-tabs-items v-model="tab">
        <v-tab-item>

          <v-subheader>{{ $t('info') }}</v-subheader>
          <v-card>
            <v-card-text>
              <v-layout wrap>

                <v-flex lg3 md6 sm6 xs12 order-lg2 order-xs2>
                  <v-layout column>
                    <div class="info-input">
                      <div class="body-2 info-heading"><v-icon>account_circle</v-icon><span>{{ $t('username') }}</span></div>
                      <div class="info-text">{{ user.username }}</div>
                    </div>
                  </v-layout>
                </v-flex>

                <v-flex lg3 md6 sm6 xs12 order-lg4 order-xs4>
                  <v-layout column>
                    <div class="info-input">
                      <div class="body-2 info-heading"><v-icon>label</v-icon><span>{{ $t('name') }}</span></div>
                      <div v-if="!editInfoMode" class="info-text">{{ user.name }}</div>
                      <v-text-field v-else v-model="infoName" class="info-text pa-0" hide-details></v-text-field>
                    </div>
                  </v-layout>
                </v-flex>

                <v-flex lg3 md6 sm6 xs12 order-lg6 order-xs6>
                  <v-layout column>
                    <div class="info-input">
                      <div class="body-2 info-heading"><v-icon>email</v-icon><span>{{ $t('email') }}</span></div>
                      <div v-if="!editInfoMode" class="info-text">{{ user.email }}</div>
                      <v-text-field
                        v-else
                        v-model="infoEmail"
                        :rules="emailRules"
                        ref="emailField"
                        validate-on-blur
                        class="info-text pa-0"
                        hide-details
                      ></v-text-field>
                    </div>
                  </v-layout>
                </v-flex>

                <v-flex lg3 md6 sm6 xs12 order-lg7 order-md3 order-sm3 order-xs1 class="text-xs-right">
                  <div class="info-input">
                    <div v-if="!editInfoMode">
                      <v-btn color="primary" flat @click="editInfo" class="info-buttons tutorial-element label-left" style="--label-number: '1'">
                        <v-icon left>create</v-icon>{{ $t('edit') }}
                      </v-btn>
                    </div>
                    <div v-else>
                      <v-btn color="primary" flat @click="cancelEditInfo" class="info-buttons">{{ $t('cancel') }}</v-btn>
                      <v-btn color="primary" @click="submitInfo" class="info-buttons">
                        <v-icon left>save</v-icon>{{ $t('save') }}
                      </v-btn>
                    </div>
                  </div>
                </v-flex>

              </v-layout>
            </v-card-text>
          </v-card>

          <v-subheader>{{ $t('security') }}</v-subheader>
          <v-card>
            <v-card-text>
              <v-layout wrap>

                <v-flex lg4 md6 sm6 xs12 order-lg1 order-xs2>
                  <v-layout column>
                    <div class="info-input">
                      <div class="body-2 info-heading"><v-icon>lock</v-icon><span>{{ $t('password') }}</span></div>
                      <div v-if="!editPasswordMode" class="info-text">********</div>
                      <div v-else class="info-text">
                        <v-form ref="passwordForm" v-model="valid" lazy-validation>
                          <v-text-field
                            type="password"
                            v-model="passwordCurrent"
                            :rules="currentPasswordRules"
                            @input="clearCurrentPasswordErrors"
                          >
                            <template slot="label">{{ $t('passwordCurrent') }} <label class="error--text">*</label></template>
                          </v-text-field>
                           <v-text-field
                            validate-on-blur
                            type="password"
                            v-model="passwordNew"
                            :rules="passwordRules"
                          >
                            <template slot="label">{{ $t('passwordNew') }} <label class="error--text">*</label></template>
                           </v-text-field>
                          <v-text-field
                            validate-on-blur
                            type="password"
                            v-model="passwordConfirm"
                            :rules="confirmPasswordRules"
                          >
                            <template slot="label">{{ $t('passwordConfirm') }} <label class="error--text">*</label></template>
                          </v-text-field>
                        </v-form>
                      </div>
                    </div>
                  </v-layout>
                </v-flex>

                <v-flex lg4 md6 sm6 xs12 order-lg1 order-xs3 v-if="editPasswordMode">
                  <v-layout column>
                    <div class="info-input">
                      <div class="body-2 info-heading"><v-icon>ballot</v-icon><span>{{ $t('passwordRequirements') }}</span></div>
                      <div class="info-text">{{ $t('passwordRequirement1') }}</div>
                      <div class="info-text">{{ $t('passwordRequirement2') }}</div>
                      <div class="info-text">{{ $t('passwordRequirement3') }}</div>
                      <div class="info-text">{{ $t('passwordRequirement4') }}</div>
                      <div class="info-text">{{ $t('passwordRequirement5') }}</div>
                    </div>
                  </v-layout>
                </v-flex>

                <v-flex v-if="!editPasswordMode" lg8 sm6 xs12 order-lg3 order-sm2 order-xs1 class="text-xs-right">
                  <div class="info-input">
                    <v-btn color="primary" flat @click="editPassword" class="info-buttons tutorial-element label-left" style="--label-number: '2'">
                      <v-icon left>create</v-icon>{{ $t('edit') }}
                    </v-btn>
                  </div>
                </v-flex>

                <v-flex v-else lg4 sm12 xs12 order-lg3 order-sm1 order-xs1 class="text-xs-right">
                  <div class="info-input">
                      <v-btn color="primary" flat @click="cancelEditPassword" class="info-buttons">{{ $t('cancel') }}</v-btn>
                      <v-btn color="primary" @click="submitPassword" class="info-buttons">
                        <v-icon left>save</v-icon>{{ $t('save') }}
                      </v-btn>
                  </div>
                </v-flex>

              </v-layout>
            </v-card-text>
          </v-card>
          <v-spacer></v-spacer>
          <div style="display: flex; justify-content: center;">
            <v-btn
              class="delete-button tutorial-element label-left" style="--label-number: '3'"
              color="error"
              @click="dialog = true"
            ><v-icon left>delete</v-icon>{{ $t('deleteAccount') }}</v-btn>
          </div>
        </v-tab-item>
      </v-tabs-items>
      </v-flex>
    </v-layout>

    <v-dialog
      :value="dialog"
      scrollable
      max-width="500px"
      :fullscreen="$vuetify.breakpoint.smAndDown"
    >
      <v-card>
        <v-card-title class="elevation-3">
          <div class="headline">{{ $t('deleteAccount') }}</div>
        </v-card-title>
        <v-card-text>
          <div>{{ $t('deleteAccountMsg') }}</div>
          <span class="grey--text">{{ $t('deleteAccountMsg2') }}</span>
        </v-card-text>
        <v-divider></v-divider>
        <v-card-actions>
          <v-spacer></v-spacer>
          <v-btn flat="flat" @click="dialog = false">{{ $t('cancel') }}</v-btn>
          <v-btn color="error" @click="deleteAccount">{{ $t('delete') }}</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>

  </v-container>
</template>

<script>
import { mapGetters } from 'vuex'
export default {
  name: 'AccountPage',
  data () {
    return {
      tab: 0,
      dialog: false,
      valid: true,
      editInfoMode: false,
      infoName: '',
      infoEmail: '',
      passwordConfirm: '',
      passwordCurrent: '',
      passwordNew: '',
      invalidPasswordError: false,
      editPasswordMode: false,
      currentPasswordRules: [
        v => !!v || this.$t('passwordEmptyError'),
        v => !this.invalidPasswordError || this.$t('passwordInvalidError')
      ],
      emailRules: [
        v => this.$validateEmail(v) || this.$t('emailError')
      ],
      passwordRules: [
        v => !!v || this.$t('passwordEmptyError'),
        v => v.length >= 8 || this.$t('passwordLengthError'),
        v => v !== this.passwordCurrent || this.$t('passwordNotDifferentError')
      ],
      confirmPasswordRules: [
        v => this.passwordConfirm === this.passwordNew || this.$t('passwordMatchError')
      ],
      testId: 0
    }
  },
  computed: {
    ...mapGetters(['tabsDark', 'tabsColor', 'tabsSliderColor', 'user'])
  },
  methods: {
    deleteAccount () {
      this.dialog = false
      this.$http.delete('/api/users/current').then(response => {
        this.$http.post('/api/authentication/logout').then(response => {
          this.$router.push('/login')
          this.$socket.close()
        })
      })
    },
    clearCurrentPasswordErrors () {
      this.invalidPasswordError = false
    },
    submitInfo () {
      if (!this.$refs.emailField.validate()) return

      // Axios request to submit info (username, name, email)
      this.$http.post('/api/users/current', { name: this.infoName, email: this.infoEmail }).then(response => {
        this.editInfoMode = false
        this.$store.dispatch('loadUser')
        this.$snackbar({ color: 'success', text: this.$t('userInfoChanged') })
      })
    },
    submitPassword () {
      if (this.$refs.passwordForm.validate()) {
        this.$http.post('/api/users/' + this.user.id + '/password', { passwordCurrent: this.passwordCurrent, password: this.passwordNew }).then(response => {
          this.cancelEditPassword()
          this.$snackbar({ color: 'success', text: this.$t('passwordChanged') })
        }).catch(error => {
          if (error.response.data.error === 'PASSWORD_INVALID') {
            this.invalidPasswordError = true
            this.passwordNew = ''
            this.passwordConfirm = ''
          }
          this.$refs.passwordForm.validate()
        })
      }
    },
    editInfo () {
      this.infoName = this.user.name
      this.infoEmail = this.user.email
      this.editInfoMode = true
    },
    editPassword () {
      this.editPasswordMode = true
    },
    cancelEditInfo () {
      this.editInfoMode = false
    },
    cancelEditPassword () {
      this.editPasswordMode = false
      this.passwordCurrent = ''
      this.passwordNew = ''
      this.passwordConfirm = ''
    },
    newAlert () {
      this.$alert({ type: 'success', text: 'aaaaaaaaaaaaaaaaaaaaaaaaaas das dsad asdpioipoidijoijoawiojdiojijowaijo d o wiadijo oiawio jdi aaaaaaaaaaaaaaaaaaaaaa uo iashdoiuas dhuas hduioash diuash diuash diuash diuh test ' + this.testId })
      this.testId++
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.delete-button {
  margin-top: 40px;
}

.info-buttons {
  margin: 0;
}
.info-input {
  margin: 20px;
}
.info-heading {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
}
.info-heading > span {
  margin-left: 10px;
}
.info-text {
  margin-left: 34px;
  font-family: 'Roboto Mono';
}
</style>