completed selected many actions

This commit is contained in:
Vitaliy Pavlov 2024-07-29 07:12:30 +07:00
parent e2bb2122dd
commit 3fcd1a5a38
16 changed files with 265 additions and 20 deletions

View File

@ -69,4 +69,16 @@ export default {
return [undefined, e?.response?.data?.error ?? e.message];
}
},
deleteMany: async (IDs: number[]): Promise<[AxiosResponse?, string?]> => {
try {
const data = await $axios.delete(`v1/groups`, {
data: {
array: IDs,
},
});
return [data, undefined];
} catch (e: any) {
return [undefined, e?.response?.data?.error ?? e.message];
}
},
};

View File

@ -71,4 +71,36 @@ export default {
return [undefined, e?.response?.data?.error ?? e.message];
}
},
deleteMany: async (IDs: number[]): Promise<[AxiosResponse?, string?]> => {
try {
const data = await $axios.delete(`v1/users`, {
data: {
array: IDs,
},
});
return [data, undefined];
} catch (e: any) {
return [undefined, e?.response?.data?.error ?? e.message];
}
},
blockMany: async (IDs: number[]): Promise<[AxiosResponse?, string?]> => {
try {
const data = await $axios.patch(`v1/users/block`, {
array: IDs,
});
return [data, undefined];
} catch (e: any) {
return [undefined, e?.response?.data?.error ?? e.message];
}
},
unblockMany: async (IDs: number[]): Promise<[AxiosResponse?, string?]> => {
try {
const data = await $axios.patch(`v1/users/unblock`, {
array: IDs,
});
return [data, undefined];
} catch (e: any) {
return [undefined, e?.response?.data?.error ?? e.message];
}
},
};

2
src/components.d.ts vendored
View File

@ -9,8 +9,10 @@ declare module 'vue' {
export interface GlobalComponents {
AppFooter: typeof import('./components/AppFooter.vue')['default']
ConfirmModal: typeof import('./components/ConfirmModal.vue')['default']
copy: typeof import('./components/ConfirmModal copy.vue')['default']
ErrorPlate: typeof import('./components/ErrorPlate.vue')['default']
HelloWorld: typeof import('./components/HelloWorld.vue')['default']
InfoModal: typeof import('./components/InfoModal.vue')['default']
LanguageSwitcher: typeof import('./components/LanguageSwitcher.vue')['default']
LogoComponent: typeof import('./components/Icons/LogoComponent.vue')['default']
NavigationDrawer: typeof import('./components/NavigationDrawer.vue')['default']

View File

@ -0,0 +1,42 @@
<script setup lang="ts">
import { useConfirmationStore } from '@/stores/confirmation';
import { useLocale } from 'vuetify';
const { t } = useLocale();
const $confirmation = useConfirmationStore();
</script>
<template>
<v-dialog persistent :model-value="$confirmation.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>
</v-card-title>
</template>
<v-card-text>{{ $confirmation.message }}</v-card-text>
<!-- Actions -->
<v-card-actions class="flex justify-end p-5 gap-1">
<!-- Cancel -->
<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()"
></v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>

View File

@ -4,6 +4,7 @@
"create-group": "Create group",
"create-user": "Create user",
"reset-password": "Reset password",
"delete": "Delete",
"edit": "Edit",
"block": "Block",
"unblock": "Unblock",

View File

@ -26,6 +26,10 @@
"delete-one": {
"title": "Delete group",
"message": "Are you sure you want to delete group with ID {0}? This action cannot be undone"
},
"delete-many": {
"title": "Delete groups",
"message": "Are you sure you want to delete {0} groups? This action cannot be undone"
}
}
}

View File

@ -40,6 +40,10 @@
"delete-one": {
"title": "Delete user",
"message": "Are you sure you want to delete user with ID {0}? This action cannot be undone"
},
"delete-many": {
"title": "Delete users",
"message": "Are you sure you want to delete {0} users? This action cannot be undone"
}
}
}

View File

@ -4,6 +4,7 @@
"create-group": "Создать группу",
"create-user": "Создать пользователя",
"reset-password": "Сбросить пароль",
"delete": "Удалить",
"edit": "Редактировать",
"block": "Заблокировать",
"unblock": "Разблокировать",

View File

@ -26,6 +26,10 @@
"delete-one": {
"title": "Удаление группы",
"message": "Вы уверены, что хотите удалить группу с ID {0}? Данное действие нельзя будет отменить"
},
"delete-many": {
"title": "Удаление групп",
"message": "Вы уверены, что хотите удалить {0} групп? Данное действие нельзя будет отменить"
}
}
}

