Skip to content

Commit cc73800

Browse files
committed
Role management on new Admin UI. In development.
1 parent f545f6d commit cc73800

File tree

4 files changed

+188
-73
lines changed

4 files changed

+188
-73
lines changed

src/AdminUI/src/components/AdminPage.vue

+9-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
<v-card class="fill-height d-flex flex-column">
44
<v-tabs v-model="activeTab" color="primary" align-tabs="center">
55
<v-tab :value="1">{{ $t('admin.tabs.userManagement') }}</v-tab>
6-
<v-tab :value="2">{{ $t('admin.tabs.systemSettings') }}</v-tab>
7-
<v-tab :value="3">{{ $t('admin.tabs.logs') }}</v-tab>
6+
<v-tab :value="2">{{ $t('admin.tabs.rolesManagement') }}</v-tab>
7+
<v-tab :value="3">{{ $t('admin.tabs.systemSettings') }}</v-tab>
8+
<v-tab :value="4">{{ $t('admin.tabs.logs') }}</v-tab>
89
</v-tabs>
910

1011
<v-card-text>
@@ -14,10 +15,14 @@
1415
</v-window-item>
1516

1617
<v-window-item :value="2">
17-
<system-settings-tab />
18+
<roles-management-tab />
1819
</v-window-item>
1920

2021
<v-window-item :value="3">
22+
<system-settings-tab />
23+
</v-window-item>
24+
25+
<v-window-item :value="4">
2126
<logs-tab />
2227
</v-window-item>
2328
</v-window>
@@ -29,6 +34,7 @@
2934
<script setup>
3035
import { ref } from 'vue';
3136
import UserManagementTab from './UserManagementTab.vue';
37+
import RolesManagementTab from './RolesManagementTab.vue';
3238
import SystemSettingsTab from './SystemSettingsTab.vue';
3339
import LogsTab from './LogsTab.vue';
3440

src/AdminUI/src/components/LoginPage.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,10 @@ function testLogin() {
174174
.then((data) => {
175175
if ('ok' in data && data.ok) {
176176
router.push('/dashboard')
177-
console.log('User is logged in')
177+
// console.log('User is logged in')
178178
} else {
179179
router.push('/login')
180-
console.log('User is not logged in')
180+
// console.log('User is not logged in')
181181
}
182182
})
183183
.catch((error) => {

src/AdminUI/src/components/UserManagementTab.vue

+130-67
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,108 @@
11
<template>
22
<v-container fluid class="user-management-tab">
3-
<h2 class="text-h5 mb-4">{{ $t('admin.userManagement.title') }}</h2>
3+
4+
<v-btn color="primary" class="mt-4" @click="openAddUserDialog">
5+
{{ $t('admin.userManagement.addUser') }}
6+
</v-btn>
47

58
<v-data-table :headers="headers" :items="users" :items-per-page="5" class="elevation-1" :load-children="fetchUsers"
69
:items-per-page-text="$t('common.itemsPerPageText')">
710
<template #[`item.actions`]="{ item }">
811
<v-icon size="small" class="me-2" @click="openEditUserDialog(item)">
912
mdi-pencil
1013
</v-icon>
11-
<v-icon v-if="item.username !== 'admin'" size="small" @click="deleteUser(item)">
14+
<v-icon v-if="item.username !== 'admin'" size="small" @click="openDeleteConfirmDialog(item)">
1215
mdi-delete
1316
</v-icon>
1417
</template>
1518
</v-data-table>
16-
17-
<v-btn color="primary" class="mt-4" @click="openAddUserDialog">
18-
{{ $t('admin.userManagement.addUser') }}
19-
</v-btn>
2019
<div>
2120
<v-chip v-if="error" color="red darken-1">{{ $t('common.error') }}</v-chip>
2221
</div>
22+
</v-container>
2323

24-
<v-dialog v-model="addUserDialog" max-width="500px">
25-
<v-card>
26-
<v-card-title>{{ $t('admin.userManagement.addNewUser') }}</v-card-title>
27-
<v-card-text>
28-
<v-text-field v-model="newUser.username" :label="$t('admin.userManagement.username')" required></v-text-field>
29-
<v-text-field v-model="newUser.email" :label="$t('admin.userManagement.email')" required
30-
type="email"></v-text-field>
31-
<v-text-field v-model="newUser.password" :label="$t('admin.userManagement.password')" required
32-
type="password"></v-text-field>
33-
<v-autocomplete v-model="newUser.roles" :items="roles" item-title="name" outlined chips closable-chips
34-
small-chips :label="$t('admin.userManagement.roles')" multiple></v-autocomplete>
35-
</v-card-text>
36-
<v-card-actions>
37-
<v-spacer></v-spacer>
38-
<v-btn color="blue darken-1" text @click="closeAddUserDialog">{{ $t('common.cancel') }}</v-btn>
39-
<v-btn color="blue darken-1" text @click="createUser">{{ $t('common.save') }}</v-btn>
40-
<v-chip v-if="error" color="red darken-1">{{ $t('common.error') }}</v-chip>
41-
</v-card-actions>
42-
</v-card>
43-
</v-dialog>
4424

45-
<v-dialog v-model="editUserDialog" max-width="500px">
46-
<v-card>
47-
<v-card-title>{{ $t('admin.userManagement.editUser') }}</v-card-title>
48-
<v-card-text>
49-
<v-text-field v-model="editedUser.username" :label="$t('admin.userManagement.username')"
50-
required></v-text-field>
51-
<v-text-field v-model="editedUser.email" :label="$t('admin.userManagement.email')" required
52-
type="email"></v-text-field>
53-
<v-text-field v-model="editedUser.password" :label="$t('admin.userManagement.password')" required
54-
type="password"></v-text-field>
55-
<v-autocomplete v-model="editedUser.roles" :items="roles" item-title="name" outlined chips closable-chips
56-
small-chips :label="$t('admin.userManagement.roles')" multiple></v-autocomplete>
57-
</v-card-text>
58-
<v-card-actions>
59-
<v-spacer> </v-spacer>
60-
<v-btn color="blue darken-1" text @click="closeEditUserDialog">{{ $t('common.cancel') }}</v-btn>
61-
<v-btn color="blue darken-1" text @click="updateUser(editedUser)">{{ $t('common.save') }}</v-btn>
62-
<v-chip v-if="error" color="red darken-1">{{ $t('common.error') }}</v-chip>
63-
</v-card-actions>
64-
</v-card>
65-
</v-dialog>
66-
</v-container>
25+
<v-dialog v-model="addUserDialog" max-width="500px">
26+
<v-card>
27+
<v-card-title>{{ $t('admin.userManagement.addNewUser') }}</v-card-title>
28+
<v-card-text>
29+
<v-text-field v-model="newUser.username" :label="$t('admin.userManagement.username')" required></v-text-field>
30+
<v-text-field v-model="newUser.email" :label="$t('admin.userManagement.email')" required
31+
type="email"></v-text-field>
32+
<v-text-field v-model="newUser.password" :label="$t('admin.userManagement.password')" required
33+
type="password"></v-text-field>
34+
<v-autocomplete v-model="newUser.roles" :items="roles" item-title="name" outlined chips closable-chips
35+
small-chips :label="$t('admin.userManagement.roles')" multiple></v-autocomplete>
36+
</v-card-text>
37+
<v-card-actions>
38+
<v-spacer></v-spacer>
39+
<v-btn color="blue darken-1" text @click="closeAddUserDialog">{{ $t('common.cancel') }}</v-btn>
40+
<v-btn color="blue darken-1" text @click="createUser">{{ $t('common.save') }}</v-btn>
41+
<v-chip v-if="error" color="red darken-1">{{ $t('common.error') }}</v-chip>
42+
</v-card-actions>
43+
</v-card>
44+
</v-dialog>
45+
46+
<v-dialog v-model="editUserDialog" max-width="500px">
47+
<v-card>
48+
<v-card-title>{{ $t('admin.userManagement.editUser') }}</v-card-title>
49+
<v-card-text>
50+
<v-text-field v-model="editedUser.username" :label="$t('admin.userManagement.username')"
51+
required></v-text-field>
52+
<v-text-field v-model="editedUser.email" :label="$t('admin.userManagement.email')" required
53+
type="email"></v-text-field>
54+
<v-text-field v-model="editedUser.password" :label="$t('admin.userManagement.password')" required
55+
type="password"></v-text-field>
56+
<v-autocomplete v-model="editedUser.roles" :items="roles" item-title="name" outlined chips closable-chips
57+
small-chips :label="$t('admin.userManagement.roles')" multiple></v-autocomplete>
58+
</v-card-text>
59+
<v-card-actions>
60+
<v-spacer> </v-spacer>
61+
<v-btn color="blue darken-1" text @click="closeEditUserDialog">{{ $t('common.cancel') }}</v-btn>
62+
<v-btn color="blue darken-1" text @click="updateUser(editedUser)">{{ $t('common.save') }}</v-btn>
63+
<v-chip v-if="error" color="red darken-1">{{ $t('common.error') }}</v-chip>
64+
</v-card-actions>
65+
</v-card>
66+
</v-dialog>
67+
68+
<v-dialog v-model="deleteConfirmDialog" max-width="400px">
69+
<v-card>
70+
<v-card-title>{{ $t('admin.userManagement.confirmDelete') }}</v-card-title>
71+
<v-card-text>
72+
{{ $t('admin.userManagement.deleteConfirmMessage') }}
73+
</v-card-text>
74+
<v-card-text>
75+
{{ userToDelete.username }}
76+
</v-card-text>
77+
<v-card-actions>
78+
<v-spacer></v-spacer>
79+
<v-btn color="blue darken-1" text @click="closeDeleteConfirmDialog">{{ $t('common.cancel') }}</v-btn>
80+
<v-btn color="red darken-1" text @click="deleteUser(userToDelete)">{{ $t('common.delete') }}</v-btn>
81+
</v-card-actions>
82+
<v-chip v-if="error" color="red darken-1">{{ $t('common.error') }}</v-chip>
83+
</v-card>
84+
</v-dialog>
85+
6786
</template>
6887

6988
<script setup>
70-
import { ref, computed, onMounted } from 'vue';
89+
import { ref, computed, onMounted, onUnmounted } from 'vue';
7190
import { useI18n } from 'vue-i18n';
7291
7392
onMounted(async () => {
7493
await fetchUsers();
7594
await fetchRoles();
95+
document.documentElement.style.overflowY = 'scroll';
96+
});
97+
98+
onUnmounted(async () => {
99+
document.documentElement.style.overflowY = 'auto';
76100
});
77101
78102
const { t } = useI18n();
79103
80104
const headers = computed(() => [
105+
{ title: '#', key: 'id' },
81106
{ title: t('admin.userManagement.headers.username'), align: 'start', key: 'username' },
82107
{ title: t('admin.userManagement.headers.email'), key: 'email' },
83108
{ title: t('admin.userManagement.headers.roles'), key: 'rolesText' },
@@ -96,6 +121,21 @@ const newUser = ref({
96121
roles: [],
97122
});
98123
124+
const deleteConfirmDialog = ref(false);
125+
const userToDelete = ref({});
126+
127+
const openDeleteConfirmDialog = (user) => {
128+
error.value = false;
129+
userToDelete.value = user;
130+
deleteConfirmDialog.value = true;
131+
};
132+
133+
const closeDeleteConfirmDialog = () => {
134+
error.value = false;
135+
userToDelete.value = {};
136+
deleteConfirmDialog.value = false;
137+
};
138+
99139
const openAddUserDialog = () => {
100140
error.value = false;
101141
addUserDialog.value = true;
@@ -107,7 +147,6 @@ const openAddUserDialog = () => {
107147
};
108148
109149
const editUserDialog = ref(false);
110-
111150
const editedUser = ref({
112151
username: '',
113152
email: '',
@@ -135,6 +174,7 @@ const closeEditUserDialog = () => {
135174
};
136175
137176
const closeAddUserDialog = () => {
177+
error.value = false;
138178
addUserDialog.value = false;
139179
newUser.value = {
140180
username: '',
@@ -144,8 +184,10 @@ const closeAddUserDialog = () => {
144184
};
145185
146186
const deleteUser = async (user) => {
147-
// Implement delete user logic
148-
console.log('Delete user:', user);
187+
error.value = false;
188+
if (user.username === "admin") {
189+
return;
190+
}
149191
return await fetch("/Invoke/auth/deleteUser", {
150192
method: "post",
151193
headers: {
@@ -160,6 +202,7 @@ const deleteUser = async (user) => {
160202
.then((res) => res.json())
161203
.then((json) => {
162204
fetchUsers(); // refreshes users
205+
closeDeleteConfirmDialog();
163206
})
164207
.catch((err) => { error.value = true; console.warn(err); });
165208
};
@@ -194,8 +237,9 @@ const fetchRoles = async () => {
194237
195238
const updateUser = async (user) => {
196239
error.value = false;
240+
if (user.value) user = user.value;
197241
roleChange(user);
198-
var userDup = Object.assign({}, user);
242+
const userDup = Object.assign({}, user);
199243
delete userDup["id"];
200244
delete userDup["rolesText"];
201245
delete userDup["roles"];
@@ -213,8 +257,12 @@ const updateUser = async (user) => {
213257
})
214258
.then((res) => res.json())
215259
.then((json) => {
260+
if (json.error === false) {
261+
closeEditUserDialog();
262+
} else {
263+
error.value = true;
264+
}
216265
fetchUsers(); // refreshes users
217-
closeEditUserDialog();
218266
})
219267
.catch((err) => { error.value = true; console.warn(err); });
220268
}
@@ -239,6 +287,9 @@ const addRoleToUser = async (username, roleName) => {
239287
}
240288
241289
const removeRoleFromUser = async (username, roleName) => {
290+
if (username === "admin" && roleName === "admin") {
291+
return;
292+
}
242293
return await fetch("/Invoke/auth/userRemoveRole", {
243294
method: "post",
244295
headers: {
@@ -258,15 +309,23 @@ const removeRoleFromUser = async (username, roleName) => {
258309
}
259310
260311
const roleChange = (user) => {
261-
for (let i = 0; i < user.roles.length; i++)
262-
if (!editedUserRoles.value.includes(user.roles[i]))
263-
addRoleToUser(user.username, user.roles[i]);
264-
for (let i = 0; i < editedUserRoles.value.length; i++)
265-
if (!user.roles.includes(editedUserRoles.value[i]))
266-
removeRoleFromUser(user.username, editedUserRoles.value[i]);
312+
for (let i = 0; i < user.roles.length; i++) {
313+
const roleName = user.roles[i]?.name || user.roles[i];
314+
if (!editedUserRoles.value.includes(roleName))
315+
addRoleToUser(user.username, roleName);
316+
}
317+
318+
for (let i = 0; i < editedUserRoles.value.length; i++) {
319+
const roleName = editedUserRoles.value[i]?.name || editedUserRoles.value[i];
320+
if (!user.roles.includes(roleName))
321+
removeRoleFromUser(user.username, roleName);
322+
}
267323
}
268324
269325
const createUser = async () => {
326+
if (newUser.value.username === "admin") {
327+
return;
328+
}
270329
return await fetch("/Invoke/auth/createUser", {
271330
method: "post",
272331
headers: {
@@ -277,15 +336,19 @@ const createUser = async () => {
277336
})
278337
.then((res) => res.json())
279338
.then(async (json) => {
280-
await fetchUsers(); // refreshes users
281-
for (let i = 0; i < users.value.length; i++) {
282-
if (users.value[i].username === newUser.value.username) {
283-
newUser.value._id = users.value[i]._id;
284-
await updateUser(newUser.value);
285-
break;
339+
if (json.error === false) {
340+
await fetchUsers(); // refreshes users
341+
for (let i = 0; i < users.value.length; i++) {
342+
if (users.value[i].username === newUser.value.username) {
343+
newUser.value._id = users.value[i]._id;
344+
await updateUser(newUser);
345+
break;
346+
}
286347
}
348+
closeAddUserDialog();
349+
} else {
350+
error.value = true;
287351
}
288-
closeAddUserDialog();
289352
})
290353
.catch((err) => { error.value = true; console.warn(err); });
291354
}

0 commit comments

Comments
 (0)