Client - update password update in user account

This commit is contained in:
Sam
2022-02-26 21:20:11 +01:00
parent a4d7dc24da
commit 7d78bcc302
19 changed files with 276 additions and 73 deletions

View File

@ -1,6 +1,7 @@
<template>
<div class="password-input">
<input
id="password"
:disabled="disabled"
:placeholder="placeholder"
:required="required"
@ -18,13 +19,21 @@
aria-hidden="true"
/>
</div>
<div v-if="checkStrength" class="form-info">
<i class="fa fa-info-circle" aria-hidden="true" />
{{ $t('user.PASSWORD_INFO') }}
</div>
<PasswordStrength v-if="checkStrength" :password="passwordValue" />
</div>
</template>
<script setup lang="ts">
import { Ref, ref, toRefs, watch, withDefaults } from 'vue'
import PasswordStrength from '@/components/Common/PasswordStength.vue'
interface Props {
checkStrength?: boolean
disabled?: boolean
password?: string
placeholder?: string
@ -32,10 +41,13 @@
}
const props = withDefaults(defineProps<Props>(), {
checkStrength: false,
disabled: false,
password: '',
required: false,
})
const { disabled, password, placeholder, required } = toRefs(props)
const { checkStrength, disabled, password, placeholder, required } =
toRefs(props)
const showPassword: Ref<boolean> = ref(false)
const passwordValue: Ref<string> = ref('')

View File

@ -38,14 +38,13 @@
watch,
} from 'vue'
import { ROOT_STORE } from '@/store/constants'
import { AUTH_USER_STORE, ROOT_STORE } from '@/store/constants'
import { useStore } from '@/use/useStore'
import { getPasswordStrength, setZxcvbnOptions } from '@/utils/password'
interface Props {
password: string
}
const props = defineProps<Props>()
const { password } = toRefs(props)
@ -53,6 +52,9 @@
const language: ComputedRef<string> = computed(
() => store.getters[ROOT_STORE.GETTERS.LANGUAGE]
)
const isSuccess: ComputedRef<boolean> = computed(
() => store.getters[AUTH_USER_STORE.GETTERS.IS_SUCCESS]
)
const passwordScore: Ref<number> = ref(0)
const passwordStrength: Ref<string> = ref('')
const passwordSuggestions: Ref<string[]> = ref([])
@ -77,7 +79,11 @@
watch(
() => password.value,
async (newPassword) => {
calculatePasswordStrength(newPassword)
if (isSuccess.value) {
passwordStrength.value = ''
} else {
calculatePasswordStrength(newPassword)
}
}
)
</script>

View File

