1<template> 2 <b-container fluid="xl"> 3 <page-title /> 4 5 <b-row> 6 <b-col md="8" lg="8" xl="6"> 7 <page-section 8 :section-title="$t('pageProfileSettings.profileInfoTitle')" 9 > 10 <dl> 11 <dt>{{ $t('pageProfileSettings.username') }}</dt> 12 <dd> 13 {{ username }} 14 </dd> 15 </dl> 16 </page-section> 17 </b-col> 18 </b-row> 19 20 <b-form @submit.prevent="submitForm"> 21 <!-- Hidden username field for browser autocomplete accessibility --> 22 <input 23 type="text" 24 name="username" 25 :value="username" 26 autocomplete="username" 27 class="visually-hidden" 28 aria-hidden="true" 29 tabindex="-1" 30 /> 31 <b-row> 32 <b-col sm="8" md="6" xl="3"> 33 <page-section 34 :section-title="$t('pageProfileSettings.changePassword')" 35 > 36 <b-form-group 37 id="input-group-0" 38 :label="$t('pageProfileSettings.currentPassword')" 39 label-for="input-0" 40 > 41 <input-password-toggle> 42 <b-form-input 43 id="old-password" 44 v-model="form.currentPassword" 45 type="password" 46 autocomplete="current-password" 47 data-test-id="profileSettings-input-ocurrentPassword" 48 class="form-control-with-button" 49 /> 50 </input-password-toggle> 51 </b-form-group> 52 <b-form-group 53 id="input-group-1" 54 :label="$t('pageProfileSettings.newPassword')" 55 label-for="input-1" 56 > 57 <b-form-text id="password-help-block"> 58 {{ 59 $t('pageUserManagement.modal.passwordMustBeBetween', { 60 min: passwordRequirements.minLength, 61 max: passwordRequirements.maxLength, 62 }) 63 }} 64 </b-form-text> 65 <input-password-toggle> 66 <b-form-input 67 id="password" 68 v-model="form.newPassword" 69 type="password" 70 aria-describedby="password-help-block" 71 autocomplete="new-password" 72 :state="getValidationState(v$.form.newPassword)" 73 data-test-id="profileSettings-input-newPassword" 74 class="form-control-with-button" 75 @input="v$.form.newPassword.$touch()" 76 /> 77 <b-form-invalid-feedback role="alert"> 78 <template 79 v-if=" 80 v$.form.newPassword.minLength.$invalid || 81 v$.form.newPassword.maxLength.$invalid 82 " 83 > 84 {{ 85 $t('pageProfileSettings.newPassLabelTextInfo', { 86 min: passwordRequirements.minLength, 87 max: passwordRequirements.maxLength, 88 }) 89 }} 90 </template> 91 </b-form-invalid-feedback> 92 </input-password-toggle> 93 </b-form-group> 94 <b-form-group 95 id="input-group-2" 96 :label="$t('pageProfileSettings.confirmPassword')" 97 label-for="input-2" 98 > 99 <input-password-toggle> 100 <b-form-input 101 id="password-confirmation" 102 v-model="form.confirmPassword" 103 type="password" 104 autocomplete="new-password" 105 :state="getValidationState(v$.form.confirmPassword)" 106 data-test-id="profileSettings-input-confirmPassword" 107 class="form-control-with-button" 108 @input="v$.form.confirmPassword.$touch()" 109 /> 110 <b-form-invalid-feedback role="alert"> 111 <template 112 v-if="v$.form.confirmPassword.sameAsPassword.$invalid" 113 > 114 {{ $t('pageProfileSettings.passwordsDoNotMatch') }} 115 </template> 116 </b-form-invalid-feedback> 117 </input-password-toggle> 118 </b-form-group> 119 </page-section> 120 </b-col> 121 </b-row> 122 <page-section :section-title="$t('pageProfileSettings.timezoneDisplay')"> 123 <p>{{ $t('pageProfileSettings.timezoneDisplayDesc') }}</p> 124 <b-row> 125 <b-col md="9" lg="8" xl="9"> 126 <b-form-group :label="$t('pageProfileSettings.timezone')"> 127 <b-form-radio 128 v-model="form.isUtcDisplay" 129 :value="true" 130 data-test-id="profileSettings-radio-defaultUTC" 131 > 132 {{ $t('pageProfileSettings.defaultUTC') }} 133 </b-form-radio> 134 <b-form-radio 135 v-model="form.isUtcDisplay" 136 :value="false" 137 data-test-id="profileSettings-radio-browserOffset" 138 > 139 {{ 140 $t('pageProfileSettings.browserOffset', { 141 timezone, 142 }) 143 }} 144 </b-form-radio> 145 </b-form-group> 146 </b-col> 147 </b-row> 148 </page-section> 149 <b-button 150 variant="primary" 151 type="submit" 152 data-test-id="profileSettings-button-saveSettings" 153 > 154 {{ $t('global.action.saveSettings') }} 155 </b-button> 156 </b-form> 157 </b-container> 158</template> 159 160<script> 161import BVToastMixin from '@/components/Mixins/BVToastMixin'; 162import InputPasswordToggle from '@/components/Global/InputPasswordToggle'; 163import { maxLength, minLength, sameAs } from '@vuelidate/validators'; 164import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; 165import LocalTimezoneLabelMixin from '@/components/Mixins/LocalTimezoneLabelMixin'; 166import PageTitle from '@/components/Global/PageTitle'; 167import PageSection from '@/components/Global/PageSection'; 168import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; 169import { useVuelidate } from '@vuelidate/core'; 170import { useI18n } from 'vue-i18n'; 171import i18n from '@/i18n'; 172 173export default { 174 name: 'ProfileSettings', 175 components: { InputPasswordToggle, PageSection, PageTitle }, 176 mixins: [ 177 BVToastMixin, 178 LocalTimezoneLabelMixin, 179 LoadingBarMixin, 180 VuelidateMixin, 181 ], 182 setup() { 183 return { 184 v$: useVuelidate(), 185 }; 186 }, 187 data() { 188 return { 189 $t: useI18n().t, 190 form: { 191 newPassword: '', 192 confirmPassword: '', 193 currentPassword: '', 194 isUtcDisplay: this.$store.getters['global/isUtcDisplay'], 195 }, 196 }; 197 }, 198 computed: { 199 username() { 200 return this.$store.getters['global/username']; 201 }, 202 passwordRequirements() { 203 return this.$store.getters['userManagement/accountPasswordRequirements']; 204 }, 205 timezone() { 206 return this.localOffset(); 207 }, 208 }, 209 created() { 210 this.startLoader(); 211 this.$store 212 .dispatch('userManagement/getAccountSettings') 213 .finally(() => this.endLoader()); 214 }, 215 validations() { 216 return { 217 form: { 218 newPassword: { 219 minLength: minLength(this.passwordRequirements.minLength), 220 maxLength: maxLength(this.passwordRequirements.maxLength), 221 }, 222 confirmPassword: { 223 sameAsPassword: sameAs(this.form.newPassword), 224 }, 225 }, 226 }; 227 }, 228 methods: { 229 saveNewPasswordInputData() { 230 this.v$.form.confirmPassword.$touch(); 231 this.v$.form.newPassword.$touch(); 232 if (this.v$.$invalid) return; 233 let userData = { 234 originalUsername: this.username, 235 password: this.form.newPassword, 236 }; 237 238 this.$store 239 .dispatch('userManagement/updateUser', userData) 240 .then((message) => { 241 (this.form.newPassword = ''), 242 (this.form.confirmPassword = ''), 243 (this.form.currentPassword = ''); 244 this.v$.$reset(); 245 this.successToast(message); 246 this.$store.dispatch('authentication/logout'); 247 }) 248 .catch(({ message }) => this.errorToast(message)); 249 }, 250 saveTimeZonePrefrenceData() { 251 localStorage.setItem('storedUtcDisplay', this.form.isUtcDisplay); 252 this.$store.commit('global/setUtcTime', this.form.isUtcDisplay); 253 this.successToast( 254 i18n.global.t('pageProfileSettings.toast.successUpdatingTimeZone'), 255 ); 256 }, 257 submitForm() { 258 if ( 259 this.form.confirmPassword && 260 this.form.newPassword && 261 this.form.currentPassword 262 ) { 263 this.confirmAuthenticate(); 264 } 265 if ( 266 this.$store.getters['global/isUtcDisplay'] != this.form.isUtcDisplay 267 ) { 268 this.saveTimeZonePrefrenceData(); 269 } 270 }, 271 confirmAuthenticate() { 272 this.v$.form.newPassword.$touch(); 273 if (this.v$.$invalid) return; 274 275 const username = this.username; 276 const password = this.form.currentPassword; 277 278 this.$store 279 .dispatch('authentication/login', { username, password }) 280 .then(() => { 281 this.saveNewPasswordInputData(); 282 }) 283 .catch(() => { 284 this.v$.$reset(); 285 this.errorToast( 286 i18n.global.t('pageProfileSettings.toast.wrongCredentials'), 287 ); 288 }); 289 }, 290 }, 291}; 292</script> 293