web: added functionality for add/delete user

This commit is contained in:
Mika 2024-07-12 00:50:30 +02:00
parent d2c50fc2e5
commit c6074c26e0
6 changed files with 200 additions and 14 deletions

1
web/components.d.ts vendored
View file

@ -16,6 +16,7 @@ declare module 'vue' {
LoginPage: typeof import('./src/components/loginPage.vue')['default']
MainPage: typeof import('./src/components/MainPage.vue')['default']
UpdateDialog: typeof import('./src/components/UpdateDialog.vue')['default']
UsersAddDialog: typeof import('./src/components/NavBarIcons/UsersAddDialog.vue')['default']
UsersDeleteActions: typeof import('./src/components/NavBarIcons/UsersDeleteActions.vue')['default']
UsersDialog: typeof import('./src/components/NavBarIcons/UsersDialog.vue')['default']
UsersEditActions: typeof import('./src/components/NavBarIcons/UsersEditActions.vue')['default']

View file

@ -6,7 +6,9 @@
<!-- Users, Groups, Licenses -->
<div>
<!-- USER MANAGEMENT -->
<UsersDialog />
<template v-if="!user" />
<UsersDialog v-else-if="user.admin" />
<UsersEditActions v-else :admin-menu="false" :user="user!" />
<!-- -->
<!-- GROUP SECTION -->
<!-- -->
@ -206,8 +208,10 @@ import { SubmitEventPromise } from "vuetify";
import { ref } from "vue";
import { Plus, LogOut, FolderPlus } from "lucide-vue-next";
import { useQuery, useMutation, useQueryClient } from "@tanstack/vue-query";
import UsersDialog from "./NavBarIcons/UsersDialog.vue";
import UsersEditActions from "./NavBarIcons/UsersEditActions.vue";
import { axiosInstance } from "@/client";
import { LicenseGroup, CreateLicenseDto, CreateGroupDto } from "@/types";
import { LicenseGroup, CreateLicenseDto, CreateGroupDto, User } from "@/types";
const queryClient = useQueryClient();
@ -227,6 +231,14 @@ const { data } = useQuery({
},
});
const { data: user } = useQuery({
queryKey: ["user"],
queryFn: async () => {
const res = await axiosInstance.get<User>("/users/me");
return res.data;
},
});
const { mutate: licenseMutate } = useMutation({
mutationFn: async (newLicense: CreateLicenseDto) => {
await axiosInstance.post("/licenses", newLicense);

View file

@ -0,0 +1,149 @@
<template>
<div>
<v-btn prepend-icon="mdi-plus" variant="outlined" block @click="add = true">
Add User
</v-btn>
<v-dialog v-model="add" width="600" persistent>
<v-card max-width="600">
<v-form @submit.prevent="submit">
<v-card-title>
<span class="headline">Add User</span>
</v-card-title>
<v-card-subtitle>
<span> Add an User to the database</span>
</v-card-subtitle>
<v-card-text>
<div>
<v-text-field
label="User Name *"
v-model="addUserName"
variant="outlined"
required
clearable
:rules="userNameRules"
class="mb-3"
></v-text-field>
<v-text-field
label="User Email *"
v-model="addUserEmail"
variant="outlined"
required
clearable
:rules="userNameEmail"
class="mb-3"
></v-text-field>
<v-text-field
label="User Password *"
v-model="addUserPassword"
variant="outlined"
required
clearable
:rules="userNamePassword"
class="mb-3"
></v-text-field>
<v-switch
label="Admin (Optional)"
inset
v-model="addUserAdmin"
color="primary"
></v-switch>
<span class="dialogNote">
all fields marked with * are required
</span>
</div>
</v-card-text>
<v-card-actions>
<v-row>
<v-col cols="10" align="right" no-gutters>
<v-btn
class="ms-auto"
text="Cancel"
color="blue darken-1"
@click="add = false"
></v-btn>
</v-col>
<v-col>
<v-btn
class="ms-auto"
text="Add"
type="submit"
color="blue darken-1"
></v-btn>
</v-col>
</v-row>
</v-card-actions>
</v-form>
</v-card>
</v-dialog>
</div>
</template>
<script setup lang="ts">
import { axiosInstance } from "@/client";
import { CreateUserDto } from "@/types";
import { useMutation, useQueryClient } from "@tanstack/vue-query";
import { ref } from "vue";
import { SubmitEventPromise } from "vuetify";
const add = ref(false);
const addUserName = ref("");
const addUserEmail = ref("");
const addUserPassword = ref("");
const addUserAdmin = ref(false);
const userNameRules = [
(value: string) => {
if (value) return true;
return "YOU MUST ENTER (A USER NAME";
},
];
const userNameEmail = [
(value: string) => {
if (value) return true;
return "YOU MUST ENTER (A USER EMAIL)";
},
];
const userNamePassword = [
(value: string) => {
if (value) return true;
return "YOU MUST ENTER (A USER PASSWORD)";
},
];
const queryClient = useQueryClient();
const { mutate: userMutate } = useMutation({
mutationFn: async (newUser: CreateUserDto) => {
await axiosInstance.post("/users", newUser);
},
onError: (error) => {
console.log(error);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["users"] });
add.value = false;
},
});
async function submit(event: SubmitEventPromise) {
const result = await event;
if (result.valid) {
const data = {
name: addUserName.value,
email: addUserEmail.value,
password: addUserPassword.value,
admin: addUserAdmin.value,
};
console.log(data);
userMutate(data);
} else {
console.log("Invalid");
}
}
</script>
<style scoped></style>

View file

@ -16,16 +16,16 @@
:items="data ?? []"
:items-per-page="10"
item-key="name"
class="elevation-5"
class="elevation-5 mb-5"
>
<template v-slot:item.actions="{ item }">
<UsersEditAction :user="item" />
<UsersEditAction :user="item" :admin-menu="true" />
<UsersDeleteAction :user="item" />
</template>
</v-data-table>
<UsersAddDialog />
<v-divider class="border-opacity-50 mt-5" :thickness="2"></v-divider>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" @click="users = false">Close</v-btn>
@ -37,9 +37,10 @@
<script setup lang="ts">
import UsersDeleteAction from "./UsersDeleteActions.vue";
import UsersEditAction from "./UsersEditActions.vue";
import UsersAddDialog from "./UsersAddDialog.vue";
import { Users } from "lucide-vue-next";
import { defineComponent, ref, computed, watch } from "vue";
import { useQuery, useMutation, useQueryClient } from "@tanstack/vue-query";
import { ref } from "vue";
import { useQuery, useQueryClient } from "@tanstack/vue-query";
import { axiosInstance } from "@/client";
import { User } from "@/types";

View file

@ -1,5 +1,10 @@
<template>
<v-icon class="me-2" size="small" @click="edit = true">mdi-pencil</v-icon>
<v-icon v-if="adminMenu" class="me-2" size="small" @click="edit = true"
>mdi-pencil</v-icon
>
<v-btn v-else icon class="mr-3" @click="edit = true">
<Users />
</v-btn>
<v-dialog v-model="edit" width="600" persistent>
<v-card max-width="600">
<v-form @submit.prevent="submitGroup">
@ -15,7 +20,6 @@
label="User Name *"
v-model="editUserName"
variant="outlined"
required
clearable
:rules="editUserNameRules"
class="mb-3"
@ -24,17 +28,27 @@
label="User Email *"
v-model="editUserEmail"
variant="outlined"
required
clearable
:rules="editUserEmailRules"
class="mb-3"
></v-text-field>
<v-text-field
label="User Password (Optional)"
v-model="editUserPassword"
variant="outlined"
clearable
class="mb-3"
></v-text-field>
<v-switch
label="Admin"
label="Admin (Optional)"
inset
v-model="editUserAdmin"
color="primary"
v-if="adminMenu"
></v-switch>
<span class="dialogNote">
all fields marked with * are required
</span>
</div>
</v-card-text>
<v-card-actions>
@ -68,8 +82,9 @@ import { SubmitEventPromise } from "vuetify";
import { axiosInstance } from "@/client";
import { useMutation, useQueryClient } from "@tanstack/vue-query";
import { CreateLicenseDto, User } from "@/types";
import { Users } from "lucide-vue-next";
const { user } = defineProps<{ user: User }>();
const { user, adminMenu } = defineProps<{ user: User; adminMenu: boolean }>();
const queryClient = useQueryClient();
@ -107,15 +122,16 @@ const { mutate: userMutate } = useMutation({
await axiosInstance.post("/licenses", newLicense);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["licenses"] });
queryClient.invalidateQueries({ queryKey: ["users"] });
edit.value = false;
},
});
const edit = ref(false);
const editUserName = ref(user.name);
const editUserEmail = ref(user.email);
const editUserAdmin = ref(user.admin);
const edit = ref(false);
const editUserPassword = ref(undefined);
</script>
<style scoped></style>

View file

@ -32,6 +32,13 @@ export interface User {
admin: boolean;
}
export interface CreateUserDto {
name: string;
email: string;
password: string;
admin: boolean;
}
export type CreateGroupDto = Omit<LicenseGroup, "id" | "licenses">;
export type UpdateLicenseDto = Omit<License, "id">;