completed user and group delete features
This commit is contained in:
parent
978a08af00
commit
e2bb2122dd
@ -2,10 +2,12 @@
|
||||
<v-app>
|
||||
<v-main>
|
||||
<router-view />
|
||||
|
||||
<confirm-modal />
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
//
|
||||
//
|
||||
</script>
|
||||
|
||||
@ -61,4 +61,12 @@ export default {
|
||||
return [undefined, e?.response?.data?.error ?? e.message];
|
||||
}
|
||||
},
|
||||
delete: async (ID: number): Promise<[AxiosResponse?, string?]> => {
|
||||
try {
|
||||
const data = await $axios.delete(`v1/groups/${ID}`);
|
||||
return [data, undefined];
|
||||
} catch (e: any) {
|
||||
return [undefined, e?.response?.data?.error ?? e.message];
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@ -63,4 +63,12 @@ export default {
|
||||
return [undefined, e?.response?.data?.error ?? e.message];
|
||||
}
|
||||
},
|
||||
delete: async (ID: number): Promise<[AxiosResponse?, string?]> => {
|
||||
try {
|
||||
const data = await $axios.delete(`v1/users/${ID}`);
|
||||
return [data, undefined];
|
||||
} catch (e: any) {
|
||||
return [undefined, e?.response?.data?.error ?? e.message];
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
1
src/components.d.ts
vendored
1
src/components.d.ts
vendored
@ -8,6 +8,7 @@ export {}
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AppFooter: typeof import('./components/AppFooter.vue')['default']
|
||||
ConfirmModal: typeof import('./components/ConfirmModal.vue')['default']
|
||||
ErrorPlate: typeof import('./components/ErrorPlate.vue')['default']
|
||||
HelloWorld: typeof import('./components/HelloWorld.vue')['default']
|
||||
LanguageSwitcher: typeof import('./components/LanguageSwitcher.vue')['default']
|
||||
|
||||
42
src/components/ConfirmModal.vue
Normal file
42
src/components/ConfirmModal.vue
Normal 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>
|
||||
@ -7,6 +7,7 @@
|
||||
"edit": "Edit",
|
||||
"block": "Block",
|
||||
"unblock": "Unblock",
|
||||
"apply": "Apply",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
|
||||
@ -21,6 +21,12 @@
|
||||
"name": "Name of group",
|
||||
"permissions": "Permissions of users in group"
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"delete-one": {
|
||||
"title": "Delete group",
|
||||
"message": "Are you sure you want to delete group with ID {0}? This action cannot be undone"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -35,6 +35,12 @@
|
||||
"group": "Group",
|
||||
"no-group": "Not in group"
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"delete-one": {
|
||||
"title": "Delete user",
|
||||
"message": "Are you sure you want to delete user with ID {0}? This action cannot be undone"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,8 @@
|
||||
"edit": "Редактировать",
|
||||
"block": "Заблокировать",
|
||||
"unblock": "Разблокировать",
|
||||
"confirm": "Применить",
|
||||
"apply": "Применить",
|
||||
"confirm": "Подтвердить",
|
||||
"cancel": "Отменить"
|
||||
}
|
||||
}
|
||||
@ -21,6 +21,12 @@
|
||||
"name": "Название группы",
|
||||
"permissions": "Права пользователей в группе"
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"delete-one": {
|
||||
"title": "Удаление группы",
|
||||
"message": "Вы уверены, что хотите удалить группу с ID {0}? Данное действие нельзя будет отменить"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -35,6 +35,12 @@
|
||||
"group": "Группа пользователя",
|
||||
"no-group": "Без группы"
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"delete-one": {
|
||||
"title": "Удаление пользователя",
|
||||
"message": "Вы уверены, что хотите удалить пользователя с ID {0}? Данное действие нельзя будет отменить"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -120,7 +120,7 @@ const onConfirm = () => {
|
||||
variant="flat"
|
||||
color="primary"
|
||||
class="px-4"
|
||||
:text="t('$vuetify.actions.confirm')"
|
||||
:text="t('$vuetify.actions.apply')"
|
||||
:loading="$groups.loadingCreate"
|
||||
:disabled="!validForm"
|
||||
:class="{
|
||||
|
||||
@ -125,7 +125,7 @@ const onConfirm = () => {
|
||||
variant="flat"
|
||||
color="primary"
|
||||
class="px-4"
|
||||
:text="t('$vuetify.actions.confirm')"
|
||||
:text="t('$vuetify.actions.apply')"
|
||||
:loading="$groups.loadingCreate"
|
||||
:disabled="!validForm"
|
||||
:class="{
|
||||
|
||||
@ -6,6 +6,7 @@ import { useGroupsStore } from '@/stores/groups';
|
||||
import { isDeleted, isModifiedDate } from '@/mixins/item-validation';
|
||||
import { Group } from '@/types/group';
|
||||
import Moment from '@/plugins/moment';
|
||||
import { useConfirmationStore } from '@/stores/confirmation';
|
||||
|
||||
defineProps<{
|
||||
tableHeight: number;
|
||||
@ -14,6 +15,7 @@ defineProps<{
|
||||
const { t } = useLocale();
|
||||
|
||||
const $groups = useGroupsStore();
|
||||
const $confirmation = useConfirmationStore();
|
||||
|
||||
const headers: ComputedRef<any[]> = computed(() => [
|
||||
{ title: t('$vuetify.groups.heads.id'), key: 'ID' },
|
||||
@ -35,6 +37,14 @@ const editGroup = (g: Group) => {
|
||||
$groups.openModal();
|
||||
$groups.startEditGroup(g);
|
||||
};
|
||||
const deleteGroup = (g: Group) => {
|
||||
$confirmation.openModal();
|
||||
$confirmation.setTitle(t('$vuetify.groups.delete.delete-one.title'));
|
||||
$confirmation.setMessage(
|
||||
t('$vuetify.groups.delete.delete-one.message', [g.ID]),
|
||||
);
|
||||
$confirmation.setOnConfirm(() => $groups.deleteGroup(g.ID));
|
||||
};
|
||||
|
||||
// Status
|
||||
const calculateStatus = (group: Group) => {
|
||||
@ -152,6 +162,8 @@ watch(
|
||||
variant="text"
|
||||
density="comfortable"
|
||||
:disabled="isDeleted(item.deletedAt)"
|
||||
:loading="$groups.loadingDeleteID === item.ID"
|
||||
@click="deleteGroup(item)"
|
||||
>
|
||||
<span class="mso text-xl">close</span>
|
||||
</v-btn>
|
||||
|
||||
@ -1,12 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
computed,
|
||||
ref,
|
||||
watch,
|
||||
onBeforeMount,
|
||||
type Ref,
|
||||
type ComputedRef,
|
||||
} from 'vue';
|
||||
import { computed, watch, onBeforeMount, type ComputedRef } from 'vue';
|
||||
import { useLocale } from 'vuetify';
|
||||
import { ITEMS_PER_PAGE } from '@/constants/static';
|
||||
import { useUsersStore } from '@/stores/users';
|
||||
@ -14,6 +7,7 @@ import { UserActions } from '@/enums/user-actions.enum';
|
||||
import { User } from '@/types/user';
|
||||
import Moment from '@/plugins/moment';
|
||||
import { isDeleted, isModifiedDate } from '@/mixins/item-validation';
|
||||
import { useConfirmationStore } from '@/stores/confirmation';
|
||||
|
||||
defineProps<{
|
||||
tableHeight: number;
|
||||
@ -22,6 +16,7 @@ defineProps<{
|
||||
const { t } = useLocale();
|
||||
|
||||
const $users = useUsersStore();
|
||||
const $confirmation = useConfirmationStore();
|
||||
|
||||
const headers: ComputedRef<any[]> = computed(() => [
|
||||
{ title: t('$vuetify.users.heads.id'), key: 'ID' },
|
||||
@ -68,6 +63,14 @@ const editUser = (u: User) => {
|
||||
$users.openModal();
|
||||
$users.startEditUser(u);
|
||||
};
|
||||
const deleteUser = (u: User) => {
|
||||
$confirmation.openModal();
|
||||
$confirmation.setTitle(t('$vuetify.users.delete.delete-one.title'));
|
||||
$confirmation.setMessage(
|
||||
t('$vuetify.users.delete.delete-one.message', [u.ID]),
|
||||
);
|
||||
$confirmation.setOnConfirm(() => $users.deleteUser(u.ID));
|
||||
};
|
||||
|
||||
// Status
|
||||
const calculateUserStatus = (user: User) => {
|
||||
@ -230,6 +233,8 @@ watch(
|
||||
variant="text"
|
||||
density="comfortable"
|
||||
:disabled="isDeleted(item.deletedAt)"
|
||||
:loading="$users.loadingDeleteID === item.ID"
|
||||
@click="deleteUser(item)"
|
||||
>
|
||||
<span class="mso text-xl">close</span>
|
||||
</v-btn>
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
// Utilities
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useAppStore = defineStore('app', {
|
||||
state: () => ({
|
||||
//
|
||||
}),
|
||||
})
|
||||
48
src/stores/confirmation.ts
Normal file
48
src/stores/confirmation.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
export const useConfirmationStore = defineStore('confirmation', () => {
|
||||
const show: Ref<boolean> = ref(false);
|
||||
const title: Ref<string> = ref('');
|
||||
const message: Ref<string> = ref('');
|
||||
|
||||
let onConfirm: Function = () => {};
|
||||
let onDecline: Function = () => {};
|
||||
const setOnConfirm = (f: Function) => (onConfirm = f);
|
||||
const setOnDecline = (f: Function) => (onDecline = f);
|
||||
const openModal = () => (show.value = true);
|
||||
const closeModal = () => (show.value = false);
|
||||
const setTitle = (t: string) => (title.value = t);
|
||||
const setMessage = (m: string) => (message.value = m);
|
||||
|
||||
const setDefaults = () => {
|
||||
show.value = false;
|
||||
setTimeout(() => {
|
||||
title.value = '';
|
||||
message.value = '';
|
||||
onConfirm = () => {};
|
||||
onDecline = () => {};
|
||||
}, 250);
|
||||
};
|
||||
const doConfirm = () => {
|
||||
onConfirm();
|
||||
setDefaults();
|
||||
};
|
||||
const doDecline = () => {
|
||||
onDecline();
|
||||
setDefaults();
|
||||
};
|
||||
|
||||
return {
|
||||
show,
|
||||
title,
|
||||
message,
|
||||
setOnConfirm,
|
||||
setOnDecline,
|
||||
openModal,
|
||||
closeModal,
|
||||
setTitle,
|
||||
setMessage,
|
||||
doConfirm,
|
||||
doDecline,
|
||||
};
|
||||
});
|
||||
26
src/stores/groups/actions.ts
Normal file
26
src/stores/groups/actions.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import api from '@/api';
|
||||
import { getAll } from './search';
|
||||
|
||||
//
|
||||
// MARK: Delete group
|
||||
//
|
||||
const loadingDeleteID: Ref<number> = ref(0);
|
||||
const deleteGroup = async (ID: number) => {
|
||||
if (loadingDeleteID.value || !ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoadingDelete(ID);
|
||||
|
||||
const [_, e] = await api.groups.delete(ID);
|
||||
if (e && typeof e === 'string') {
|
||||
// TODO notify error
|
||||
} else {
|
||||
getAll();
|
||||
}
|
||||
|
||||
setLoadingDelete(0);
|
||||
};
|
||||
const setLoadingDelete = (ID: number) => (loadingDeleteID.value = ID);
|
||||
|
||||
export { loadingDeleteID, deleteGroup };
|
||||
@ -1,13 +1,16 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import * as s from './search';
|
||||
import * as a from './actions';
|
||||
import * as m from './modal';
|
||||
import * as s from './search';
|
||||
|
||||
export const useGroupsStore = defineStore('groups', () => {
|
||||
const search = { ...s };
|
||||
const actions = { ...a };
|
||||
const modal = { ...m };
|
||||
const search = { ...s };
|
||||
|
||||
return {
|
||||
...search,
|
||||
...actions,
|
||||
...modal,
|
||||
...search,
|
||||
};
|
||||
});
|
||||
|
||||
26
src/stores/users/actions.ts
Normal file
26
src/stores/users/actions.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import api from '@/api';
|
||||
import { getAll } from './search';
|
||||
|
||||
//
|
||||
// MARK: Delete user
|
||||
//
|
||||
const loadingDeleteID: Ref<number> = ref(0);
|
||||
const deleteUser = async (ID: number) => {
|
||||
if (loadingDeleteID.value || !ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoadingDelete(ID);
|
||||
|
||||
const [_, e] = await api.users.delete(ID);
|
||||
if (e && typeof e === 'string') {
|
||||
// TODO notify error
|
||||
} else {
|
||||
getAll();
|
||||
}
|
||||
|
||||
setLoadingDelete(0);
|
||||
};
|
||||
const setLoadingDelete = (ID: number) => (loadingDeleteID.value = ID);
|
||||
|
||||
export { loadingDeleteID, deleteUser };
|
||||
@ -1,13 +1,16 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import * as s from './search';
|
||||
import * as a from './actions';
|
||||
import * as m from './modal';
|
||||
import * as s from './search';
|
||||
|
||||
export const useUsersStore = defineStore('users', () => {
|
||||
const search = { ...s };
|
||||
const actions = { ...a };
|
||||
const modal = { ...m };
|
||||
const search = { ...s };
|
||||
|
||||
return {
|
||||
...search,
|
||||
...actions,
|
||||
...modal,
|
||||
...search,
|
||||
};
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user