completed reset password and new credentials features
This commit is contained in:
parent
3fcd1a5a38
commit
88820ce430
@ -3,7 +3,11 @@
|
||||
<v-main>
|
||||
<router-view />
|
||||
|
||||
<!-- Confirmation modal -->
|
||||
<confirm-modal />
|
||||
|
||||
<!-- Info modal -->
|
||||
<info-modal />
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
@ -71,7 +71,7 @@ export default {
|
||||
},
|
||||
deleteMany: async (IDs: number[]): Promise<[AxiosResponse?, string?]> => {
|
||||
try {
|
||||
const data = await $axios.delete(`v1/groups`, {
|
||||
const data = await $axios.delete('v1/groups', {
|
||||
data: {
|
||||
array: IDs,
|
||||
},
|
||||
|
||||
@ -3,6 +3,7 @@ import { AxiosResponse } from 'axios';
|
||||
import { OrderBy } from '@/types/order-by';
|
||||
import { camelToSnake } from '@/mixins/case-converter';
|
||||
import { setURIParams } from '@/mixins/query-params';
|
||||
import { NewCredentials } from '@/types/new-credentials';
|
||||
|
||||
export default {
|
||||
getAll: async (
|
||||
@ -73,7 +74,7 @@ export default {
|
||||
},
|
||||
deleteMany: async (IDs: number[]): Promise<[AxiosResponse?, string?]> => {
|
||||
try {
|
||||
const data = await $axios.delete(`v1/users`, {
|
||||
const data = await $axios.delete('v1/users', {
|
||||
data: {
|
||||
array: IDs,
|
||||
},
|
||||
@ -85,7 +86,7 @@ export default {
|
||||
},
|
||||
blockMany: async (IDs: number[]): Promise<[AxiosResponse?, string?]> => {
|
||||
try {
|
||||
const data = await $axios.patch(`v1/users/block`, {
|
||||
const data = await $axios.patch('v1/users/block', {
|
||||
array: IDs,
|
||||
});
|
||||
return [data, undefined];
|
||||
@ -95,7 +96,7 @@ export default {
|
||||
},
|
||||
unblockMany: async (IDs: number[]): Promise<[AxiosResponse?, string?]> => {
|
||||
try {
|
||||
const data = await $axios.patch(`v1/users/unblock`, {
|
||||
const data = await $axios.patch('v1/users/unblock', {
|
||||
array: IDs,
|
||||
});
|
||||
return [data, undefined];
|
||||
@ -103,4 +104,14 @@ export default {
|
||||
return [undefined, e?.response?.data?.error ?? e.message];
|
||||
}
|
||||
},
|
||||
resetPassword: async (
|
||||
ID: number,
|
||||
): Promise<[AxiosResponse<NewCredentials>?, string?]> => {
|
||||
try {
|
||||
const data = await $axios.patch(`v1/users/password/${ID}`);
|
||||
return [data, undefined];
|
||||
} catch (e: any) {
|
||||
return [undefined, e?.response?.data?.error ?? e.message];
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
import { useConfirmationStore } from '@/stores/confirmation';
|
||||
import { useLocale } from 'vuetify';
|
||||
import { useInfoStore } from '@/stores/info';
|
||||
|
||||
const { t } = useLocale();
|
||||
|
||||
const $confirmation = useConfirmationStore();
|
||||
const $info = useInfoStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-dialog persistent :model-value="$confirmation.show" width="450">
|
||||
<v-dialog persistent :model-value="$info.show" width="450">
|
||||
<v-card>
|
||||
<template v-slot:title>
|
||||
<v-card-title class="p-0 w-full flex items-center">
|
||||
<span class="mso mr-1 text-2xl">warning</span>
|
||||
<span>{{ $confirmation.title }}</span>
|
||||
<span>{{ $info.title }}</span>
|
||||
</v-card-title>
|
||||
</template>
|
||||
|
||||
<v-card-text>{{ $confirmation.message }}</v-card-text>
|
||||
<v-card-text class="whitespace-pre-line">{{ $info.message }}</v-card-text>
|
||||
|
||||
<!-- Actions -->
|
||||
<v-card-actions class="flex justify-end p-5 gap-1">
|
||||
@ -25,16 +25,8 @@ const $confirmation = useConfirmationStore();
|
||||
<v-btn
|
||||
variant="text"
|
||||
class="px-4"
|
||||
:text="t('$vuetify.actions.cancel')"
|
||||
@click="$confirmation.doDecline()"
|
||||
></v-btn>
|
||||
<!-- Confirm -->
|
||||
<v-btn
|
||||
variant="flat"
|
||||
color="primary"
|
||||
class="px-4"
|
||||
:text="t('$vuetify.actions.confirm')"
|
||||
@click="$confirmation.doConfirm()"
|
||||
:text="t('$vuetify.actions.hide')"
|
||||
@click="$info.close()"
|
||||
></v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
"unblock": "Unblock",
|
||||
"apply": "Apply",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel"
|
||||
"cancel": "Cancel",
|
||||
"hide": "Hide"
|
||||
}
|
||||
}
|
||||
@ -45,6 +45,10 @@
|
||||
"title": "Delete users",
|
||||
"message": "Are you sure you want to delete {0} users? This action cannot be undone"
|
||||
}
|
||||
},
|
||||
"new-credentials": {
|
||||
"title": "Login details",
|
||||
"message": "E-Mail address: {0}\nPassword: {1}\n\nRemember or write down this data, it will be impossible to find it out again"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,7 @@
|
||||
"unblock": "Разблокировать",
|
||||
"apply": "Применить",
|
||||
"confirm": "Подтвердить",
|
||||
"cancel": "Отменить"
|
||||
"cancel": "Отменить",
|
||||
"hide": "Скрыть"
|
||||
}
|
||||
}
|
||||
@ -45,6 +45,10 @@
|
||||
"title": "Удаление пользователей",
|
||||
"message": "Вы уверены, что хотите удалить {0} пользователей? Данное действие нельзя будет отменить"
|
||||
}
|
||||
},
|
||||
"new-credentials": {
|
||||
"title": "Данные для входа",
|
||||
"message": "Адрес эл. почты: {0}\nПароль: {1}\n\nЗапомните или запишите эти данные, повторно узнать их будет невозможно"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ import { useLocale, useTheme } from 'vuetify';
|
||||
import { useUsersStore } from '@/stores/users';
|
||||
import { validEmail, validName } from '@/mixins/validator';
|
||||
import { useGroupsStore } from '@/stores/groups';
|
||||
import { useInfoStore } from '@/stores/info';
|
||||
|
||||
defineProps<{
|
||||
modelValue: boolean;
|
||||
@ -10,6 +11,7 @@ defineProps<{
|
||||
|
||||
const $users = useUsersStore();
|
||||
const $groups = useGroupsStore();
|
||||
const $info = useInfoStore();
|
||||
|
||||
const { t } = useLocale();
|
||||
const theme = useTheme();
|
||||
@ -39,8 +41,17 @@ const validForm: ComputedRef<boolean> = computed(
|
||||
),
|
||||
);
|
||||
|
||||
const onConfirm = () => {
|
||||
$users.isEditing ? $users.editUser() : $users.createUser();
|
||||
const onConfirm = async () => {
|
||||
const res = await ($users.isEditing
|
||||
? $users.editUser()
|
||||
: $users.createUser());
|
||||
if (res) {
|
||||
console.log(res);
|
||||
$info.setInfoMessage(
|
||||
t('$vuetify.users.new-credentials.title'),
|
||||
t('$vuetify.users.new-credentials.message', res.email, res.password),
|
||||
);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ import { User } from '@/types/user';
|
||||
import Moment from '@/plugins/moment';
|
||||
import { isDeleted, isModifiedDate } from '@/mixins/item-validation';
|
||||
import { useConfirmationStore } from '@/stores/confirmation';
|
||||
import { useInfoStore } from '@/stores/info';
|
||||
|
||||
defineProps<{
|
||||
tableHeight: number;
|
||||
@ -17,6 +18,7 @@ const { t } = useLocale();
|
||||
|
||||
const $users = useUsersStore();
|
||||
const $confirmation = useConfirmationStore();
|
||||
const $info = useInfoStore();
|
||||
|
||||
const headers: ComputedRef<any[]> = computed(() => [
|
||||
{ title: t('$vuetify.users.heads.id'), key: 'ID' },
|
||||
@ -46,16 +48,19 @@ const actions = computed(() => [
|
||||
title: t('$vuetify.actions.block'),
|
||||
value: UserActions.BLOCK,
|
||||
blocked: false,
|
||||
onClick: (u: User) => $users.blockMany([u.ID]),
|
||||
},
|
||||
{
|
||||
title: t('$vuetify.actions.reset-password'),
|
||||
value: UserActions.RESET_PASSWORD,
|
||||
blocked: false,
|
||||
onClick: (u: User) => resetPassword(u),
|
||||
},
|
||||
{
|
||||
title: t('$vuetify.actions.unblock'),
|
||||
value: UserActions.UNBLOCK,
|
||||
blocked: true,
|
||||
onClick: (u: User) => $users.unblockMany([u.ID]),
|
||||
},
|
||||
]);
|
||||
// Actions
|
||||
@ -71,6 +76,16 @@ const deleteUser = (u: User) => {
|
||||
);
|
||||
$confirmation.setOnConfirm(() => $users.deleteUser(u.ID));
|
||||
};
|
||||
const resetPassword = async (u: User) => {
|
||||
const res = await $users.resetPassword(u.ID);
|
||||
if (res) {
|
||||
console.log(res);
|
||||
$info.setInfoMessage(
|
||||
t('$vuetify.users.new-credentials.title'),
|
||||
t('$vuetify.users.new-credentials.message', res.email, res.password),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Status
|
||||
const calculateUserStatus = (user: User) => {
|
||||
|
||||
@ -21,6 +21,10 @@ const deleteGroup = async (ID: number) => {
|
||||
|
||||
setLoadingActions([0]);
|
||||
};
|
||||
|
||||
//
|
||||
// MARK: Delete many groups
|
||||
//
|
||||
const deleteMany = async (IDs: number[]) => {
|
||||
if (!IDs.length) {
|
||||
return;
|
||||
@ -37,6 +41,10 @@ const deleteMany = async (IDs: number[]) => {
|
||||
|
||||
setLoadingActions([0]);
|
||||
};
|
||||
|
||||
//
|
||||
// MARK: Misc
|
||||
//
|
||||
const setLoadingActions = (IDs: number[]) => (loadingActionsID.value = IDs);
|
||||
|
||||
export { loadingActionsID, deleteGroup, deleteMany };
|
||||
|
||||
29
src/stores/info.ts
Normal file
29
src/stores/info.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
export const useInfoStore = defineStore('info', () => {
|
||||
const show: Ref<boolean> = ref(false);
|
||||
const title: Ref<string> = ref('');
|
||||
const message: Ref<string> = ref('');
|
||||
|
||||
const setInfoMessage = (t: string, m: string) => {
|
||||
title.value = t;
|
||||
message.value = m;
|
||||
show.value = true;
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
show.value = false;
|
||||
setTimeout(() => {
|
||||
title.value = '';
|
||||
message.value = '';
|
||||
}, 250);
|
||||
};
|
||||
|
||||
return {
|
||||
show,
|
||||
title,
|
||||
message,
|
||||
setInfoMessage,
|
||||
close,
|
||||
};
|
||||
});
|
||||
@ -1,5 +1,6 @@
|
||||
import api from '@/api';
|
||||
import { getAll } from './search';
|
||||
import { NewCredentials } from '@/types/new-credentials';
|
||||
|
||||
//
|
||||
// MARK: Delete user
|
||||
@ -21,6 +22,10 @@ const deleteUser = async (ID: number) => {
|
||||
|
||||
setLoadingActions([0]);
|
||||
};
|
||||
|
||||
//
|
||||
// MARK: Delete many users
|
||||
//
|
||||
const deleteMany = async (IDs: number[]) => {
|
||||
if (!IDs.length) {
|
||||
return;
|
||||
@ -37,6 +42,10 @@ const deleteMany = async (IDs: number[]) => {
|
||||
|
||||
setLoadingActions([0]);
|
||||
};
|
||||
|
||||
//
|
||||
// MARK: Block many users
|
||||
//
|
||||
const blockMany = async (IDs: number[]) => {
|
||||
if (!IDs.length) {
|
||||
return;
|
||||
@ -53,6 +62,10 @@ const blockMany = async (IDs: number[]) => {
|
||||
|
||||
setLoadingActions([0]);
|
||||
};
|
||||
|
||||
//
|
||||
// MARK: Unblock many users
|
||||
//
|
||||
const unblockMany = async (IDs: number[]) => {
|
||||
if (!IDs.length) {
|
||||
return;
|
||||
@ -69,6 +82,41 @@ const unblockMany = async (IDs: number[]) => {
|
||||
|
||||
setLoadingActions([0]);
|
||||
};
|
||||
|
||||
//
|
||||
// MARK: Reset user password
|
||||
//
|
||||
const resetPassword = async (
|
||||
ID: number,
|
||||
): Promise<NewCredentials | undefined> => {
|
||||
if (!ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoadingActions([ID]);
|
||||
|
||||
const [res, e] = await api.users.resetPassword(ID);
|
||||
if (e && typeof e === 'string') {
|
||||
// TODO notify error
|
||||
} else {
|
||||
getAll();
|
||||
}
|
||||
|
||||
setLoadingActions([0]);
|
||||
|
||||
return res?.data;
|
||||
};
|
||||
|
||||
//
|
||||
// MARK: Misc
|
||||
//
|
||||
const setLoadingActions = (IDs: number[]) => (loadingActionsID.value = IDs);
|
||||
|
||||
export { loadingActionsID, deleteUser, deleteMany, blockMany, unblockMany };
|
||||
export {
|
||||
loadingActionsID,
|
||||
deleteUser,
|
||||
deleteMany,
|
||||
blockMany,
|
||||
unblockMany,
|
||||
resetPassword,
|
||||
};
|
||||
|
||||
@ -2,6 +2,7 @@ import api from '@/api';
|
||||
import { validEmail, validName } from '@/mixins/validator';
|
||||
import { UserForm, User } from '@/types/user';
|
||||
import { getAll } from './search';
|
||||
import { NewCredentials } from '@/types/new-credentials';
|
||||
|
||||
//
|
||||
// MARK: User creation
|
||||
@ -15,7 +16,7 @@ const form: Ref<Partial<UserForm>> = ref({
|
||||
realName: '',
|
||||
groupID: -1,
|
||||
});
|
||||
const createUser = async () => {
|
||||
const createUser = async (): Promise<NewCredentials | undefined> => {
|
||||
if (
|
||||
loadingCreate.value ||
|
||||
!form.value.email ||
|
||||
@ -30,7 +31,7 @@ const createUser = async () => {
|
||||
resetErrorCreate();
|
||||
setLoadingCreate(true);
|
||||
|
||||
const [_, e] = await api.users.create(
|
||||
const [res, e] = await api.users.create(
|
||||
form.value.email,
|
||||
form.value.realName,
|
||||
form.value.groupID,
|
||||
@ -43,6 +44,8 @@ const createUser = async () => {
|
||||
}
|
||||
|
||||
setLoadingCreate(false);
|
||||
|
||||
return res?.data;
|
||||
};
|
||||
const resetErrorCreate = () => (errorCreate.value = '');
|
||||
const openModal = () => {
|
||||
|
||||
4
src/types/new-credentials.d.ts
vendored
Normal file
4
src/types/new-credentials.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
export interface NewCredentials {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user