diff --git a/frontend/src/assets/lang/app/en.json b/frontend/src/assets/lang/app/en.json index 068103ea..47482195 100644 --- a/frontend/src/assets/lang/app/en.json +++ b/frontend/src/assets/lang/app/en.json @@ -278,6 +278,7 @@ "error": "Error", "unknown": "An unknown error has occurred.", "create": "{type} has successfully been created.", + "edit": "{type} has successfully been edited.", "courses": { "enrollment": { "success": "You have been successfully enrolled for the course '{0}'.", diff --git a/frontend/src/assets/lang/app/nl.json b/frontend/src/assets/lang/app/nl.json index 4c1d91e2..bb17f5ee 100644 --- a/frontend/src/assets/lang/app/nl.json +++ b/frontend/src/assets/lang/app/nl.json @@ -281,6 +281,7 @@ "error": "Fout", "unknown": "Er is een onbekende fout opgetreden.", "create": "{type} werd succesvol aangemaakt.", + "edit": "{type} werd succesvol bewerkt.", "courses": { "enrollment": { "success": "Je bent succesvol ingeschreven voor het vak '{0}'.", diff --git a/frontend/src/composables/services/admin.service.ts b/frontend/src/composables/services/admin.service.ts index 5b576414..f49424de 100644 --- a/frontend/src/composables/services/admin.service.ts +++ b/frontend/src/composables/services/admin.service.ts @@ -1,6 +1,6 @@ import { type Ref, ref } from 'vue'; import { endpoints } from '@/config/endpoints.ts'; -import { get, getList, deleteId, createToast } from '@/composables/services/helpers.ts'; +import { get, getList, deleteId, create } from '@/composables/services/helpers.ts'; import { User } from '@/types/users/User.ts'; interface AdminState { @@ -28,8 +28,7 @@ export function useAdmin(): AdminState { async function createAdmin(user: User, selfProcessError: boolean = true): Promise { const endpoint = endpoints.admins.index; - await createToast( - 'admin', + await create( endpoint, { id: user.id, diff --git a/frontend/src/composables/services/assistant.service.ts b/frontend/src/composables/services/assistant.service.ts index c2e5eff3..446993c4 100644 --- a/frontend/src/composables/services/assistant.service.ts +++ b/frontend/src/composables/services/assistant.service.ts @@ -3,15 +3,7 @@ import { type User } from '@/types/users/User.ts'; import { Response } from '@/types/Response'; import { type Ref, ref } from 'vue'; import { endpoints } from '@/config/endpoints.ts'; -import { - get, - getList, - create, - deleteId, - deleteIdWithData, - getPaginatedList, - createToast, -} from '@/composables/services/helpers.ts'; +import { get, getList, create, deleteId, deleteIdWithData, getPaginatedList } from '@/composables/services/helpers.ts'; import { type PaginatorResponse } from '@/types/filter/Paginator.ts'; import { type Filter } from '@/types/filter/Filter.ts'; @@ -103,8 +95,7 @@ export function useAssistant(): AssistantState { async function createAssistant(user: User, selfProcessError: boolean = true): Promise { const endpoint = endpoints.assistants.index; - await createToast( - 'assistant', + await create( endpoint, { user: user.id, diff --git a/frontend/src/composables/services/docker.service.ts b/frontend/src/composables/services/docker.service.ts index 6ed450f2..2d03c813 100644 --- a/frontend/src/composables/services/docker.service.ts +++ b/frontend/src/composables/services/docker.service.ts @@ -4,7 +4,7 @@ import { endpoints } from '@/config/endpoints.ts'; import { type Ref, ref } from 'vue'; import { type Filter } from '@/types/filter/Filter.ts'; import { - createToast, + create, getList, getPaginatedList, patch, @@ -64,8 +64,7 @@ export function useDockerImages(): DockerImagesState { selfProcessError: boolean = true, ): Promise { const endpoint = endpoints.dockerImages.index; - await createToast( - 'docker', + await create( endpoint, { file, diff --git a/frontend/src/composables/services/faculty.service.ts b/frontend/src/composables/services/faculty.service.ts index 6c2c31f4..fe8bb5fc 100644 --- a/frontend/src/composables/services/faculty.service.ts +++ b/frontend/src/composables/services/faculty.service.ts @@ -1,7 +1,7 @@ import { Faculty } from '@/types/Faculty.ts'; import { type Ref, ref } from 'vue'; import { endpoints } from '@/config/endpoints.ts'; -import { get, getList, deleteId, createToast } from '@/composables/services/helpers.ts'; +import { get, getList, deleteId, create } from '@/composables/services/helpers.ts'; interface FacultyState { faculties: Ref; @@ -28,8 +28,7 @@ export function useFaculty(): FacultyState { async function createFaculty(facultyData: Faculty, selfProcessError: boolean = true): Promise { const endpoint = endpoints.faculties.index; - await createToast( - 'faculty', + await create( endpoint, { id: facultyData.id, name: facultyData.name }, faculty, diff --git a/frontend/src/composables/services/group.service.ts b/frontend/src/composables/services/group.service.ts index cf212cff..14d1557f 100644 --- a/frontend/src/composables/services/group.service.ts +++ b/frontend/src/composables/services/group.service.ts @@ -65,6 +65,7 @@ export function useGroup(): GroupState { score: groupData.score, }, response, + undefined, selfProcessError, ); } diff --git a/frontend/src/composables/services/helpers.ts b/frontend/src/composables/services/helpers.ts index 4eab55c7..44367384 100644 --- a/frontend/src/composables/services/helpers.ts +++ b/frontend/src/composables/services/helpers.ts @@ -13,6 +13,7 @@ import { type Filter } from '@/types/filter/Filter.ts'; * @param endpoint * @param ref * @param fromJson + * @param selfProcessError */ export async function get( endpoint: string, @@ -44,6 +45,7 @@ export async function get( * @param ref * @param fromJson * @param contentType + * @param selfProcessError */ export async function create( endpoint: string, @@ -70,31 +72,6 @@ export async function create( } } -/** - * Create an item and display toast message when successful stating the creation has been successful. - * - * @param type type of the object that gets created - * @param endpoint - * @param data - * @param ref - * @param fromJson - * @param contentType - */ -export async function createToast( - type: string, - endpoint: string, - data: any, - ref: Ref, - fromJson: (data: any) => T, - contentType: string = 'application/json', -): Promise { - const { t } = i18n.global; - const { addSuccessMessage } = useMessagesStore(); - - await create(endpoint, data, ref, fromJson, contentType); - addSuccessMessage(t('toasts.messages.success'), t('toasts.messages.create', { type: t('types.article.' + type) })); -} - /** * Patch an item given its ID. * @@ -102,6 +79,7 @@ export async function createToast( * @param data * @param ref * @param contentType + * @param selfProcessError */ export async function patch( endpoint: string, @@ -133,6 +111,7 @@ export async function patch( * @param endpoint * @param data * @param contentType + * @param selfProcessError */ export async function put( endpoint: string, @@ -162,6 +141,7 @@ export async function put( * @param endpoint * @param ref * @param fromJson + * @param selfProcessError */ export async function deleteId( endpoint: string, @@ -189,6 +169,7 @@ export async function deleteId( * @param data * @param ref * @param fromJson + * @param selfProcessError */ export async function deleteIdWithData( endpoint: string, @@ -216,6 +197,7 @@ export async function deleteIdWithData( * @param endpoint * @param ref * @param fromJson + * @param selfProcessError */ export async function getList( endpoint: string, @@ -246,6 +228,7 @@ export async function getList( * @param pageSize * @param pagination * @param fromJson + * @param selfProcessError */ export async function getPaginatedList( endpoint: string, diff --git a/frontend/src/composables/services/project.service.ts b/frontend/src/composables/services/project.service.ts index 05e2f713..9f15c3ed 100644 --- a/frontend/src/composables/services/project.service.ts +++ b/frontend/src/composables/services/project.service.ts @@ -1,10 +1,8 @@ import { Project } from '@/types/Project'; import { type Ref, ref } from 'vue'; import { endpoints } from '@/config/endpoints.ts'; -import { i18n } from '@/config/i18n.ts'; import { create, deleteId, get, getList, patch } from '@/composables/services/helpers.ts'; import { type Response } from '@/types/Response.ts'; -import { useMessagesStore } from '@/store/messages.store.ts'; interface ProjectState { projects: Ref; @@ -75,9 +73,6 @@ export function useProject(): ProjectState { numberOfGroups: number, selfProcessError: boolean = true, ): Promise { - const { t } = i18n.global; - const { addSuccessMessage } = useMessagesStore(); - const endpoint = endpoints.projects.byCourse.replace('{courseId}', courseId); // Initialize an empty object to hold the data to send @@ -107,10 +102,6 @@ export function useProject(): ProjectState { 'multipart/form-data', selfProcessError, ); - addSuccessMessage( - t('toasts.messages.success'), - t('toasts.messages.projects.create.success', [project.value?.name]), - ); } async function updateProject(projectData: Project, selfProcessError: boolean = true): Promise { diff --git a/frontend/src/composables/services/structure_check.service.ts b/frontend/src/composables/services/structure_check.service.ts index 72651093..79e50428 100644 --- a/frontend/src/composables/services/structure_check.service.ts +++ b/frontend/src/composables/services/structure_check.service.ts @@ -1,7 +1,7 @@ import { StructureCheck } from '@/types/StructureCheck.ts'; import { type Ref, ref } from 'vue'; import { endpoints } from '@/config/endpoints.ts'; -import { get, getList, createToast, deleteId, put } from '@/composables/services/helpers.ts'; +import { get, getList, create, deleteId, put } from '@/composables/services/helpers.ts'; interface StructureCheckState { structureChecks: Ref; @@ -42,8 +42,7 @@ export function useStructureCheck(): StructureCheckState { selfProcessError: boolean = true, ): Promise { const endpoint = endpoints.structureChecks.byProject.replace('{projectId}', projectId); - await createToast( - 'structureCheck', + await create( endpoint, { path: structureCheckData.path, diff --git a/frontend/src/composables/services/student.service.ts b/frontend/src/composables/services/student.service.ts index f12cf6d6..1ea17139 100644 --- a/frontend/src/composables/services/student.service.ts +++ b/frontend/src/composables/services/student.service.ts @@ -2,7 +2,7 @@ import { Student } from '@/types/users/Student.ts'; import { Response } from '@/types/Response'; import { type Ref, ref } from 'vue'; import { endpoints } from '@/config/endpoints.ts'; -import { get, getList, create, deleteId, deleteIdWithData, createToast } from '@/composables/services/helpers.ts'; +import { get, getList, create, deleteId, deleteIdWithData } from '@/composables/services/helpers.ts'; interface StudentsState { students: Ref; @@ -111,8 +111,7 @@ export function useStudents(): StudentsState { async function createStudent(studentData: Student, selfProcessError: boolean = true): Promise { const endpoint = endpoints.students.index; - await createToast( - 'student', + await create( endpoint, { user: studentData.id, diff --git a/frontend/src/composables/services/submission.service.ts b/frontend/src/composables/services/submission.service.ts index 2387d0a2..589e4296 100644 --- a/frontend/src/composables/services/submission.service.ts +++ b/frontend/src/composables/services/submission.service.ts @@ -1,9 +1,7 @@ import { Submission } from '@/types/submission/Submission.ts'; import { type Ref, ref, type UnwrapRef } from 'vue'; import { endpoints } from '@/config/endpoints.ts'; -import { i18n } from '@/config/i18n.ts'; import { get, getList, deleteId, create } from '@/composables/services/helpers.ts'; -import { useMessagesStore } from '@/store/messages.store.ts'; interface SubmissionState { submissions: Ref; @@ -39,9 +37,6 @@ export function useSubmission(): SubmissionState { groupId: string, selfProcessError: boolean = true, ): Promise { - const { t } = i18n.global; - const { addSuccessMessage } = useMessagesStore(); - const endpoint = endpoints.submissions.byGroup.replace('{groupId}', groupId); // formData is necessary with multiform data (otherwise files value is changed to files[] by axios) const formData = new FormData(); @@ -49,7 +44,6 @@ export function useSubmission(): SubmissionState { formData.append('files', file); // Gebruik 'files' in plaats van 'files[]' }); await create(endpoint, formData, submission, Submission.fromJSON, 'multipart/form-data', selfProcessError); - addSuccessMessage(t('toasts.messages.success'), t('toasts.messages.submissions.create.success')); } async function deleteSubmission(id: string, selfProcessError: boolean = true): Promise { diff --git a/frontend/src/composables/services/teacher.service.ts b/frontend/src/composables/services/teacher.service.ts index f25583c5..dc4a4c8b 100644 --- a/frontend/src/composables/services/teacher.service.ts +++ b/frontend/src/composables/services/teacher.service.ts @@ -4,15 +4,7 @@ import { Teacher } from '@/types/users/Teacher.ts'; import { Response } from '@/types/Response'; import { type Ref, ref } from 'vue'; import { endpoints } from '@/config/endpoints.ts'; -import { - get, - getList, - create, - deleteId, - deleteIdWithData, - getPaginatedList, - createToast, -} from '@/composables/services/helpers.ts'; +import { get, getList, create, deleteId, deleteIdWithData, getPaginatedList } from '@/composables/services/helpers.ts'; import { type PaginatorResponse } from '@/types/filter/Paginator.ts'; import { type Filter } from '@/types/filter/Filter.ts'; @@ -104,8 +96,7 @@ export function useTeacher(): TeacherState { async function createTeacher(user: User, selfProcessError: boolean = true): Promise { const endpoint = endpoints.teachers.index; - await createToast( - 'teacher', + await create( endpoint, { user: user.id, diff --git a/frontend/src/composables/services/users.service.ts b/frontend/src/composables/services/users.service.ts index 1f6bfaf9..924bf576 100644 --- a/frontend/src/composables/services/users.service.ts +++ b/frontend/src/composables/services/users.service.ts @@ -3,7 +3,7 @@ import { User } from '@/types/users/User.ts'; import { type Response } from '@/types/Response.ts'; import { type PaginatorResponse } from '@/types/filter/Paginator.ts'; import { endpoints } from '@/config/endpoints.ts'; -import { get, patch, getList, getPaginatedList, createToast } from '@/composables/services/helpers.ts'; +import { get, patch, getList, getPaginatedList, create } from '@/composables/services/helpers.ts'; import { type Filter } from '@/types/filter/Filter.ts'; interface userState { @@ -45,8 +45,7 @@ export function useUser(): userState { async function createUser(userData: User, selfProcessError: boolean = true): Promise { const endpoint = endpoints.users.index; - await createToast( - 'user', + await create( endpoint, { username: userData.username, diff --git a/frontend/src/views/admin/DockerImagesView.vue b/frontend/src/views/admin/DockerImagesView.vue index bc24007e..2c4c1d67 100644 --- a/frontend/src/views/admin/DockerImagesView.vue +++ b/frontend/src/views/admin/DockerImagesView.vue @@ -15,6 +15,7 @@ import Body from '@/views/layout/Body.vue'; import LazyDataTable from '@/components/admin/LazyDataTable.vue'; import { useDockerImages } from '@/composables/services/docker.service.ts'; import { useFilter } from '@/composables/filters/filter.ts'; +import { useMessagesStore } from '@/store/messages.store.ts'; import { useI18n } from 'vue-i18n'; import { computed, ref } from 'vue'; import { useRoute } from 'vue-router'; @@ -35,6 +36,7 @@ const { deleteDockerImages, } = useDockerImages(); const { filter, onFilter } = useFilter(getDockerImageFilters(query)); +const { addSuccessMessage } = useMessagesStore(); const confirm = useConfirm(); /* State */ @@ -93,7 +95,12 @@ const toggleSafetyGuardEdit = (data: DockerImage): void => { * Changes the public status in the backend of the docker image whose attributes are in editItem's value attribute */ const changePublicStatus = async (): Promise => { - await patchDockerImage(editItem.value); + try { + await patchDockerImage(editItem.value); + addSuccessMessage(t('toasts.messages.success'), t('toasts.messages.edit', { type: t('types.article.docker') })); + } catch (e) { + // TODO error message (when editing public status of docker image) + } }; /** * Show safety guard for removing a docker image from the backend and set up values for when confirmed @@ -135,10 +142,18 @@ const removeItems = async (): Promise => { * @param event Event containing docker image file in files attributes */ const upload = async (event: FileUploadUploaderEvent): Promise => { - const files: File[] = event.files as File[]; - await createDockerImage(addItem.value, files[0]); - addItem.value.name = ''; - selectedOption.value = selectOptions.value[0]; + try { + const files: File[] = event.files as File[]; + await createDockerImage(addItem.value, files[0]); + addSuccessMessage( + t('toasts.messages.success'), + t('toasts.messages.create', { type: t('types.article.docker') }), + ); + addItem.value.name = ''; + selectedOption.value = selectOptions.value[0]; + } catch (e) { + // TODO error message + } }; /** diff --git a/frontend/src/views/admin/UsersView.vue b/frontend/src/views/admin/UsersView.vue index 470d683b..9304da85 100644 --- a/frontend/src/views/admin/UsersView.vue +++ b/frontend/src/views/admin/UsersView.vue @@ -26,7 +26,7 @@ import { useRoute } from 'vue-router'; /* Composable injections */ const { t } = useI18n(); -const { addErrorMessage } = useMessagesStore(); +const { addErrorMessage, addSuccessMessage } = useMessagesStore(); const { query } = useRoute(); const { pagination, users, getUsers, searchUsers, toggleAdmin } = useUser(); const { createStudent, deleteStudent } = useStudents(); @@ -105,34 +105,42 @@ const saveItem = async (): Promise => { const index = value.results.findIndex((row: User) => row.id === editItem.value.id); const paginationItem = value.results[index]; - for (let i = 1; i < roles.length; i++) { - const role = roles[i]; - // determine whether a role is in our original item and whether it's in our update item - // knowing this, we know which roles to destroy in the backend and which to create - const paginationIncludes = paginationItem.roles.includes(role); - const editIncludes = editItem.value.roles.includes(role); - if (!paginationIncludes && editIncludes) { - // add role in backend - const func = creators.value[role]; + try { + for (let i = 1; i < roles.length; i++) { + const role = roles[i]; + // determine whether a role is in our original item and whether it's in our update item + // knowing this, we know which roles to destroy in the backend and which to create + const paginationIncludes = paginationItem.roles.includes(role); + const editIncludes = editItem.value.roles.includes(role); + if (!paginationIncludes && editIncludes) { + // add role in backend + const func = creators.value[role]; - // if the role that needs to be created is a student, a studentId needs to be supplied as well - if (role === 'student') { - const data: Record = { - ...editItem.value, - student_id: editItem.value.id, - }; - await func(data); - } else { - await func(editItem.value); + // if the role that needs to be created is a student, a studentId needs to be supplied as well + if (role === 'student') { + const data: Record = { + ...editItem.value, + student_id: editItem.value.id, + }; + await func(data); + } else { + await func(editItem.value); + } + } else if (paginationIncludes && !editIncludes) { + // remove role in backend + const func = destroyers.value[role]; + await func(editItem.value.id); } - } else if (paginationIncludes && !editIncludes) { - // remove role in backend - const func = destroyers.value[role]; - await func(editItem.value.id); } + // update admin status + await toggleAdmin(editItem.value.id, editItem.value.is_staff); + addSuccessMessage( + t('toasts.messages.success'), + t('toasts.messages.edit', { type: t('types.article.user') }), + ); + } catch (e) { + // TODO error message (failed to edit user) } - // update admin status - await toggleAdmin(editItem.value.id, editItem.value.is_staff); // update locally await dataTable.value.fetch(); } else { diff --git a/frontend/src/views/projects/CreateProjectView.vue b/frontend/src/views/projects/CreateProjectView.vue index 52d296fd..33d904de 100644 --- a/frontend/src/views/projects/CreateProjectView.vue +++ b/frontend/src/views/projects/CreateProjectView.vue @@ -23,7 +23,7 @@ const { params } = useRoute(); /* Service injection */ const { push } = useRouter(); -const { addErrorMessage } = useMessagesStore(); +const { addErrorMessage, addSuccessMessage } = useMessagesStore(); const { course, getCourseByID } = useCourses(); const { project, createProject } = useProject(); const { setStructureChecks } = useStructureCheck(); @@ -47,6 +47,10 @@ async function saveProject(newProject: Project, numberOfGroups: number): Promise if (project.value !== null) { await setStructureChecks(newProject.structure_checks ?? [], project.value.id); await setExtraChecks(newProject.extra_checks ?? [], project.value.id); + addSuccessMessage( + t('toasts.messages.success'), + t('toasts.messages.projects.create.success', [project.value?.name]), + ); await push({ name: 'course-project', params: { courseId: course.value.id, projectId: project.value.id }, diff --git a/frontend/src/views/submissions/SubmissionsView.vue b/frontend/src/views/submissions/SubmissionsView.vue index 1b99f96e..3aa6001d 100644 --- a/frontend/src/views/submissions/SubmissionsView.vue +++ b/frontend/src/views/submissions/SubmissionsView.vue @@ -12,6 +12,7 @@ import { PrimeIcons } from 'primevue/api'; import AllSubmission from '@/components/submissions/AllSubmission.vue'; import { useGroup } from '@/composables/services/group.service.ts'; import { useSubmission } from '@/composables/services/submission.service.ts'; +import { useMessagesStore } from '@/store/messages.store.ts'; const { t } = useI18n(); const route = useRoute(); @@ -19,6 +20,7 @@ const { project, getProjectByID } = useProject(); const { course, getCourseByID } = useCourses(); const { group, getGroupByID } = useGroup(); const { submission, submissions, createSubmission, getSubmissionByGroup } = useSubmission(); +const { addSuccessMessage, addErrorMessage } = useMessagesStore(); /* State */ const files = ref([]); @@ -32,13 +34,18 @@ onMounted(async () => { const onUpload = async (callback: () => void): Promise => { if (group.value !== null) { - console.log(files.value); - await createSubmission(files.value as File[], group.value.id); - if (submission.value != null) { - submissions.value = [...(submissions.value ?? []), submission.value]; + try { + console.log(files.value); + await createSubmission(files.value as File[], group.value.id); + addSuccessMessage(t('toasts.messages.success'), t('toasts.messages.submissions.create.success')); + if (submission.value != null) { + submissions.value = [...(submissions.value ?? []), submission.value]; + } + files.value = []; + callback(); + } catch (e) { + addErrorMessage(t('toasts.messages.error'), t('toasts.messages.submissions.create.error')); } - files.value = []; - callback(); } };