Skip to content

Commit

Permalink
Role management on new Admin UI. In development.
Browse files Browse the repository at this point in the history
  • Loading branch information
riclolsen committed Sep 22, 2024
1 parent f545f6d commit cc73800
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 73 deletions.
12 changes: 9 additions & 3 deletions src/AdminUI/src/components/AdminPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
<v-card class="fill-height d-flex flex-column">
<v-tabs v-model="activeTab" color="primary" align-tabs="center">
<v-tab :value="1">{{ $t('admin.tabs.userManagement') }}</v-tab>
<v-tab :value="2">{{ $t('admin.tabs.systemSettings') }}</v-tab>
<v-tab :value="3">{{ $t('admin.tabs.logs') }}</v-tab>
<v-tab :value="2">{{ $t('admin.tabs.rolesManagement') }}</v-tab>
<v-tab :value="3">{{ $t('admin.tabs.systemSettings') }}</v-tab>
<v-tab :value="4">{{ $t('admin.tabs.logs') }}</v-tab>
</v-tabs>

<v-card-text>
Expand All @@ -14,10 +15,14 @@
</v-window-item>

<v-window-item :value="2">
<system-settings-tab />
<roles-management-tab />
</v-window-item>

<v-window-item :value="3">
<system-settings-tab />
</v-window-item>

