feat(frontend): groups in assignments ophalen
This commit is contained in:
		
							parent
							
								
									45cb020861
								
							
						
					
					
						commit
						d6009ff878
					
				
					 8 changed files with 182 additions and 126 deletions
				
			
		|  | @ -7,7 +7,7 @@ | |||
| } | ||||
| 
 | ||||
| .assignment-card { | ||||
|     width: 85%; | ||||
|     width: 80%; | ||||
|     padding: 2%; | ||||
|     border-radius: 12px; | ||||
| } | ||||
|  |  | |||
|  | @ -23,28 +23,12 @@ const {t, locale} = useI18n(); | |||
| const role = ref(auth.authState.activeRole); | ||||
| const username = ref<string>(""); | ||||
| 
 | ||||
| async function submitForm(assignmentTitle: string, | ||||
|                           selectedLearningPath: string, | ||||
|                           selectedClass: string, | ||||
|                           groups: string[][], | ||||
|                           deadline: string, | ||||
|                           description: string, | ||||
|                           currentLanguage: string): Promise<void> { | ||||
|     const assignmentDTO: AssignmentDTO = { | ||||
|         id: 0, | ||||
|         class: selectedClass, | ||||
|         title: assignmentTitle, | ||||
|         description: description, | ||||
|         learningPath: selectedLearningPath, | ||||
|         language: currentLanguage, | ||||
|         groups: groups, | ||||
|         //deadline: deadline, | ||||
|     }; | ||||
| 
 | ||||
| async function submitForm(assignmentDTO: AssignmentDTO): Promise<void> { | ||||
|     //TODO: replace with query function | ||||
|     const controller: AssignmentController = new AssignmentController(selectedClass); | ||||
|     const controller: AssignmentController = new AssignmentController(assignmentDTO.class); | ||||
|     await controller.createAssignment(assignmentDTO); | ||||
| 
 | ||||
|     // Navigate back to all assignments | ||||
|     await router.push('/user/assignment'); | ||||
| } | ||||
| 
 | ||||
|  | @ -79,22 +63,30 @@ const description = ref(''); | |||
| const groups = ref<string[][]>([]); | ||||
| 
 | ||||
| // New group is added to the list | ||||
| const addGroupToList = (students: string[]) => { | ||||
| function addGroupToList(students: string[]): void { | ||||
|     if (students.length) { | ||||
|         groups.value = [...groups.value, students]; | ||||
|     } | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| watch(selectedClass, () => { | ||||
|     groups.value = []; | ||||
| }); | ||||
| 
 | ||||
| const submitFormHandler = async () => { | ||||
| async function submitFormHandler(): Promise<void> { | ||||
|     const {valid} = await form.value.validate(); | ||||
|     // Don't submit the form if all rules don't apply | ||||
|     if (!valid) return; | ||||
|     await submitForm(assignmentTitle.value, selectedLearningPath.value?.hruid, selectedClass.value.id, groups.value, deadline.value, description.value, locale.value); | ||||
| }; | ||||
|     const assignmentDTO: AssignmentDTO = { | ||||
|         id: 0, | ||||
|         class: selectedClass.value?.id || "", | ||||
|         title: assignmentTitle.value, | ||||
|         description: description.value, | ||||
|         learningPath: selectedLearningPath.value?.hruid || "", | ||||
|         language: language.value | ||||
|     } | ||||
|     await submitForm(assignmentDTO); | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ import type {StudentsResponse} from "@/controllers/students.ts"; | |||
| 
 | ||||
| const props = defineProps<{ | ||||
|     classId: string | undefined | ||||
|     groups: string[][], // All groups | ||||
|     groups: string[][], | ||||
| }>(); | ||||
| const emit = defineEmits(['groupCreated']); | ||||
| const {t} = useI18n(); | ||||
|  | @ -16,7 +16,6 @@ const selectedStudents = ref([]); | |||
| 
 | ||||
| const studentQueryResult = useClassStudentsQuery(() => props.classId, true); | ||||
| 
 | ||||
| 
 | ||||
| function filterStudents(data: StudentsResponse): { title: string, value: string }[] { | ||||
|     const students = data.students; | ||||
|     const studentsInGroups = props.groups.flat(); | ||||
|  |  | |||
|  | @ -1,8 +1,9 @@ | |||
| import { computed, toValue } from "vue"; | ||||
| import {computed, type Ref, toValue} from "vue"; | ||||
| import type {MaybeRefOrGetter} from "vue"; | ||||
| import { | ||||
|     type QueryObserverResult, | ||||
|     useMutation, | ||||
|     type UseMutationReturnType, | ||||
|     type UseMutationReturnType, useQueries, | ||||
|     useQuery, | ||||
|     useQueryClient, | ||||
|     type UseQueryReturnType, | ||||
|  | @ -27,27 +28,35 @@ const studentController = new StudentController(); | |||
| function studentsQueryKey(full: boolean): [string, boolean] { | ||||
|     return ["students", full]; | ||||
| } | ||||
| 
 | ||||
| function studentQueryKey(username: string): [string, string] { | ||||
|     return ["student", username]; | ||||
| } | ||||
| 
 | ||||
| function studentClassesQueryKey(username: string, full: boolean): [string, string, boolean] { | ||||
|     return ["student-classes", username, full]; | ||||
| } | ||||
| 
 | ||||
| function studentAssignmentsQueryKey(username: string, full: boolean): [string, string, boolean] { | ||||
|     return ["student-assignments", username, full]; | ||||
| } | ||||
| 
 | ||||
| function studentGroupsQueryKeys(username: string, full: boolean): [string, string, boolean] { | ||||
|     return ["student-groups", username, full]; | ||||
| } | ||||
| 
 | ||||
| function studentSubmissionsQueryKey(username: string): [string, string] { | ||||
|     return ["student-submissions", username]; | ||||
| } | ||||
| 
 | ||||
| function studentQuestionsQueryKey(username: string, full: boolean): [string, string, boolean] { | ||||
|     return ["student-questions", username, full]; | ||||
| } | ||||
| 
 | ||||
| export function studentJoinRequestsQueryKey(username: string): [string, string] { | ||||
|     return ["student-join-requests", username]; | ||||
| } | ||||
| 
 | ||||
| export function studentJoinRequestQueryKey(username: string, classId: string): [string, string, string] { | ||||
|     return ["student-join-request", username, classId]; | ||||
| } | ||||
|  | @ -69,6 +78,21 @@ export function useStudentQuery( | |||
|     }); | ||||
| } | ||||
| 
 | ||||
| export function useStudentsByUsernamesQuery( | ||||
|     usernames: MaybeRefOrGetter<string[] | undefined> | ||||
| ): Ref<QueryObserverResult<StudentResponse>[]> { | ||||
|     const resolvedUsernames = toValue(usernames) ?? []; | ||||
| 
 | ||||
|     return useQueries({ | ||||
|         queries: resolvedUsernames?.map((username) => ({ | ||||
|             queryKey: computed(() => studentQueryKey(toValue(username))), | ||||
|             queryFn: async () => studentController.getByUsername(toValue(username)), | ||||
|             enabled: Boolean(toValue(username)), | ||||
|         })), | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| export function useStudentClassesQuery( | ||||
|     username: MaybeRefOrGetter<string | undefined>, | ||||
|     full: MaybeRefOrGetter<boolean> = true, | ||||
|  |  | |||
|  | @ -1,18 +1,48 @@ | |||
| <script setup lang="ts"> | ||||
| 
 | ||||
| import auth from "@/services/auth/auth-service.ts"; | ||||
| import {computed} from "vue"; | ||||
| import {computed, ref} from "vue"; | ||||
| import StudentAssignment from "@/views/assignments/StudentAssignment.vue"; | ||||
| import TeacherAssignment from "@/views/assignments/TeacherAssignment.vue"; | ||||
| import {asyncComputed} from "@vueuse/core"; | ||||
| import {GroupController} from "@/controllers/groups.ts"; | ||||
| import {useRoute} from "vue-router"; | ||||
| 
 | ||||
| const role = auth.authState.activeRole; | ||||
| const isTeacher = computed(() => role === 'teacher'); | ||||
| 
 | ||||
| // Get the user's username/id | ||||
| const username = asyncComputed(async () => { | ||||
|     const user = await auth.loadUser(); | ||||
|     return user?.profile?.preferred_username ?? undefined | ||||
| }); | ||||
| 
 | ||||
| const route = useRoute(); | ||||
| const assignmentId = ref(Number(route.params.id)); | ||||
| const classId = window.history.state?.class_id; | ||||
| 
 | ||||
| const groupController = new GroupController(classId, assignmentId.value); | ||||
| 
 | ||||
| const groupDTOs = asyncComputed(async () => await groupController.getAll(true)); | ||||
| console.log(groupDTOs.value); | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|     <StudentAssignment v-if="!isTeacher"></StudentAssignment> | ||||
|     <TeacherAssignment v-else></TeacherAssignment> | ||||
|     <TeacherAssignment | ||||
|         :class-id="classId" | ||||
|         :assignment-id="assignmentId" | ||||
|         :groups="groupDTOs" | ||||
|         v-if="isTeacher" | ||||
|     > | ||||
|     </TeacherAssignment> | ||||
|     <StudentAssignment | ||||
|         :class-id="classId" | ||||
|         :assignment-id="assignmentId" | ||||
|         :groups="groupDTOs" | ||||
|         v-else | ||||
|     > | ||||
|     </StudentAssignment> | ||||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
|  |  | |||
|  | @ -1,21 +1,29 @@ | |||
| <script setup lang="ts"> | ||||
| import { useRoute } from "vue-router"; | ||||
| import {ref, computed} from "vue"; | ||||
| import {ref, computed, defineProps} from "vue"; | ||||
| import auth from "@/services/auth/auth-service.ts"; | ||||
| import {useI18n} from "vue-i18n"; | ||||
| import {AssignmentController} from "@/controllers/assignments.ts"; | ||||
| import {useAssignmentQuery} from "@/queries/assignments.ts"; | ||||
| import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||
| import type {AssignmentResponse} from "@/controllers/assignments.ts"; | ||||
| import type {GroupDTO} from "@dwengo-1/common/interfaces/group"; | ||||
| import {asyncComputed} from "@vueuse/core"; | ||||
| import {useStudentsByUsernamesQuery} from "@/queries/students.ts"; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
|     classId: string | ||||
|     assignmentId: number | ||||
|     groups: GroupDTO[] | undefined | ||||
| }>(); | ||||
| 
 | ||||
| const {t, locale} = useI18n(); | ||||
| const language = computed(() => locale.value); | ||||
| const route = useRoute(); | ||||
| const assignmentId = ref(Number(route.params.id)); | ||||
| const classId = window.history.state?.class_id; | ||||
| const controller = new AssignmentController(classId); | ||||
| // Get the user's username/id | ||||
| const username = asyncComputed(async () => { | ||||
|     const user = await auth.loadUser(); | ||||
|     return user?.profile?.preferred_username ?? undefined | ||||
| }); | ||||
| 
 | ||||
| const assignment = asyncComputed(async () => { | ||||
|     return await controller.getByNumber(assignmentId.value) | ||||
| }, null); | ||||
| const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); | ||||
| 
 | ||||
| const submitted = ref(true);//TODO: update by fetching submissions and check group | ||||
| 
 | ||||
|  | @ -23,23 +31,24 @@ const submitAssignment = async () => { | |||
|     //TODO | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /*** | ||||
|  // Display group members | ||||
|  const myGroup = computed(() => { | ||||
|  if (!assignment.value || !assignment.value.groups) return null; | ||||
|  console.log(assignment.value.groups) | ||||
|  return assignment.value.groups.find(group => | ||||
|  group.members.some(m => m.username === myUsername) | ||||
| const group = computed(() => { | ||||
|     return props?.groups?.find(group => | ||||
|         group.members.some(m => m.username === username.value) | ||||
|     ); | ||||
| }); | ||||
|  */ | ||||
| 
 | ||||
| // Assuming group.value.members is a list of usernames | ||||
| const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as string[]); | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|     <div class="container"> | ||||
|         <v-card v-if="assignment" class="assignment-card"> | ||||
|         <using-query-result | ||||
|             :query-result="assignmentQueryResult" | ||||
|             v-slot="{ data }: {data: AssignmentResponse}" | ||||
|         > | ||||
|             <v-card v-if="data" class="assignment-card"> | ||||
|                 <div class="top-buttons"> | ||||
|                     <v-btn | ||||
|                         icon | ||||
|  | @ -51,6 +60,7 @@ const submitAssignment = async () => { | |||
|                     </v-btn> | ||||
| 
 | ||||
|                     <v-chip | ||||
|                         v-if="submitted" | ||||
|                         class="ma-2 top-right-btn" | ||||
|                         label | ||||
|                         color="success" | ||||
|  | @ -58,10 +68,10 @@ const submitAssignment = async () => { | |||
|                         {{ t("submitted") }} | ||||
|                     </v-chip> | ||||
|                 </div> | ||||
|             <v-card-title class="text-h4">{{ assignment.title }}</v-card-title> | ||||
|                 <v-card-title class="text-h4">{{ data.title }}</v-card-title> | ||||
|                 <v-card-subtitle class="subtitle-section"> | ||||
|                     <v-btn | ||||
|                     :to="`/learningPath/${language}/${assignment.learningPath}`" | ||||
|                         :to="`/learningPath/${language}/${data.learningPath}`" | ||||
|                         variant="tonal" | ||||
|                         color="primary" | ||||
|                     > | ||||
|  | @ -70,22 +80,19 @@ const submitAssignment = async () => { | |||
|                 </v-card-subtitle> | ||||
| 
 | ||||
|                 <v-card-text class="description"> | ||||
|                 {{ assignment.description }} | ||||
|                     {{ data.description }} | ||||
|                 </v-card-text> | ||||
| 
 | ||||
|                 <v-card-text class="group-section"> | ||||
|                     <h3>{{ t("group") }}</h3> | ||||
| 
 | ||||
|                 <!-- Student view | ||||
|                 <div v-if="!isTeacher"> | ||||
|                     <div v-if="myGroup"> | ||||
|                     <pre>{{ props.groups }}</pre> | ||||
|                     <div v-if="studentQueries"> | ||||
|                         <ul> | ||||
|                             <li v-for="student in myGroup.members" :key="student.username"> | ||||
|                                 {{ student.firstName + ' ' + student.lastName}} | ||||
|                             <li v-for="student in studentQueries" :key="student.data?.student.id"> | ||||
|                                 {{ student.data?.student.firstName + ' ' + student.data?.student.lastName }} | ||||
|                             </li> | ||||
|                         </ul> | ||||
|                     </div> | ||||
|                 </div>--> | ||||
| 
 | ||||
|                 </v-card-text> | ||||
|                 <v-card-actions class="justify-end"> | ||||
|  | @ -100,6 +107,7 @@ const submitAssignment = async () => { | |||
|                 </v-card-actions> | ||||
| 
 | ||||
|             </v-card> | ||||
|         </using-query-result> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,20 +1,22 @@ | |||
| <script setup lang="ts"> | ||||
| import {useRoute} from "vue-router"; | ||||
| import {ref, computed} from "vue"; | ||||
| import {computed, defineProps} from "vue"; | ||||
| import {useI18n} from "vue-i18n"; | ||||
| import {AssignmentController, type AssignmentResponse} from "@/controllers/assignments.ts"; | ||||
| import {useAssignmentQuery} from "@/queries/assignments.ts"; | ||||
| import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||
| import type {GroupDTO} from "@dwengo-1/common/interfaces/group"; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
|     classId: string | ||||
|     assignmentId: number | ||||
|     groups: GroupDTO[] | undefined | ||||
| }>(); | ||||
| 
 | ||||
| const {t, locale} = useI18n(); | ||||
| const language = computed(() => locale.value); | ||||
| const route = useRoute(); | ||||
| const assignmentId = ref(Number(route.params.id)); | ||||
| const classId = window.history.state?.class_id; | ||||
| const controller = new AssignmentController(classId); | ||||
| const controller = new AssignmentController(props.classId); | ||||
| 
 | ||||
| const assignmentQueryResult = useAssignmentQuery(() => classId, assignmentId); | ||||
| const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); | ||||
| 
 | ||||
| /*** | ||||
|  // Display group members | ||||
|  | @ -28,7 +30,7 @@ const assignmentQueryResult = useAssignmentQuery(() => classId, assignmentId); | |||
|  */ | ||||
| 
 | ||||
| const deleteAssignment = async () => { | ||||
|     await controller.deleteAssignment(assignmentId.value); | ||||
|     await controller.deleteAssignment(props.assignmentId.value); | ||||
| }; | ||||
| 
 | ||||
| </script> | ||||
|  |  | |||
|  | @ -30,6 +30,7 @@ if (isTeacher.value) { | |||
| //TODO: replace with query from classes | ||||
| const classController = new ClassController(); | ||||
| 
 | ||||
| //TODO: replace by query that fetches all user's assignment | ||||
| const assignments = asyncComputed(async () => { | ||||
|     const classes = classesQueryResults?.data?.value?.classes; | ||||
|     if (!classes) return []; | ||||
|  |  | |||
		Reference in a new issue
	
	 Joyelle Ndagijimana
						Joyelle Ndagijimana