Skip to content

Commit

Permalink
Patch subject (#240)
Browse files Browse the repository at this point in the history
* restructured files + first changes patch

* passed down name and academic year props

* null fix is in create

* fixed router

* title and academic year patch

* added and deleted instructors

* adding + deleting subject instructors

* implementation patch subject mutation + error fix

* fixes to form validation

* adding and removing works

* skeleton for query done

* small academic year fix

* slight cleanup

* create works with patch

* patch logic done

* subjects refactoring modify

* installed dialog component

* i18n

* lint format

* implement patch mutation

* fixes to broken patch page

* fix broken merge conflicts

* knoppen academie jaar subjects fix

* lint format

* fix van de fix van de academie jaar buttons

* changed location of subject files in tests because of refactoring

* add permission middleware

---------

Co-authored-by: Bram Reyniers <[email protected]>
  • Loading branch information
2 people authored and xerbalind committed May 23, 2024
1 parent 11da694 commit b78cd93
Show file tree
Hide file tree
Showing 33 changed files with 493 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
</template>

<script setup lang="ts">
import SubjectInstructorsCard from "@/components/subject/createSubjectView/body/SubjectInstructorsCard.vue";
import SubjectInstructorsCard from "@/components/subject/modify/body/SubjectInstructorsCard.vue";
import type User from "@/models/User";
import UserSearchCard from "@/components/subject/createSubjectView/body/UserSearchCard.vue";
import UserSearchCard from "@/components/subject/modify/body/UserSearchCard.vue";
defineProps<{
currentUser: User;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
</template>

<script setup lang="ts">
import UserSearchList from "@/components/subject/createSubjectView/body/UserSearchList.vue";
import UserSearchList from "@/components/subject/modify/body/UserSearchList.vue";
import type User from "@/models/User";
defineProps<{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<v-card variant="text" class="title-card" width="100%" height="45vh">
<v-card-title class="title">
{{ $t("create_subject.new_subject") }}
{{ title }}
</v-card-title>
<v-card-text>
<v-text-field
Expand Down Expand Up @@ -60,13 +60,17 @@
</template>

<script setup lang="ts">
import { ref, toRefs, watch } from "vue";
import useAcademicYear from "@/composables/useAcademicYear";
import { computed, ref, toRefs, watch } from "vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const props = defineProps<{
title: string;
subjectName: string;
academicYear: number;
subjectMail: string;
currentUserAsInstructor: boolean;
isSubjectNameError: boolean;
isSubjectMailError: boolean;
Expand All @@ -80,11 +84,19 @@ watch(currentUserAsInstructor, (newValue) => {
checkbox.value = newValue;
});
const subjectName = ref("");
const subjectMail = ref("");
const activeAcademicYear = ref<number>(useAcademicYear());
const academicYearItems = [activeAcademicYear.value, activeAcademicYear.value + 1];
const subjectName = ref(props.subjectName);
const activeAcademicYear = ref<number>(props.academicYear);
const subjectMail = ref(props.subjectMail);
const currentAcademicYear = useAcademicYear();
const academicYearItems = computed(() => {
const years = [currentAcademicYear];
if (activeAcademicYear.value !== currentAcademicYear) {
years.push(activeAcademicYear.value);
}
years.push(activeAcademicYear.value + 1);
return years;
});
const rules = {
required: (value: string) => !!value || t("create_subject.field_required"),
length: (value: string) => value.length > 2 || t("create_subject.field_length"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
<template>
<v-row>
<v-col cols="4">
<CreateSubjectHeaderImage :imagePath="imagePath"></CreateSubjectHeaderImage>
<ModifySubjectHeaderImage :imagePath="imagePath"></ModifySubjectHeaderImage>
</v-col>
<v-col cols="8">
<CreateSubjectHeaderCard
<ModifySubjectHeaderCard
:title="title"
:subject-name="subjectName"
:academic-year="academicYear"
:subject-mail="subjectMail"
:current-user-as-instructor="currentUserAsInstructor"
:is-subject-name-error="isSubjectNameError"
:is-subject-mail-error="isSubjectMailError"
Expand All @@ -15,17 +19,21 @@
$emit('update:current-user-as-instructor', $event)
"
>
</CreateSubjectHeaderCard>
</ModifySubjectHeaderCard>
</v-col>
</v-row>
</template>

<script setup lang="ts">
import CreateSubjectHeaderCard from "@/components/subject/createSubjectView/header/CreateSubjectHeaderCard.vue";
import CreateSubjectHeaderImage from "@/components/subject/createSubjectView/header/CreateSubjectHeaderImage.vue";
import ModifySubjectHeaderCard from "@/components/subject/modify/header/ModifySubjectHeaderCard.vue";
import ModifySubjectHeaderImage from "@/components/subject/modify/header/ModifySubjectHeaderImage.vue";
defineProps<{
title: string;
imagePath: string;
subjectName: string;
academicYear: number;
subjectMail: string;
currentUserAsInstructor: boolean;
isSubjectNameError: boolean;
isSubjectMailError: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@
</template>

<script setup lang="ts">
import SubjectProjectsList from "@/components/subject/subjectview/body/projects/list/SubjectProjectsList.vue";
import SubjectProjectsList from "@/components/subject/subject/body/projects/list/SubjectProjectsList.vue";
import type Project from "@/models/Project";
import { FilterOptions } from "@/models/Project";
import { computed, ref, toRefs } from "vue";
import SubjectProjectPage from "@/components/subject/subjectview/body/projects/SubjectProjectPage.vue";
import SubjectProjectPage from "@/components/subject/subject/body/projects/SubjectProjectPage.vue";
const props = defineProps<{
projects: Project[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
</template>

<script setup lang="ts">
import SubjectTab from "@/components/subject/subjectview/body/projects/list/SubjectTab.vue";
import SubjectTab from "@/components/subject/subject/body/projects/list/SubjectTab.vue";
import type Project from "@/models/Project";
import { FilterOptions } from "@/models/Project";
import { ref, watch } from "vue";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@
</v-card-text>
</v-col>
<v-col v-if="isAdmin || (isInstructor && isTeacher)" cols="1">
<router-link to="" class="link">
<router-link
:to="{ name: 'edit-subject', params: { subjectId: subjectId } }"
class="link"
>
<v-icon size="large">mdi-square-edit-outline</v-icon>
</router-link>
</v-col>
Expand All @@ -57,6 +60,7 @@ import SubjectIcon from "@/components/subject/extra/SubjectIcon.vue";
import { SubjectRole } from "@/models/Subject";
defineProps<{
subjectId: number;
title: string;
academicYear: number;
instructors: User[];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<SubjectHeaderCard
:title="title"
:subject-id="subjectId"
:academic-year="academicYear"
:instructors="instructors"
:isInstructor="isInstructor"
Expand All @@ -10,13 +11,14 @@
</template>

<script setup lang="ts">
import SubjectHeaderCard from "@/components/subject/subjectview/header/SubjectHeaderCard.vue";
import SubjectHeaderCard from "@/components/subject/subject/header/SubjectHeaderCard.vue";
import type User from "@/models/User";
import { SubjectRole } from "@/models/Subject";
import { computed, toRefs } from "vue";
const props = defineProps<{
title: string;
subjectId: number;
academicYear: number;
instructors: User[];
imagePath: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
color="secondary"
variant="flat"
>
{{ `${academicYear}-${academicYear + 1}` }}
{{ `${academicYear - 2000}-${academicYear - 2000 + 1}` }}
</v-chip>
</v-chip-group>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</template>

<script setup lang="ts">
import SubjectsHeaderCard from "@/components/subject/subjectsview/header/SubjectsHeaderCard.vue";
import SubjectsHeaderCard from "@/components/subject/subjects/header/SubjectsHeaderCard.vue";
import type { SubjectDetails, SubjectFilter } from "@/models/Subject";
defineProps<{
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ export default {
email_invalid: "Provided email is invalid",
},

patch_subject: {
cancel_dialog: "Cancel editing subject?",
edit_subject: "Edit subject",
},

group: {
not_found: "Group not found",
not_found2: "No groups found",
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/i18n/locales/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ export default {
email_invalid: "Ingevoerde email is ongeldig",
},

patch_subject: {
cancel_dialog: "Wijzigingen annuleren?",
edit_subject: "Wijzig vak",
},

group: {
not_found: "Groep niet gevonden",
not_found2: "Geen groepen teruggevonden",
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/models/Subject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface SubjectDetails {
role: SubjectRole;
}

export default interface SubjectForm {
export interface SubjectForm {
name: string;
email: string;
academic_year: number;
Expand Down
62 changes: 61 additions & 1 deletion frontend/src/queries/Subject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import {
getSubjectUuid,
createSubject,
createSubjectInstructor,
updateSubject,
deleteSubjectInstructor,
} from "@/services/subject";
import { getSubjectProjects } from "@/services/project";
import type User from "@/models/User";
import type Subject from "@/models/Subject";
import type { UserSubjectList } from "@/models/Subject";
import type Project from "@/models/Project";
import type SubjectForm from "@/models/Subject";
import type { SubjectForm } from "@/models/Subject";

function SUBJECT_QUERY_KEY(subjectId: number | string): (string | number)[] {
return ["subject", subjectId];
Expand Down Expand Up @@ -177,6 +179,31 @@ export function useCreateSubjectMutation(): UseMutationReturnType<
});
}

export function useUpdateSubjectMutation(): UseMutationReturnType<
void,
Error,
{ subjectId: number; subject: SubjectForm },
Subject
> {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ subjectId, subject }) => updateSubject(subjectId, subject),
onMutate: ({ subjectId, subject }) => {
const previousSubject = queryClient.getQueryData<Subject>(SUBJECT_QUERY_KEY(subjectId));
queryClient.cancelQueries({ queryKey: SUBJECT_QUERY_KEY(subjectId) });
queryClient.setQueryData(SUBJECT_QUERY_KEY(subjectId), subject);
return previousSubject;
},
onSuccess: (_, { subjectId }) => {
queryClient.invalidateQueries({ queryKey: SUBJECT_QUERY_KEY(subjectId) });
},
onError: (_, { subjectId }, previousSubject) => {
queryClient.setQueryData(SUBJECT_QUERY_KEY(subjectId), previousSubject);
alert("Could not update subject. Please try again.");
},
});
}

/**
* Mutation composable for creating subject instructor
*/
Expand Down Expand Up @@ -214,3 +241,36 @@ export function useCreateSubjectInstructorMutation(): UseMutationReturnType<
},
});
}

export function useDeleteSubjectInstructorMutation(): UseMutationReturnType<
void,
Error,
{ user: User; subjectId: number },
{ previousUsers: User[] }
> {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ user, subjectId }) => deleteSubjectInstructor(subjectId, user.uid),
onMutate: ({ user, subjectId }) => {
const previousUsers = queryClient.getQueryData<User[]>(
SUBJECT_INSTRUCTORS_QUERY_KEY(subjectId)
);
queryClient.cancelQueries({ queryKey: SUBJECT_INSTRUCTORS_QUERY_KEY(subjectId) });
const newUsers = previousUsers ? previousUsers.filter((u) => u.uid !== user.uid) : [];
queryClient.setQueryData(SUBJECT_INSTRUCTORS_QUERY_KEY(subjectId), newUsers);
return { previousUsers: previousUsers || [] };
},
onSuccess: (_, { subjectId }) => {
queryClient.invalidateQueries({
queryKey: SUBJECT_INSTRUCTORS_QUERY_KEY(subjectId),
});
},
onError: (_, { subjectId }, ctx) => {
queryClient.setQueryData<User[]>(
SUBJECT_INSTRUCTORS_QUERY_KEY(subjectId),
() => ctx!.previousUsers!
);
alert("Could not delete subject instructor. Please try again.");
},
});
}
16 changes: 14 additions & 2 deletions frontend/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import useCanVisit, {
useOrCondition,
useIsInGroupOfProjectCondition,
useIsTeacherCondition,
useAndCondition,
} from "./middleware/canVisit";

declare module "vue-router" {
Expand Down Expand Up @@ -123,11 +124,22 @@ const router = createRouter({
{
path: "/subjects/create",
name: "create-subject",
component: () => import("../views/subject/CreateSubjectView.vue"),
component: () => import("../views/subject/modify/CreateSubjectView.vue"),
meta: {
middleware: useCanVisit(useOrCondition(useIsAdminCondition, useIsTeacherCondition)),
},
},
{
path: "/subjects/:subjectId(\\d+)/edit",
name: "edit-subject",
component: () => import("../views/subject/modify/PatchSubjectView.vue"),
props: (route) => ({ subjectId: Number(route.params.subjectId) }),
meta: {
middleware: useCanVisit(
useAndCondition(useIsTeacherCondition, useIsInstructorOfSubjectCondition)
),
},
},
{
path: "/subjects/:subjectId(\\d+)",
name: "subject",
Expand Down Expand Up @@ -205,7 +217,7 @@ router.beforeEach(async (to, from, next) => {
}

let new_next = next;
for (let middleware of middlewares) {
for (const middleware of middlewares) {
const context: MiddlewareContext = { to, from, next: new_next, router };
const { next: returned_next, final } = await middleware(context);
if (final) {
Expand Down
Loading

0 comments on commit b78cd93

Please sign in to comment.