diff --git a/src/api/groups.ts b/src/api/groups.ts
index 8ec82d2..a122f39 100644
--- a/src/api/groups.ts
+++ b/src/api/groups.ts
@@ -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];
+ }
+ },
};
diff --git a/src/api/users.ts b/src/api/users.ts
index ca127d8..b5b4e88 100644
--- a/src/api/users.ts
+++ b/src/api/users.ts
@@ -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];
+ }
+ },
};
diff --git a/src/components.d.ts b/src/components.d.ts
index dd81cd0..62bd02a 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -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']
diff --git a/src/components/InfoModal.vue b/src/components/InfoModal.vue
new file mode 100644
index 0000000..0935692
--- /dev/null
+++ b/src/components/InfoModal.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+ warning
+ {{ $confirmation.title }}
+
+
+
+ {{ $confirmation.message }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/locales/en/actions.json b/src/locales/en/actions.json
index 0288fde..e61d40d 100644
--- a/src/locales/en/actions.json
+++ b/src/locales/en/actions.json
@@ -4,6 +4,7 @@
"create-group": "Create group",
"create-user": "Create user",
"reset-password": "Reset password",
+ "delete": "Delete",
"edit": "Edit",
"block": "Block",
"unblock": "Unblock",
diff --git a/src/locales/en/groups.json b/src/locales/en/groups.json
index 93d46f5..36d696e 100644
--- a/src/locales/en/groups.json
+++ b/src/locales/en/groups.json
@@ -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"
}
}
}
diff --git a/src/locales/en/users.json b/src/locales/en/users.json
index 4053cdb..25b6640 100644
--- a/src/locales/en/users.json
+++ b/src/locales/en/users.json
@@ -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"
}
}
}
diff --git a/src/locales/ru/actions.json b/src/locales/ru/actions.json
index 26df083..8438ef6 100644
--- a/src/locales/ru/actions.json
+++ b/src/locales/ru/actions.json
@@ -4,6 +4,7 @@
"create-group": "Создать группу",
"create-user": "Создать пользователя",
"reset-password": "Сбросить пароль",
+ "delete": "Удалить",
"edit": "Редактировать",
"block": "Заблокировать",
"unblock": "Разблокировать",
diff --git a/src/locales/ru/groups.json b/src/locales/ru/groups.json
index 4ae0092..f6fa6b9 100644
--- a/src/locales/ru/groups.json
+++ b/src/locales/ru/groups.json
@@ -26,6 +26,10 @@
"delete-one": {
"title": "Удаление группы",
"message": "Вы уверены, что хотите удалить группу с ID {0}? Данное действие нельзя будет отменить"
+ },
+ "delete-many": {
+ "title": "Удаление групп",
+ "message": "Вы уверены, что хотите удалить {0} групп? Данное действие нельзя будет отменить"
}
}
}
diff --git a/src/locales/ru/users.json b/src/locales/ru/users.json
index 284546f..dbb432d 100644
--- a/src/locales/ru/users.json
+++ b/src/locales/ru/users.json
@@ -40,6 +40,10 @@
"delete-one": {
"title": "Удаление пользователя",
"message": "Вы уверены, что хотите удалить пользователя с ID {0}? Данное действие нельзя будет отменить"
+ },
+ "delete-many": {
+ "title": "Удаление пользователей",
+ "message": "Вы уверены, что хотите удалить {0} пользователей? Данное действие нельзя будет отменить"
}
}
}
diff --git a/src/pages/users/components/GroupsTab.vue b/src/pages/users/components/GroupsTab.vue
index 1f3dae7..ce9c3ca 100644
--- a/src/pages/users/components/GroupsTab.vue
+++ b/src/pages/users/components/GroupsTab.vue
@@ -151,6 +151,7 @@ watch(
density="comfortable"
class="mr-1"
:disabled="isDeleted(item.deletedAt)"
+ :loading="$groups.loadingActionsID.includes(item.ID)"
@click="editGroup(item)"
>
edit
@@ -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)"
>
close
diff --git a/src/pages/users/components/UsersTab.vue b/src/pages/users/components/UsersTab.vue
index dadfba6..23d503c 100644
--- a/src/pages/users/components/UsersTab.vue
+++ b/src/pages/users/components/UsersTab.vue
@@ -204,6 +204,7 @@ watch(
density="comfortable"
class="mr-1"
:disabled="isDeleted(item.deletedAt)"
+ :loading="$users.loadingActionsID.includes(item.ID)"
>
more_vert
@@ -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)"
>
close
diff --git a/src/pages/users/index.vue b/src/pages/users/index.vue
index c446745..69d77a1 100644
--- a/src/pages/users/index.vue
+++ b/src/pages/users/index.vue
@@ -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 = ref(500);
setInterval(() => {
@@ -28,7 +30,7 @@ setInterval(() => {
}, 100);
//
-// MARK: Tabs
+// Tabs
//
const tab: Ref = 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 = computed({
get() {
@@ -107,14 +109,14 @@ const $search: WritableComputedRef = computed({
},
});
//
-// MARK: Error
+// Error
//
const $error: ComputedRef = 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();
+};
@@ -164,6 +203,41 @@ const resetActions = () => {
: $groups.selected?.length,
])
}}
+
+
+
+ {{
+ tab === UsersPageTabs.USERS ? 'person_remove' : 'group_remove'
+ }}
+ {{ t('$vuetify.actions.delete') }}
+
+
+
+
+ person_cancel
+ {{ t('$vuetify.actions.block') }}
+
+
+
+ person_check
+ {{ t('$vuetify.actions.unblock') }}
+
+
diff --git a/src/stores/groups/actions.ts b/src/stores/groups/actions.ts
index 0d2037f..c989c84 100644
--- a/src/stores/groups/actions.ts
+++ b/src/stores/groups/actions.ts
@@ -4,13 +4,13 @@ import { getAll } from './search';
//
// MARK: Delete group
//
-const loadingDeleteID: Ref = ref(0);
+const loadingActionsID: Ref = 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 };
diff --git a/src/stores/users/actions.ts b/src/stores/users/actions.ts
index 7e45081..31790dd 100644
--- a/src/stores/users/actions.ts
+++ b/src/stores/users/actions.ts
@@ -4,13 +4,13 @@ import { getAll } from './search';
//
// MARK: Delete user
//
-const loadingDeleteID: Ref = ref(0);
+const loadingActionsID: Ref = 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 };
diff --git a/src/stores/users/modal.ts b/src/stores/users/modal.ts
index 84e946f..a741f83 100644
--- a/src/stores/users/modal.ts
+++ b/src/stores/users/modal.ts
@@ -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);