<v-window-item :value="4">
<logs-tab />
</v-window-item>
</v-window>
Expand All @@ -29,6 +34,7 @@
<script setup>
import { ref } from 'vue';
import UserManagementTab from './UserManagementTab.vue';
import RolesManagementTab from './RolesManagementTab.vue';
import SystemSettingsTab from './SystemSettingsTab.vue';
import LogsTab from './LogsTab.vue';
Expand Down
4 changes: 2 additions & 2 deletions src/AdminUI/src/components/LoginPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,10 @@ function testLogin() {
.then((data) => {
if ('ok' in data && data.ok) {
router.push('/dashboard')
console.log('User is logged in')
// console.log('User is logged in')
} else {
router.push('/login')
console.log('User is not logged in')
// console.log('User is not logged in')
}
})
.catch((error) => {
Expand Down
197 changes: 130 additions & 67 deletions src/AdminUI/src/components/UserManagementTab.vue
Original file line number Diff line number Diff line change
@@ -1,83 +1,108 @@
<template>
<v-container fluid class="user-management-tab">
<h2 class="text-h5 mb-4">{{ $t('admin.userManagement.title') }}</h2>

<v-btn color="primary" class="mt-4" @click="openAddUserDialog">
{{ $t('admin.userManagement.addUser') }}
</v-btn>

<v-data-table :headers="headers" :items="users" :items-per-page="5" class="elevation-1" :load-children="fetchUsers"
:items-per-page-text="$t('common.itemsPerPageText')">
<template #[`item.actions`]="{ item }">
<v-icon size="small" class="me-2" @click="openEditUserDialog(item)">
mdi-pencil
</v-icon>
<v-icon v-if="item.username !== 'admin'" size="small" @click="deleteUser(item)">
<v-icon v-if="item.username !== 'admin'" size="small" @click="openDeleteConfirmDialog(item)">
mdi-delete
</v-icon>
</template>
</v-data-table>

<v-btn color="primary" class="mt-4" @click="openAddUserDialog">
{{ $t('admin.userManagement.addUser') }}
</v-btn>
<div>
<v-chip v-if="error" color="red darken-1">{{ $t('common.error') }}</v-chip>
</div>
</v-container>

<v-dialog v-model="addUserDialog" max-width="500px">
<v-card>
<v-card-title>{{ $t('admin.userManagement.addNewUser') }}</v-card-title>
<v-card-text>
<v-text-field v-model="newUser.username" :label="$t('admin.userManagement.username')" required></v-text-field>
<v-text-field v-model="newUser.email" :label="$t('admin.userManagement.email')" required
type="email"></v-text-field>
<v-text-field v-model="newUser.password" :label="$t('admin.userManagement.password')" required
type="password"></v-text-field>
<v-autocomplete v-model="newUser.roles" :items="roles" item-title="name" outlined chips closable-chips
small-chips :label="$t('admin.userManagement.roles')" multiple></v-autocomplete>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text @click="closeAddUserDialog">{{ $t('common.cancel') }}</v-btn>
<v-btn color="blue darken-1" text @click="createUser">{{ $t('common.save') }}</v-btn>
<v-chip v-if="error" color="red darken-1">{{ $t('common.error') }}</v-chip>
</v-card-actions>
</v-card>
</v-dialog>

<v-dialog v-model="editUserDialog" max-width="500px">
<v-card>
<v-card-title>{{ $t('admin.userManagement.editUser') }}</v-card-title>
<v-card-text>
<v-text-field v-model="editedUser.username" :label="$t('admin.userManagement.username')"
required></v-text-field>
<v-text-field v-model="editedUser.email" :label="$t('admin.userManagement.email')" required
type="email"></v-text-field>
<v-text-field v-model="editedUser.password" :label="$t('admin.userManagement.password')" required
type="password"></v-text-field>
<v-autocomplete v-model="editedUser.roles" :items="roles" item-title="name" outlined chips closable-chips
small-chips :label="$t('admin.userManagement.roles')" multiple></v-autocomplete>
</v-card-text>
<v-card-actions>
<v-spacer> </v-spacer>
<v-btn color="blue darken-1" text @click="closeEditUserDialog">{{ $t('common.cancel') }}</v-btn>
<v-btn color="blue darken-1" text @click="updateUser(editedUser)">{{ $t('common.save') }}</v-btn>
<v-chip v-if="error" color="red darken-1">{{ $t('common.error') }}</v-chip>
</v-card-actions>
</v-card>
</v-dialog>
</v-container>
<v-dialog v-model="addUserDialog" max-width="500px">
<v-card>
<v-card-title>{{ $t('admin.userManagement.addNewUser') }}</v-card-title>
<v-card-text>
<v-text-field v-model="newUser.username" :label="$t('admin.userManagement.username')" required></v-text-field>
<v-text-field v-model="newUser.email" :label="$t('admin.userManagement.email')" required
type="email"></v-text-field>
<v-text-field v-model="newUser.password" :label="$t('admin.userManagement.password')" required
type="password"></v-text-field>
<v-autocomplete v-model="newUser.roles" :items="roles" item-title="name" outlined chips closable-chips
small-chips :label="$t('admin.userManagement.roles')" multiple></v-autocomplete>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text @click="closeAddUserDialog">{{ $t('common.cancel') }}</v-btn>
<v-btn color="blue darken-1" text @click="createUser">{{ $t('common.save') }}</v-btn>
<v-chip v-if="error" color="red darken-1">{{ $t('common.error') }}</v-chip>
</v-card-actions>
</v-card>
</v-dialog>

<v-dialog v-model="editUserDialog" max-width="500px">
<v-card>
<v-card-title>{{ $t('admin.userManagement.editUser') }}</v-card-title>
<v-card-text>
<v-text-field v-model="editedUser.username" :label="$t('admin.userManagement.username')"
required></v-text-field>
<v-text-field v-model="editedUser.email" :label="$t('admin.userManagement.email')" required
type="email"></v-text-field>
<v-text-field v-model="editedUser.password" :label="$t('admin.userManagement.password')" required
type="password"></v-text-field>
<v-autocomplete v-model="editedUser.roles" :items="roles" item-title="name" outlined chips closable-chips
small-chips :label="$t('admin.userManagement.roles')" multiple></v-autocomplete>
</v-card-text>
<v-card-actions>
<v-spacer> </v-spacer>
<v-btn color="blue darken-1" text @click="closeEditUserDialog">{{ $t('common.cancel') }}</v-btn>
<v-btn color="blue darken-1" text @click="updateUser(editedUser)">{{ $t('common.save') }}</v-btn>
<v-chip v-if="error" color="red darken-1">{{ $t('common.error') }}</v-chip>
</v-card-actions>
</v-card>
</v-dialog>

<v-dialog v-model="deleteConfirmDialog" max-width="400px">
<v-card>
<v-card-title>{{ $t('admin.userManagement.confirmDelete') }}</v-card-title>
<v-card-text>
{{ $t('admin.userManagement.deleteConfirmMessage') }}
</v-card-text>
<v-card-text>
{{ userToDelete.username }}
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text @click="closeDeleteConfirmDialog">{{ $t('common.cancel') }}</v-btn>
<v-btn color="red darken-1" text @click="deleteUser(userToDelete)">{{ $t('common.delete') }}</v-btn>
</v-card-actions>
<v-chip v-if="error" color="red darken-1">{{ $t('common.error') }}</v-chip>
</v-card>
</v-dialog>

</template>

<script setup>
import { ref, computed, onMounted } from 'vue';
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { useI18n } from 'vue-i18n';
onMounted(async () => {
await fetchUsers();
await fetchRoles();
document.documentElement.style.overflowY = 'scroll';
});
onUnmounted(async () => {
document.documentElement.style.overflowY = 'auto';
});
const { t } = useI18n();
const headers = computed(() => [
{ title: '#', key: 'id' },
{ title: t('admin.userManagement.headers.username'), align: 'start', key: 'username' },
{ title: t('admin.userManagement.headers.email'), key: 'email' },
{ title: t('admin.userManagement.headers.roles'), key: 'rolesText' },
Expand All @@ -96,6 +121,21 @@ const newUser = ref({
roles: [],
});
const deleteConfirmDialog = ref(false);
const userToDelete = ref({});
const openDeleteConfirmDialog = (user) => {
error.value = false;
userToDelete.value = user;
deleteConfirmDialog.value = true;
};
const closeDeleteConfirmDialog = () => {
error.value = false;
userToDelete.value = {};
deleteConfirmDialog.value = false;
};
const openAddUserDialog = () => {
error.value = false;
addUserDialog.value = true;
Expand All @@ -107,7 +147,6 @@ const openAddUserDialog = () => {
};
const editUserDialog = ref(false);
const editedUser = ref({
username: '',
email: '',
Expand Down Expand Up @@ -135,6 +174,7 @@ const closeEditUserDialog = () => {
};
const closeAddUserDialog = () => {
error.value = false;
addUserDialog.value = false;
newUser.value = {
username: '',
Expand All @@ -144,8 +184,10 @@ const closeAddUserDialog = () => {
};
const deleteUser = async (user) => {
// Implement delete user logic
console.log('Delete user:', user);
error.value = false;
if (user.username === "admin") {
return;
}
return await fetch("/Invoke/auth/deleteUser", {
method: "post",
headers: {
Expand All @@ -160,6 +202,7 @@ const deleteUser = async (user) => {
.then((res) => res.json())
.then((json) => {
fetchUsers(); // refreshes users
closeDeleteConfirmDialog();
})
.catch((err) => { error.value = true; console.warn(err); });
};
Expand Down Expand Up @@ -194,8 +237,9 @@ const fetchRoles = async () => {
const updateUser = async (user) => {
error.value = false;
if (user.value) user = user.value;
roleChange(user);
var userDup = Object.assign({}, user);
const userDup = Object.assign({}, user);
delete userDup["id"];
delete userDup["rolesText"];
delete userDup["roles"];
Expand All @@ -213,8 +257,12 @@ const updateUser = async (user) => {
})
.then((res) => res.json())
.then((json) => {
if (json.error === false) {
closeEditUserDialog();
} else {
error.value = true;
}
fetchUsers(); // refreshes users
closeEditUserDialog();
})
.catch((err) => { error.value = true; console.warn(err); });
}
Expand All @@ -239,6 +287,9 @@ const addRoleToUser = async (username, roleName) => {
}
const removeRoleFromUser = async (username, roleName) => {
if (username === "admin" && roleName === "admin") {
return;
}
return await fetch("/Invoke/auth/userRemoveRole", {
method: "post",
headers: {
Expand All @@ -258,15 +309,23 @@ const removeRoleFromUser = async (username, roleName) => {
}
const roleChange = (user) => {
for (let i = 0; i < user.roles.length; i++)
if (!editedUserRoles.value.includes(user.roles[i]))
addRoleToUser(user.username, user.roles[i]);
for (let i = 0; i < editedUserRoles.value.length; i++)
if (!user.roles.includes(editedUserRoles.value[i]))
removeRoleFromUser(user.username, editedUserRoles.value[i]);
for (let i = 0; i < user.roles.length; i++) {
const roleName = user.roles[i]?.name || user.roles[i];
if (!editedUserRoles.value.includes(roleName))
addRoleToUser(user.username, roleName);
}
for (let i = 0; i < editedUserRoles.value.length; i++) {
const roleName = editedUserRoles.value[i]?.name || editedUserRoles.value[i];
if (!user.roles.includes(roleName))
removeRoleFromUser(user.username, roleName);
}
}
const createUser = async () => {
if (newUser.value.username === "admin") {
return;
}
return await fetch("/Invoke/auth/createUser", {
method: "post",
headers: {
Expand All @@ -277,15 +336,19 @@ const createUser = async () => {
})
.then((res) => res.json())
.then(async (json) => {
await fetchUsers(); // refreshes users
for (let i = 0; i < users.value.length; i++) {
if (users.value[i].username === newUser.value.username) {
newUser.value._id = users.value[i]._id;
await updateUser(newUser.value);
break;
if (json.error === false) {
await fetchUsers(); // refreshes users
for (let i = 0; i < users.value.length; i++) {
if (users.value[i].username === newUser.value.username) {
newUser.value._id = users.value[i]._id;
await updateUser(newUser);
break;
}
}
closeAddUserDialog();
} else {
error.value = true;
}
closeAddUserDialog();
})
.catch((err) => { error.value = true; console.warn(err); });
}
Expand Down
Loading

0 comments on commit cc73800

Please sign in to comment.