View File

@ -40,6 +40,10 @@
"delete-one": {
"title": "Удаление пользователя",
"message": "Вы уверены, что хотите удалить пользователя с ID {0}? Данное действие нельзя будет отменить"
},
"delete-many": {
"title": "Удаление пользователей",
"message": "Вы уверены, что хотите удалить {0} пользователей? Данное действие нельзя будет отменить"
}
}
}

View File

@ -151,6 +151,7 @@ watch(
density="comfortable"
class="mr-1"
:disabled="isDeleted(item.deletedAt)"
:loading="$groups.loadingActionsID.includes(item.ID)"
@click="editGroup(item)"
>
<span class="mso text-xl">edit</span>
@ -162,7 +163,7 @@ watch(
variant="text"
density="comfortable"
:disabled="isDeleted(item.deletedAt)"
:loading="$groups.loadingDeleteID === item.ID"
:loading="$groups.loadingActionsID.includes(item.ID)"
@click="deleteGroup(item)"
>
<span class="mso text-xl">close</span>

View File

@ -204,6 +204,7 @@ watch(
density="comfortable"
class="mr-1"
:disabled="isDeleted(item.deletedAt)"
:loading="$users.loadingActionsID.includes(item.ID)"
>
<span class="mso text-xl">more_vert</span>
</v-btn>
@ -233,7 +234,7 @@ watch(
variant="text"
density="comfortable"
:disabled="isDeleted(item.deletedAt)"
:loading="$users.loadingDeleteID === item.ID"
:loading="$users.loadingActionsID.includes(item.ID)"
@click="deleteUser(item)"
>
<span class="mso text-xl">close</span>

View File

@ -3,6 +3,7 @@ import { useLocale } from 'vuetify';
import { UsersPageTabs } from '@/enums/user-tab.enum';
import { useUsersStore } from '@/stores/users';
import { useGroupsStore } from '@/stores/groups';
import { useConfirmationStore } from '@/stores/confirmation';
import ErrorPlate from '@/components/ErrorPlate.vue';
import AddUser from './components/AddUser.vue';
import AddGroup from './components/AddGroup.vue';
@ -16,6 +17,7 @@ const { t } = useLocale();
const $users = useUsersStore();
const $groups = useGroupsStore();
const $confirmation = useConfirmationStore();
const tableHeight: Ref<number> = ref(500);
setInterval(() => {
@ -28,7 +30,7 @@ setInterval(() => {
}, 100);
//
// MARK: Tabs
// Tabs
//
const tab: Ref<UsersPageTabs> = ref(UsersPageTabs.USERS);
const parseURLSearch = () => {
@ -87,14 +89,14 @@ watch(tab, () => {
});
//
// MARK: Creation
// Creation
//
const openCreationModal = () => {
tab.value === UsersPageTabs.USERS ? $users.openModal() : $groups.openModal();
};
//
// MARK: Search model
// Search model
//
const $search: WritableComputedRef<string> = computed({
get() {
@ -107,14 +109,14 @@ const $search: WritableComputedRef<string> = computed({
},
});
//
// MARK: Error
// Error
//
const $error: ComputedRef<string> = computed(() =>
tab.value === UsersPageTabs.USERS ? $users.error : $groups.error,
);
//
// MARK: Common functions
// Common functions
//
const getAll = () => {
tab.value === UsersPageTabs.USERS ? $users.getAll() : $groups.getAll();
@ -129,6 +131,43 @@ const resetActions = () => {
$users.resetSelected();
$groups.resetSelected();
};
const deleteMany = () => {
const len =
tab.value === UsersPageTabs.USERS
? $users.selected.length
: $groups.selected.length;
$confirmation.openModal();
$confirmation.setTitle(
t(
`$vuetify.${tab.value === UsersPageTabs.USERS ? 'users' : 'groups'}.delete.delete-many.title`,
),
);
$confirmation.setMessage(
t(
`$vuetify.${tab.value === UsersPageTabs.USERS ? 'users' : 'groups'}.delete.delete-many.message`,
[len],
),
);
$confirmation.setOnConfirm(
tab.value === UsersPageTabs.USERS
? () => {
$users.deleteMany($users.selected);
$users.resetSelected();
}
: () => {
$groups.deleteMany($groups.selected);
$groups.resetSelected();
},
);
};
const blockMany = () => {
$users.blockMany($users.selected);
$users.resetSelected();
};
const unblockMany = () => {
$users.unblockMany($users.selected);
$users.resetSelected();
};
</script>
<template>
@ -164,6 +203,41 @@ const resetActions = () => {
: $groups.selected?.length,
])
}}</span>
<!-- Actions -->
<!-- Delete -->
<v-btn
color="primary"
variant="text"
class="ml-10 mr-3 px-3"
@click="deleteMany()"
>
<span class="mso mr-1 text-xl">{{
tab === UsersPageTabs.USERS ? 'person_remove' : 'group_remove'
}}</span>
<span>{{ t('$vuetify.actions.delete') }}</span>
</v-btn>
<template v-if="tab === UsersPageTabs.USERS">
<!-- Block -->
<v-btn
color="primary"
variant="text"
class="mr-3 px-3"
@click="blockMany()"
>
<span class="mso mr-1 text-xl">person_cancel</span>
<span>{{ t('$vuetify.actions.block') }}</span>
</v-btn>
<!-- Unblock -->
<v-btn
color="primary"
variant="text"
class="mr-3 px-3"
@click="unblockMany()"
>
<span class="mso mr-1 text-xl">person_check</span>
<span>{{ t('$vuetify.actions.unblock') }}</span>
</v-btn>
</template>
</div>
<!-- Title -->

View File

@ -4,13 +4,13 @@ import { getAll } from './search';
//
// MARK: Delete group
//
const loadingDeleteID: Ref<number> = ref(0);
const loadingActionsID: Ref<number[]> = ref([0]);
const deleteGroup = async (ID: number) => {
if (loadingDeleteID.value || !ID) {
if (!ID) {
return;
}
setLoadingDelete(ID);
setLoadingActions([ID]);
const [_, e] = await api.groups.delete(ID);
if (e && typeof e === 'string') {
@ -19,8 +19,24 @@ const deleteGroup = async (ID: number) => {
getAll();
}
setLoadingDelete(0);
setLoadingActions([0]);
};
const setLoadingDelete = (ID: number) => (loadingDeleteID.value = ID);
const deleteMany = async (IDs: number[]) => {
if (!IDs.length) {
return;
}
export { loadingDeleteID, deleteGroup };
setLoadingActions(IDs);
const [_, e] = await api.groups.deleteMany(IDs);
if (e && typeof e === 'string') {
// TODO notify error
} else {
getAll();
}
setLoadingActions([0]);
};
const setLoadingActions = (IDs: number[]) => (loadingActionsID.value = IDs);
export { loadingActionsID, deleteGroup, deleteMany };

View File

@ -4,13 +4,13 @@ import { getAll } from './search';
//
// MARK: Delete user
//
const loadingDeleteID: Ref<number> = ref(0);
const loadingActionsID: Ref<number[]> = ref([0]);
const deleteUser = async (ID: number) => {
if (loadingDeleteID.value || !ID) {
if (!ID) {
return;
}
setLoadingDelete(ID);
setLoadingActions([ID]);
const [_, e] = await api.users.delete(ID);
if (e && typeof e === 'string') {
@ -19,8 +19,56 @@ const deleteUser = async (ID: number) => {
getAll();
}
setLoadingDelete(0);
setLoadingActions([0]);
};
const setLoadingDelete = (ID: number) => (loadingDeleteID.value = ID);
const deleteMany = async (IDs: number[]) => {
if (!IDs.length) {
return;
}
export { loadingDeleteID, deleteUser };
setLoadingActions(IDs);
const [_, e] = await api.users.deleteMany(IDs);
if (e && typeof e === 'string') {
// TODO notify error
} else {
getAll();
}
setLoadingActions([0]);
};
const blockMany = async (IDs: number[]) => {
if (!IDs.length) {
return;
}
setLoadingActions(IDs);
const [_, e] = await api.users.blockMany(IDs);
if (e && typeof e === 'string') {
// TODO notify error
} else {
getAll();
}
setLoadingActions([0]);
};
const unblockMany = async (IDs: number[]) => {
if (!IDs.length) {
return;
}
setLoadingActions(IDs);
const [_, e] = await api.users.unblockMany(IDs);
if (e && typeof e === 'string') {
// TODO notify error
} else {
getAll();
}
setLoadingActions([0]);
};
const setLoadingActions = (IDs: number[]) => (loadingActionsID.value = IDs);
export { loadingActionsID, deleteUser, deleteMany, blockMany, unblockMany };

View File

@ -103,7 +103,6 @@ const startEditUser = async (u: User) => {
realName: u.realName,
groupID: u.groupID,
};
console.log(form.value, u);
};
const setIsEditing = (state: boolean) => (isEditing.value = state);