diff --git a/dashboard/src/components/GroupDialog.vue b/dashboard/src/components/GroupDialog.vue
new file mode 100644
index 000000000..cb08d6d2d
--- /dev/null
+++ b/dashboard/src/components/GroupDialog.vue
@@ -0,0 +1,109 @@
+
+
+
+
+
diff --git a/dashboard/src/models/GroupsModel.js b/dashboard/src/models/GroupsModel.js
index c59c8e452..2e3afa745 100644
--- a/dashboard/src/models/GroupsModel.js
+++ b/dashboard/src/models/GroupsModel.js
@@ -7,16 +7,89 @@ function create() {
return {
async list() {
- let error, result;
+ let result;
try {
result = await fetcher.get(`${origin}/api/v1/groups`, { access_token: accessToken });
} catch (e) {
- error = e;
+ return [e];
}
- if (error || result.status !== 200) return [error || result];
+ if (result.status !== 200) return [result];
return [null, result.body.groups];
},
+ async add(name, userIds, appIds) {
+ let result;
+ try {
+ result = await fetcher.post(`${origin}/api/v1/groups`, { name }, { access_token: accessToken });
+ } catch (e) {
+ return [e];
+ }
+
+ if (result.status !== 201) return [result];
+
+ if (userIds.length) {
+ let resultUserIds;
+ try {
+ resultUserIds = await fetcher.put(`${origin}/api/v1/groups/${result.body.id}/members`, { userIds }, { access_token: accessToken });
+ } catch (e) {
+ return [e];
+ }
+
+ if (resultUserIds.status !== 200) return [resultUserIds];
+ }
+
+ if (appIds.length) {
+ let resultAppIds;
+ try {
+ resultAppIds = await fetcher.put(`${origin}/api/v1/groups/${result.body.id}/apps`, { appIds }, { access_token: accessToken });
+ } catch (e) {
+ return [e];
+ }
+
+ if (resultAppIds.status !== 200) return [resultAppIds];
+ }
+
+ return [null, result.body];
+ },
+ async update(id, name, userIds, appIds) {
+ let result;
+ try {
+ result = await fetcher.put(`${origin}/api/v1/groups/${id}/name`, { name }, { access_token: accessToken });
+ } catch (e) {
+ return [e];
+ }
+
+ if (result.status !== 200) return [result];
+
+ try {
+ result = await fetcher.put(`${origin}/api/v1/groups/${id}/members`, { userIds }, { access_token: accessToken });
+ } catch (e) {
+ return [e];
+ }
+
+ if (result.status !== 200) return [result];
+
+ try {
+ result = await fetcher.put(`${origin}/api/v1/groups/${id}/apps`, { appIds }, { access_token: accessToken });
+ } catch (e) {
+ return [e];
+ }
+
+ if (result.status !== 200) return [result];
+
+ return [null];
+ },
+ async remove(id) {
+ let result;
+ try {
+ result = await fetcher.del(`${origin}/api/v1/groups/${id}`, { access_token: accessToken });
+ } catch (e) {
+ return [e];
+ }
+
+ if (result.status !== 204) return [result];
+ return [null];
+ },
};
}
diff --git a/dashboard/src/views/UsersView.vue b/dashboard/src/views/UsersView.vue
index 9828cfca1..7d042a608 100644
--- a/dashboard/src/views/UsersView.vue
+++ b/dashboard/src/views/UsersView.vue
@@ -4,10 +4,11 @@ import { useI18n } from 'vue-i18n';
const i18n = useI18n();
const t = i18n.t;
-import { ref, onMounted, computed } from 'vue';
-import { Button, ButtonGroup, TextInput, Dropdown, TableView } from 'pankow';
+import { ref, onMounted, computed, useTemplateRef } from 'vue';
+import { Button, ButtonGroup, TextInput, Dropdown, TableView, InputDialog } from 'pankow';
import { ROLES } from '../constants.js';
import Section from '../components/Section.vue';
+import GroupDialog from '../components/GroupDialog.vue';
import UsersModel from '../models/UsersModel.js';
import GroupsModel from '../models/GroupsModel.js';
import ProfileModel from '../models/ProfileModel.js';
@@ -45,6 +46,9 @@ const groups = ref([]);
const groupsById = ref({});
const roles = ref([]);
+const inputDialog = useTemplateRef('inputDialog');
+const groupDialog = useTemplateRef('groupDialog');
+
const filteredUsers = computed(() => {
return users.value.filter(u => {
return u.username.indexOf(search.value) !== -1 || u.email.indexOf(search.value) !== -1 || u.displayName.indexOf(search.value) !== -1;
@@ -109,6 +113,27 @@ function groupMembers(group) {
return group.userIds.filter(function (uid) { return !!usersById.value[uid]; }).map(function (uid) { return usersById.value[uid].username || usersById.value[uid].email; }).join(' ');
}
+function onEditOrAddGroup(group = null) {
+ groupDialog.value.open(group);
+}
+
+async function onRemoveGroup(group) {
+ const yes = await inputDialog.value.confirm({
+ title: t('users.deleteGroupDialog.title', { name: group.name }),
+ message: t('users.deleteGroupDialog.description', { memberCount: group.memberCount }),
+ confirmStyle: 'danger',
+ confirmLabel: t('users.deleteGroupDialog.deleteAction'),
+ rejectLabel: t('main.dialog.cancel')
+ });
+
+ if (!yes) return;
+
+ const [error] = await groupsModel.remove(group.id);
+ if (error) console.error(error);
+
+ await refreshGroups();
+}
+
onMounted(async () => {
const [error, result] = await profileModel.get();
if (error) return console.error(error);
@@ -133,6 +158,9 @@ onMounted(async () => {
+