Skip to content
Snippets Groups Projects
Verified Commit 1c68f0a8 authored by Daniel Göbel's avatar Daniel Göbel
Browse files

Add modal to register new users

parent 48b87115
No related branches found
No related tags found
1 merge request!116Resolve "Create user"
...@@ -37,7 +37,7 @@ build: ...@@ -37,7 +37,7 @@ build:
.build-container-job: .build-container-job:
stage: deploy stage: deploy
image: image:
name: gcr.io/kaniko-project/executor:v1.21.1-debug name: gcr.io/kaniko-project/executor:v1.23.0-debug
entrypoint: [ "" ] entrypoint: [ "" ]
dependencies: [ ] dependencies: [ ]
cache: [ ] cache: [ ]
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import { useUserStore } from "@/stores/users"; import { useUserStore } from "@/stores/users";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import { useCookies } from "vue3-cookies";
import { watch, ref, computed } from "vue"; import { watch, ref, computed } from "vue";
import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import BootstrapModal from "@/components/modals/BootstrapModal.vue";
import { OpenAPI } from "@/client"; import { OpenAPI } from "@/client";
...@@ -10,12 +9,10 @@ import CopyToClipboardIcon from "@/components/CopyToClipboardIcon.vue"; ...@@ -10,12 +9,10 @@ import CopyToClipboardIcon from "@/components/CopyToClipboardIcon.vue";
import dayjs from "dayjs"; import dayjs from "dayjs";
const userRepository = useUserStore(); const userRepository = useUserStore();
const { cookies } = useCookies();
const route = useRoute(); const route = useRoute();
function logout() { function logout() {
userRepository.logout(); userRepository.logout();
cookies.remove("bearer");
} }
const activeRoute = ref(""); const activeRoute = ref("");
...@@ -299,13 +296,12 @@ watch( ...@@ -299,13 +296,12 @@ watch(
<hr class="dropdown-divider" /> <hr class="dropdown-divider" />
</li> </li>
<li> <li>
<router-link <a
:to="{ name: 'login' }"
class="dropdown-item" class="dropdown-item"
@click="logout" @click="logout"
:href="OpenAPI.BASE + '/auth/logout'"
>Sign out</a
> >
Sign out
</router-link>
</li> </li>
</ul> </ul>
</div> </div>
......
<script setup lang="ts">
import { RoleEnum, type UserIn, type UserOutExtended } from "@/client";
import { onMounted, reactive, ref } from "vue";
import { Modal, Toast } from "bootstrap";
import BootstrapModal from "@/components/modals/BootstrapModal.vue";
import { useUserStore } from "@/stores/users";
import BootstrapToast from "@/components/BootstrapToast.vue";
let modal: Modal | null = null;
const createUserForm = ref<HTMLFormElement | undefined>(undefined);
let successToast: Toast | undefined;
let errorToast: Toast | undefined;
const userRepository = useUserStore();
const formState = reactive<{
loading: boolean;
user: UserIn;
errorMessage?: string;
registeredUserName: string;
validated: boolean;
}>({
loading: false,
errorMessage: "",
validated: false,
registeredUserName: "",
user: {
display_name: "",
email: "",
roles: [RoleEnum.USER],
},
});
const props = defineProps<{
modalId: string;
}>();
const emit = defineEmits<{
(e: "user-created", user: UserOutExtended): void;
}>();
function createUser() {
formState.validated = true;
if (createUserForm.value?.checkValidity()) {
formState.loading = true;
formState.registeredUserName = formState.user.display_name;
userRepository
.createUser(formState.user)
.then((user) => {
emit("user-created", user);
formState.validated = false;
formState.user = {
display_name: "",
email: "",
roles: [RoleEnum.USER],
};
successToast?.show();
modal?.hide();
})
.catch((err) => {
formState.errorMessage = err.body?.detail;
errorToast?.show();
})
.finally(() => {
formState.loading = false;
});
}
}
onMounted(() => {
modal = Modal.getOrCreateInstance(`#${props.modalId}`);
successToast = new Toast("#create-user-success-toast");
errorToast = new Toast("#create-user-error-toast");
});
</script>
<template>
<bootstrap-toast toast-id="create-user-success-toast">
Successfully registered user {{ formState.registeredUserName }}
</bootstrap-toast>
<bootstrap-toast toast-id="create-user-error-toast" color-class="danger">
<template #default
>Couldn't regsiter user
{{ formState.registeredUserName }}
</template>
<template #body>Error: {{ formState.errorMessage }}</template>
</bootstrap-toast>
<bootstrap-modal
:modalId="props.modalId"
:static-backdrop="true"
modal-label="Create user"
>
<template #header>Register a user</template>
<template #body>
<form
id="create-user-form"
@submit.prevent="createUser()"
:class="{ 'was-validated': formState.validated }"
ref="createUserForm"
novalidate
>
<div class="mb-3">
<label for="create-user-name" class="form-label">Name</label>
<input
type="text"
class="form-control"
id="create-user-name"
minlength="3"
maxlength="256"
required
placeholder="John Doe"
v-model="formState.user.display_name"
/>
<div class="invalid-feedback">Please choose a name.</div>
</div>
<div class="mb-3">
<label for="create-user-email" class="form-label"
>Email address</label
>
<input
type="email"
class="form-control"
id="create-user-email"
required
placeholder="name@example.com"
minlength="3"
maxlength="256"
v-model="formState.user.email"
/>
<div class="invalid-feedback">
Please provide a valid email address.
</div>
</div>
<div class="mb-3">
<div class="mb-1">Roles:</div>
<div
class="form-check"
v-for="role in Object.values(RoleEnum)"
:key="role"
>
<input
class="form-check-input"
type="checkbox"
:value="role"
:id="`create-user-role-${role}`"
v-model="formState.user.roles"
/>
<label class="form-check-label" :for="`create-user-role-${role}`">
{{ role.toUpperCase() }}
</label>
</div>
</div>
</form>
</template>
<template #footer>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
Close
</button>
<button
type="submit"
form="create-user-form"
class="btn btn-primary"
:disabled="formState.loading"
>
Save
</button>
</template>
</bootstrap-modal>
</template>
<style scoped></style>
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import type { UserOutExtended, UserOut } from "@/client"; import type { UserOutExtended, UserOut, UserIn } from "@/client";
import { OpenAPI, UserService, RoleEnum } from "@/client"; import { OpenAPI, UserService, RoleEnum } from "@/client";
import { useWorkflowExecutionStore } from "@/stores/workflowExecutions"; import { useWorkflowExecutionStore } from "@/stores/workflowExecutions";
import { useBucketStore } from "@/stores/buckets"; import { useBucketStore } from "@/stores/buckets";
...@@ -113,6 +113,12 @@ export const useUserStore = defineStore({ ...@@ -113,6 +113,12 @@ export const useUserStore = defineStore({
roles: roles, roles: roles,
}); });
}, },
createUser(userIn: UserIn): Promise<UserOutExtended> {
return UserService.userCreateUser(userIn).then((user) => {
useNameStore().addNameToMapping(user.uid, user.display_name);
return user;
});
},
searchUser(searchString: string): Promise<UserOut[]> { searchUser(searchString: string): Promise<UserOut[]> {
return UserService.userSearchUsers(searchString).then((users) => { return UserService.userSearchUsers(searchString).then((users) => {
const nameStore = useNameStore(); const nameStore = useNameStore();
......
...@@ -5,6 +5,7 @@ import { RoleEnum, type UserOutExtended } from "@/client"; ...@@ -5,6 +5,7 @@ import { RoleEnum, type UserOutExtended } from "@/client";
import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import BootstrapToast from "@/components/BootstrapToast.vue"; import BootstrapToast from "@/components/BootstrapToast.vue";
import { Toast } from "bootstrap"; import { Toast } from "bootstrap";
import CreateUserModal from "@/components/admin/CreateUserModal.vue";
const userRepository = useUserStore(); const userRepository = useUserStore();
type RoleList = RoleEnum[]; type RoleList = RoleEnum[];
...@@ -96,10 +97,19 @@ onMounted(() => { ...@@ -96,10 +97,19 @@ onMounted(() => {
</template> </template>
<template #body>Error: {{ userState.errorMessage }}</template> <template #body>Error: {{ userState.errorMessage }}</template>
</bootstrap-toast> </bootstrap-toast>
<create-user-modal modal-id="create-user-modal" />
<div <div
class="row border-bottom mb-4 justify-content-between align-items-center" class="row border-bottom mb-4 justify-content-between align-items-center pb-2 pe-2"
> >
<h2>Manage Users</h2> <h2 class="w-fit">Manage Users</h2>
<button
type="button"
class="btn btn-primary w-fit"
data-bs-toggle="modal"
data-bs-target="#create-user-modal"
>
Register User
</button>
</div> </div>
<form @submit.prevent="searchUsers" id="admin-user-search-form"> <form @submit.prevent="searchUsers" id="admin-user-search-form">
<div class="d-flex justify-content-evenly align-content-center"> <div class="d-flex justify-content-evenly align-content-center">
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment