Client - add password reset in users admin

This commit is contained in:
Sam 2022-03-13 17:41:37 +01:00
parent ce680b0d3e
commit 6371fb0622
11 changed files with 108 additions and 13 deletions

View File

@ -3,11 +3,22 @@
<Modal <Modal
v-if="displayModal" v-if="displayModal"
:title="$t('common.CONFIRMATION')" :title="$t('common.CONFIRMATION')"
message="admin.CONFIRM_USER_ACCOUNT_DELETION" :message="
displayModal === 'delete'
? 'admin.CONFIRM_USER_ACCOUNT_DELETION'
: 'admin.CONFIRM_USER_PASSWORD_RESET'
"
:strongMessage="user.username" :strongMessage="user.username"
@confirmAction="deleteUserAccount(user.username)" @confirmAction="
@cancelAction="updateDisplayModal(false)" displayModal === 'delete'
? deleteUserAccount(user.username)
: resetUserPassword(user.username)
"
@cancelAction="updateDisplayModal('')"
/> />
<div class="info-box success-message" v-if="isSuccess">
{{ $t('admin.PASSWORD_RESET_SUCCESSFUL') }}
</div>
<dl> <dl>
<dt>{{ $t('user.PROFILE.REGISTRATION_DATE') }}:</dt> <dt>{{ $t('user.PROFILE.REGISTRATION_DATE') }}:</dt>
<dd>{{ registrationDate }}</dd> <dd>{{ registrationDate }}</dd>
@ -28,10 +39,16 @@
<button <button
class="danger" class="danger"
v-if="authUser.username !== user.username" v-if="authUser.username !== user.username"
@click.prevent="updateDisplayModal(true)" @click.prevent="updateDisplayModal('delete')"
> >
{{ $t('admin.DELETE_USER') }} {{ $t('admin.DELETE_USER') }}
</button> </button>
<button
v-if="authUser.username !== user.username"
@click.prevent="updateDisplayModal('reset')"
>
{{ $t('admin.RESET_USER_PASSWORD') }}
</button>
<button @click="$router.go(-1)">{{ $t('buttons.BACK') }}</button> <button @click="$router.go(-1)">{{ $t('buttons.BACK') }}</button>
</div> </div>
<div class="profile-buttons" v-else> <div class="profile-buttons" v-else>
@ -45,9 +62,18 @@
<script setup lang="ts"> <script setup lang="ts">
import { format } from 'date-fns' import { format } from 'date-fns'
import { ComputedRef, Ref, computed, ref, toRefs, withDefaults } from 'vue' import {
ComputedRef,
Ref,
computed,
ref,
toRefs,
withDefaults,
watch,
onUnmounted,
} from 'vue'
import { AUTH_USER_STORE, USERS_STORE } from '@/store/constants' import { AUTH_USER_STORE, ROOT_STORE, USERS_STORE } from '@/store/constants'
import { IAuthUserProfile, IUserProfile } from '@/types/user' import { IAuthUserProfile, IUserProfile } from '@/types/user'
import { useStore } from '@/use/useStore' import { useStore } from '@/use/useStore'
@ -75,14 +101,40 @@
? format(new Date(props.user.birth_date), 'dd/MM/yyyy') ? format(new Date(props.user.birth_date), 'dd/MM/yyyy')
: '' : ''
) )
let displayModal: Ref<boolean> = ref(false) const isSuccess = computed(
() => store.getters[USERS_STORE.GETTERS.USERS_IS_SUCCESS]
)
let displayModal: Ref<string> = ref('')
function updateDisplayModal(value: boolean) { function updateDisplayModal(value: string) {
displayModal.value = value displayModal.value = value
if (value !== '') {
store.commit(USERS_STORE.MUTATIONS.UPDATE_IS_SUCCESS, false)
}
} }
function deleteUserAccount(username: string) { function deleteUserAccount(username: string) {
store.dispatch(USERS_STORE.ACTIONS.DELETE_USER_ACCOUNT, { username }) store.dispatch(USERS_STORE.ACTIONS.DELETE_USER_ACCOUNT, { username })
} }
function resetUserPassword(username: string) {
store.dispatch(USERS_STORE.ACTIONS.UPDATE_USER, {
username,
resetPassword: true,
})
}
onUnmounted(() => {
store.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
store.commit(USERS_STORE.MUTATIONS.UPDATE_IS_SUCCESS, false)
})
watch(
() => isSuccess.value,
(newIsSuccess) => {
if (newIsSuccess) {
updateDisplayModal('')
}
}
)
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -14,10 +14,13 @@
}, },
"BACK_TO_ADMIN": "Back to admin", "BACK_TO_ADMIN": "Back to admin",
"CONFIRM_USER_ACCOUNT_DELETION": "Are you sure you want to delete {0} account? All data will be deleted, this cannot be undone.", "CONFIRM_USER_ACCOUNT_DELETION": "Are you sure you want to delete {0} account? All data will be deleted, this cannot be undone.",
"CONFIRM_USER_PASSWORD_RESET": "Are you sure you want to reset {0} password?",
"DELETE_USER": "Delete user", "DELETE_USER": "Delete user",
"ENABLE_DISABLE_SPORTS": "Enable/disable sports.", "ENABLE_DISABLE_SPORTS": "Enable/disable sports.",
"PASSWORD_RESET_SUCCESSFUL": "The password has been reset.",
"REGISTRATION_DISABLED": "Registration is currently disabled.", "REGISTRATION_DISABLED": "Registration is currently disabled.",
"REGISTRATION_ENABLED": "Registration is currently enabled.", "REGISTRATION_ENABLED": "Registration is currently enabled.",
"RESET_USER_PASSWORD": "Reset password",
"SPORTS": { "SPORTS": {
"TABLE": { "TABLE": {
"ACTIVE": "Active", "ACTIVE": "Active",

View File

@ -13,11 +13,14 @@
"ZIP_UPLOAD_MAX_SIZE_LABEL": "Nombre max. de fichiers dans une archive zip " "ZIP_UPLOAD_MAX_SIZE_LABEL": "Nombre max. de fichiers dans une archive zip "
}, },
"BACK_TO_ADMIN": "Revenir à l'admin", "BACK_TO_ADMIN": "Revenir à l'admin",
"CONFIRM_USER_ACCOUNT_DELETION": "Etes-vous sûr de vouloir supprimer le compte de {0} ? Toutes les données seront définitivement.", "CONFIRM_USER_ACCOUNT_DELETION": "Êtes-vous sûr de vouloir supprimer le compte de l'utilisateur {0} ? Toutes les données seront définitivement.",
"CONFIRM_USER_PASSWORD_RESET": "Êtes-vous sûr de vouloir réinitialiser le mot de passe de l'utilisateur {0} ?",
"DELETE_USER": "Supprimer l'utilisateur", "DELETE_USER": "Supprimer l'utilisateur",
"ENABLE_DISABLE_SPORTS": "Activer/désactiver des sports.", "ENABLE_DISABLE_SPORTS": "Activer/désactiver des sports.",
"PASSWORD_RESET_SUCCESSFUL": "Le mot de passe a été réinitialisé.",
"REGISTRATION_DISABLED": "Les inscriptions sont actuellement désactivées.", "REGISTRATION_DISABLED": "Les inscriptions sont actuellement désactivées.",
"REGISTRATION_ENABLED": "Les inscriptions sont actuellement activées.", "REGISTRATION_ENABLED": "Les inscriptions sont actuellement activées.",
"RESET_USER_PASSWORD": "Réinit. le mot de passe",
"SPORTS": { "SPORTS": {
"TABLE": { "TABLE": {
"ACTIVE": "Actif", "ACTIVE": "Actif",

View File

@ -104,14 +104,23 @@ export const actions: ActionTree<IUsersState, IRootState> & IUsersActions = {
payload: IAdminUserPayload payload: IAdminUserPayload
): void { ): void {
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES) context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
context.commit(USERS_STORE.MUTATIONS.UPDATE_IS_SUCCESS, false)
const data: Record<string, boolean> = {}
if (payload.admin !== undefined) {
data.admin = payload.admin
}
if (payload.resetPassword !== undefined) {
data.reset_password = payload.resetPassword
}
authApi authApi
.patch(`users/${payload.username}`, { admin: payload.admin }) .patch(`users/${payload.username}`, data)
.then((res) => { .then((res) => {
if (res.data.status === 'success') { if (res.data.status === 'success') {
context.commit( context.commit(
USERS_STORE.MUTATIONS.UPDATE_USER_IN_USERS, USERS_STORE.MUTATIONS.UPDATE_USER_IN_USERS,
res.data.data.users[0] res.data.data.users[0]
) )
context.commit(USERS_STORE.MUTATIONS.UPDATE_IS_SUCCESS, true)
} else { } else {
handleError(context, null) handleError(context, null)
} }

View File

@ -10,6 +10,7 @@ export enum UsersActions {
export enum UsersGetters { export enum UsersGetters {
USER = 'USER', USER = 'USER',
USERS = 'USERS', USERS = 'USERS',
USERS_IS_SUCCESS = 'USERS_IS_SUCCESS',
USERS_LOADING = 'USERS_LOADING', USERS_LOADING = 'USERS_LOADING',
USERS_PAGINATION = 'USERS_PAGINATION', USERS_PAGINATION = 'USERS_PAGINATION',
} }
@ -20,4 +21,5 @@ export enum UsersMutations {
UPDATE_USERS = 'UPDATE_USERS', UPDATE_USERS = 'UPDATE_USERS',
UPDATE_USERS_LOADING = 'UPDATE_USERS_LOADING', UPDATE_USERS_LOADING = 'UPDATE_USERS_LOADING',
UPDATE_USERS_PAGINATION = 'UPDATE_USERS_PAGINATION', UPDATE_USERS_PAGINATION = 'UPDATE_USERS_PAGINATION',
UPDATE_IS_SUCCESS = 'UPDATE_IS_SUCCESS',
} }

View File

@ -11,6 +11,9 @@ export const getters: GetterTree<IUsersState, IRootState> & IUsersGetters = {
[USERS_STORE.GETTERS.USERS]: (state: IUsersState) => { [USERS_STORE.GETTERS.USERS]: (state: IUsersState) => {
return state.users return state.users
}, },
[USERS_STORE.GETTERS.USERS_IS_SUCCESS]: (state: IUsersState) => {
return state.isSuccess
},
[USERS_STORE.GETTERS.USERS_LOADING]: (state: IUsersState) => { [USERS_STORE.GETTERS.USERS_LOADING]: (state: IUsersState) => {
return state.loading return state.loading
}, },

View File

@ -38,4 +38,10 @@ export const mutations: MutationTree<IUsersState> & TUsersMutations = {
) { ) {
state.pagination = pagination state.pagination = pagination
}, },
[USERS_STORE.MUTATIONS.UPDATE_IS_SUCCESS](
state: IUsersState,
isSuccess: boolean
) {
state.isSuccess = isSuccess
},
} }

View File

@ -6,5 +6,6 @@ export const usersState: IUsersState = {
user: <IUserProfile>{}, user: <IUserProfile>{},
users: [], users: [],
loading: false, loading: false,
isSuccess: false,
pagination: <IPagination>{}, pagination: <IPagination>{},
} }

View File

@ -18,6 +18,7 @@ export interface IUsersState {
user: IUserProfile user: IUserProfile
users: IUserProfile[] users: IUserProfile[]
loading: boolean loading: boolean
isSuccess: boolean
pagination: IPagination pagination: IPagination
} }
@ -49,6 +50,7 @@ export interface IUsersActions {
export interface IUsersGetters { export interface IUsersGetters {
[USERS_STORE.GETTERS.USER](state: IUsersState): IUserProfile [USERS_STORE.GETTERS.USER](state: IUsersState): IUserProfile
[USERS_STORE.GETTERS.USERS](state: IUsersState): IUserProfile[] [USERS_STORE.GETTERS.USERS](state: IUsersState): IUserProfile[]
[USERS_STORE.GETTERS.USERS_IS_SUCCESS](state: IUsersState): boolean
[USERS_STORE.GETTERS.USERS_LOADING](state: IUsersState): boolean [USERS_STORE.GETTERS.USERS_LOADING](state: IUsersState): boolean
[USERS_STORE.GETTERS.USERS_PAGINATION](state: IUsersState): IPagination [USERS_STORE.GETTERS.USERS_PAGINATION](state: IUsersState): IPagination
} }
@ -65,6 +67,7 @@ export type TUsersMutations<S = IUsersState> = {
state: S, state: S,
pagination: IPagination pagination: IPagination
): void ): void
[USERS_STORE.MUTATIONS.UPDATE_IS_SUCCESS](state: S, isSuccess: boolean): void
} }
export type TUsersStoreModule<S = IUsersState> = Omit< export type TUsersStoreModule<S = IUsersState> = Omit<

View File

@ -50,7 +50,8 @@ export interface IUserEmailUpdatePayload {
export interface IAdminUserPayload { export interface IAdminUserPayload {
username: string username: string
admin: boolean admin?: boolean
resetPassword?: boolean
} }
export interface IUserPreferencesPayload { export interface IUserPreferencesPayload {

View File

@ -2,13 +2,19 @@
<div id="user" class="view" v-if="user.username"> <div id="user" class="view" v-if="user.username">
<UserHeader :user="user" /> <UserHeader :user="user" />
<div class="box"> <div class="box">
<UserInfos :user="user" :from-admin="true" /> <UserInfos :user="user" :from-admin="fromAdmin" />
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ComputedRef, computed, onBeforeMount, onBeforeUnmount } from 'vue' import {
ComputedRef,
computed,
onBeforeMount,
onBeforeUnmount,
toRefs,
} from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import UserHeader from '@/components/User/ProfileDisplay/UserHeader.vue' import UserHeader from '@/components/User/ProfileDisplay/UserHeader.vue'
@ -17,6 +23,12 @@
import { IUserProfile } from '@/types/user' import { IUserProfile } from '@/types/user'
import { useStore } from '@/use/useStore' import { useStore } from '@/use/useStore'
interface Props {
fromAdmin: boolean
}
const props = defineProps<Props>()
const { fromAdmin } = toRefs(props)
const route = useRoute() const route = useRoute()
const store = useStore() const store = useStore()