@ -0,0 +1,166 @@
<template>
<div id="user-infos-edition">
<Modal
v-if="displayModal"
:title="$t('common.CONFIRMATION')"
:message="$t('user.CONFIRM_ACCOUNT_DELETION')"
@confirmAction="deleteAccount(user.username)"
@cancelAction="updateDisplayModal(false)"
/>
<div class="profile-form form-box">
<ErrorMessage :message="errorMessages" v-if="errorMessages" />
<div class="info-box success-message" v-if="isSuccess">
{{ $t('user.PROFILE.SUCCESSFUL_UPDATE') }}
</div>
<form :class="{ errors: formErrors }" @submit.prevent="updateProfile">
<label class="form-items" for="email">
{{ $t('user.EMAIL') }}
<input id="email" :value="user.email" disabled />
</label>
<label class="form-items" for="password-field">
{{ $t('user.PASSWORD') }}
<PasswordInput
id="password-field"
:disabled="loading"
:checkStrength="true"
:password="userForm.password"
:isSuccess="false"
:required="true"
@updatePassword="updatePassword"
@passwordError="invalidateForm"
/>
</label>
<div class="form-buttons">
<button class="confirm" type="submit">
{{ $t('buttons.SUBMIT') }}
</button>
<button class="cancel" @click.prevent="$router.push('/profile')">
{{ $t('buttons.CANCEL') }}
</button>
<button class="danger" @click.prevent="updateDisplayModal(true)">
{{ $t('buttons.DELETE_MY_ACCOUNT') }}
</button>
</div>
</form>
</div>
</div>
</template>
<script setup lang="ts">
import {
ComputedRef,
Ref,
computed,
reactive,
ref,
toRefs,
onMounted,
watch,
onUnmounted,
} from 'vue'
import PasswordInput from '@/components/Common/PasswordInput.vue'
import { AUTH_USER_STORE, ROOT_STORE } from '@/store/constants'
import { IUserProfile, IUserAccountPayload } from '@/types/user'
import { useStore } from '@/use/useStore'
interface Props {
user: IUserProfile
}
const props = defineProps<Props>()
const { user } = toRefs(props)
const store = useStore()
const userForm: IUserAccountPayload = reactive({
email: '',
password: '',
})
const loading = computed(
() => store.getters[AUTH_USER_STORE.GETTERS.USER_LOADING]
)
const isSuccess: ComputedRef<boolean> = computed(
() => store.getters[AUTH_USER_STORE.GETTERS.IS_SUCCESS]
)
const errorMessages: ComputedRef<string | string[] | null> = computed(
() => store.getters[ROOT_STORE.GETTERS.ERROR_MESSAGES]
)
const formErrors = ref(false)
const displayModal: Ref<boolean> = ref(false)
onMounted(() => {
if (props.user) {
updateUserForm(props.user)
}
})
function invalidateForm() {
formErrors.value = true
}
function updateUserForm(user: IUserProfile) {
userForm.email = user.email
}
function updatePassword(password: string) {
userForm.password = password
}
function updateProfile() {
store.dispatch(AUTH_USER_STORE.ACTIONS.UPDATE_USER_ACCOUNT, {
password: userForm.password,
})
}
function updateDisplayModal(value: boolean) {
displayModal.value = value
}
function deleteAccount(username: string) {
store.dispatch(AUTH_USER_STORE.ACTIONS.DELETE_ACCOUNT, { username })
}
onUnmounted(() =>
store.commit(AUTH_USER_STORE.MUTATIONS.UPDATE_IS_SUCCESS, false)
)
watch(
() => isSuccess.value,
async (isSuccessValue) => {
if (isSuccessValue) {
updatePassword('')
formErrors.value = false
}
}
)
</script>
<style lang="scss" scoped>
@import '~@/scss/vars.scss';
.form-items {
.password-input {
::v-deep(.show-password) {
font-weight: normal;
font-size: 0.8em;
margin-top: -4px;
padding-left: 0;
}
::v-deep(.form-info) {
font-weight: normal;
padding-left: $default-padding;
}
::v-deep(.password-strength-details) {
font-weight: normal;
margin-top: 0;
}
}
}
.form-buttons {
flex-direction: row;
@media screen and (max-width: $x-small-limit) {
flex-direction: column;
}
}
.success-message {
margin: $default-margin * 2 0;
background-color: var(--success-background-color);
color: var(--success-color);
}
</style>

View File

@ -1,32 +1,12 @@
<template>
<div id="user-infos-edition">
<Modal
v-if="displayModal"
:title="$t('common.CONFIRMATION')"
:message="$t('user.CONFIRM_ACCOUNT_DELETION')"
@confirmAction="deleteAccount(user.username)"
@cancelAction="updateDisplayModal(false)"
/>
<div class="profile-form form-box">
<ErrorMessage :message="errorMessages" v-if="errorMessages" />
<form @submit.prevent="updateProfile">
<label class="form-items" for="email">
{{ $t('user.EMAIL') }}
<input id="email" :value="user.email" disabled />
</label>
<label class="form-items" for="registrationDate">
{{ $t('user.PROFILE.REGISTRATION_DATE') }}
<input id="registrationDate" :value="registrationDate" disabled />
</label>
<label class="form-items" for="password">
{{ $t('user.PASSWORD') }}
<PasswordInput
id="password"
:disabled="loading"
@updatePassword="updatePassword"
/>
</label>
<hr />
<label class="form-items" for="first_name">
{{ $t('user.PROFILE.FIRST_NAME') }}
<input
@ -74,9 +54,6 @@
<button class="cancel" @click.prevent="$router.push('/profile')">
{{ $t('buttons.CANCEL') }}
</button>
<button class="danger" @click.prevent="updateDisplayModal(true)">
{{ $t('buttons.DELETE_MY_ACCOUNT') }}
</button>
</div>
</form>
</div>
@ -85,17 +62,8 @@
<script setup lang="ts">
import { format } from 'date-fns'
import {
ComputedRef,
Ref,
computed,
reactive,
ref,
toRefs,
onMounted,
} from 'vue'
import { ComputedRef, computed, reactive, onMounted } from 'vue'
import PasswordInput from '@/components/Common/PasswordInput.vue'
import { AUTH_USER_STORE, ROOT_STORE } from '@/store/constants'
import { IUserProfile, IUserPayload } from '@/types/user'
import { useStore } from '@/use/useStore'
@ -107,9 +75,7 @@
const store = useStore()
const { user } = toRefs(props)
const userForm: IUserPayload = reactive({
password: '',
first_name: '',
last_name: '',
birth_date: '',
@ -127,7 +93,6 @@
const errorMessages: ComputedRef<string | string[] | null> = computed(
() => store.getters[ROOT_STORE.GETTERS.ERROR_MESSAGES]
)
let displayModal: Ref<boolean> = ref(false)
onMounted(() => {
if (props.user) {
@ -147,18 +112,9 @@
function updateBio(value: string) {
userForm.bio = value
}
function updatePassword(password: string) {
userForm.password = password
}
function updateProfile() {
store.dispatch(AUTH_USER_STORE.ACTIONS.UPDATE_USER_PROFILE, userForm)
}
function updateDisplayModal(value: boolean) {
displayModal.value = value
}
function deleteAccount(username: string) {
store.dispatch(AUTH_USER_STORE.ACTIONS.DELETE_ACCOUNT, { username })
}
</script>
<style lang="scss" scoped>

View File

@ -34,7 +34,7 @@
const store = useStore()
const { user, tab } = toRefs(props)
const tabs = ['PROFILE', 'PICTURE', 'PREFERENCES', 'SPORTS']
const tabs = ['PROFILE', 'ACCOUNT', 'PICTURE', 'PREFERENCES', 'SPORTS']
const loading = computed(
() => store.getters[AUTH_USER_STORE.GETTERS.USER_LOADING]
)

View File

@ -64,21 +64,10 @@
: $t('user.PASSWORD')
"
:password="formData.password"
:checkStrength="['reset', 'register'].includes(action)"
@updatePassword="updatePassword"
@passwordError="invalidateForm"
/>
<div
v-if="['reset', 'register'].includes(action)"
class="form-info"
>
<i class="fa fa-info-circle" aria-hidden="true" />
{{ $t('user.PASSWORD_INFO') }}
</div>
<PasswordStrength
v-if="['reset', 'register'].includes(action)"
:password="formData.password"
/>
</div>
<button type="submit" :disabled="registration_disabled">
{{ $t(buttonText) }}
@ -118,7 +107,6 @@
import { useRoute } from 'vue-router'
import PasswordInput from '@/components/Common/PasswordInput.vue'
import PasswordStrength from '@/components/Common/PasswordStength.vue'
import { AUTH_USER_STORE, ROOT_STORE } from '@/store/constants'
import { TAppConfig } from '@/types/application'
import { ILoginRegisterFormData } from '@/types/user'
@ -234,13 +222,6 @@
font-style: italic;
padding: 0 $default-padding;
}
.form-info {
color: var(--alert-color);
font-size: 0.8em;
margin-top: -0.2 * $default-margin;
padding: 0 $default-padding * 1.5;
}
button {
margin: $default-margin;
border: solid 1px var(--app-color);

View File

@ -35,8 +35,9 @@
function getPath(tab: string) {
switch (tab) {
case 'ACCOUNT':
case 'PICTURE':
return '/profile/edit/picture'
return `/profile/edit/${tab.toLocaleLowerCase()}`
case 'PREFERENCES':
case 'SPORTS':
return `/profile${
@ -52,7 +53,10 @@
<style lang="scss">
@import '~@/scss/vars.scss';
.profile-tabs {
margin: $default-margin 0 $default-margin;
.profile-tabs-checkboxes {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: $default-margin * 0.5;
}
</style>