feat: nieuwe lijstview voor assignment
This commit is contained in:
		
							parent
							
								
									c03669eda7
								
							
						
					
					
						commit
						7e4e179121
					
				
					 1 changed files with 178 additions and 156 deletions
				
			
		|  | @ -1,188 +1,210 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import { ref, computed, onMounted, watch } from "vue"; | import { ref, computed, onMounted, watch } from "vue"; | ||||||
|     import { useI18n } from "vue-i18n"; | import { useI18n } from "vue-i18n"; | ||||||
|     import { useRouter } from "vue-router"; | import { useRouter } from "vue-router"; | ||||||
|     import auth from "@/services/auth/auth-service.ts"; | import authState from "@/services/auth/auth-service.ts"; | ||||||
|     import { useTeacherClassesQuery } from "@/queries/teachers.ts"; | import auth from "@/services/auth/auth-service.ts"; | ||||||
|     import { useStudentClassesQuery } from "@/queries/students.ts"; | import { useTeacherAssignmentsQuery, useTeacherClassesQuery } from "@/queries/teachers.ts"; | ||||||
|     import { ClassController } from "@/controllers/classes.ts"; | import { useStudentAssignmentsQuery, useStudentClassesQuery } from "@/queries/students.ts"; | ||||||
|     import type { ClassDTO } from "@dwengo-1/common/interfaces/class"; | import { ClassController } from "@/controllers/classes.ts"; | ||||||
|     import { asyncComputed } from "@vueuse/core"; | import type { ClassDTO } from "@dwengo-1/common/interfaces/class"; | ||||||
|     import { useDeleteAssignmentMutation } from "@/queries/assignments.ts"; | import { asyncComputed } from "@vueuse/core"; | ||||||
|  | import { useDeleteAssignmentMutation } from "@/queries/assignments.ts"; | ||||||
|  | import type { AssignmentsResponse } from "@/controllers/assignments"; | ||||||
|  | import type { AssignmentDTO } from "@dwengo-1/common/interfaces/assignment"; | ||||||
|  | import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||||
| 
 | 
 | ||||||
|     const { t } = useI18n(); | const { t } = useI18n(); | ||||||
|     const router = useRouter(); | const router = useRouter(); | ||||||
| 
 | 
 | ||||||
|     const role = ref(auth.authState.activeRole); | const role = ref(auth.authState.activeRole); | ||||||
|     const username = ref<string>(""); | const username = ref<string | undefined>(undefined); | ||||||
|  | const isLoading = ref(false); | ||||||
|  | const isError = ref(false); | ||||||
|  | const errorMessage = ref<string>(""); | ||||||
| 
 | 
 | ||||||
|     const isTeacher = computed(() => role.value === "teacher"); | // Load current user before rendering the page | ||||||
| 
 | onMounted(async () => { | ||||||
|     // Fetch and store all the teacher's classes |     isLoading.value = true; | ||||||
|     let classesQueryResults = undefined; |     try { | ||||||
| 
 |         const userObject = await authState.loadUser(); | ||||||
|     if (isTeacher.value) { |         username.value = userObject!.profile.preferred_username; | ||||||
|         classesQueryResults = useTeacherClassesQuery(username, true); |     } catch (error) { | ||||||
|     } else { |         isError.value = true; | ||||||
|         classesQueryResults = useStudentClassesQuery(username, true); |         errorMessage.value = error instanceof Error ? error.message : String(error); | ||||||
|  |     } finally { | ||||||
|  |         isLoading.value = false; | ||||||
|     } |     } | ||||||
|  | }); | ||||||
| 
 | 
 | ||||||
|     //TODO: remove later | const isTeacher = computed(() => role.value === "teacher"); | ||||||
|     const classController = new ClassController(); |  | ||||||
| 
 | 
 | ||||||
|     //TODO: replace by query that fetches all user's assignment | const assignmentsQuery = isTeacher ? useTeacherAssignmentsQuery(username, true) : useStudentAssignmentsQuery(username, true); | ||||||
|     const assignments = asyncComputed(async () => { |  | ||||||
|         const classes = classesQueryResults?.data?.value?.classes; |  | ||||||
|         if (!classes) return []; |  | ||||||
|         const result = await Promise.all( |  | ||||||
|             (classes as ClassDTO[]).map(async (cls) => { |  | ||||||
|                 const { assignments } = await classController.getAssignments(cls.id); |  | ||||||
|                 return assignments.map((a) => ({ |  | ||||||
|                     id: a.id, |  | ||||||
|                     class: cls, |  | ||||||
|                     title: a.title, |  | ||||||
|                     description: a.description, |  | ||||||
|                     learningPath: a.learningPath, |  | ||||||
|                     language: a.language, |  | ||||||
|                     groups: a.groups, |  | ||||||
|                 })); |  | ||||||
|             }), |  | ||||||
|         ); |  | ||||||
| 
 | 
 | ||||||
|         return result.flat(); | async function goToCreateAssignment(): Promise<void> { | ||||||
|     }, []); |     await router.push("/assignment/create"); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     async function goToCreateAssignment(): Promise<void> { | async function goToAssignmentDetails(id: number, clsId: string): Promise<void> { | ||||||
|         await router.push("/assignment/create"); |     await router.push(`/assignment/${clsId}/${id}`); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const { mutate, data, isSuccess } = useDeleteAssignmentMutation(); | ||||||
|  | 
 | ||||||
|  | watch([isSuccess, data], async ([success, oldData]) => { | ||||||
|  |     if (success && oldData?.assignment) { | ||||||
|  |         window.location.reload(); | ||||||
|     } |     } | ||||||
|  | }); | ||||||
| 
 | 
 | ||||||
|     async function goToAssignmentDetails(id: number, clsId: string): Promise<void> { | async function goToDeleteAssignment(num: number, clsId: string): Promise<void> { | ||||||
|         await router.push(`/assignment/${clsId}/${id}`); |     mutate({ cid: clsId, an: num }); | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     const { mutate, data, isSuccess } = useDeleteAssignmentMutation(); | onMounted(async () => { | ||||||
| 
 |     const user = await auth.loadUser(); | ||||||
|     watch([isSuccess, data], async ([success, oldData]) => { |     username.value = user?.profile?.preferred_username ?? ""; | ||||||
|         if (success && oldData?.assignment) { | }); | ||||||
|             window.location.reload(); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     async function goToDeleteAssignment(num: number, clsId: string): Promise<void> { |  | ||||||
|         mutate({ cid: clsId, an: num }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     onMounted(async () => { |  | ||||||
|         const user = await auth.loadUser(); |  | ||||||
|         username.value = user?.profile?.preferred_username ?? ""; |  | ||||||
|     }); |  | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|     <div class="assignments-container"> |     <main> | ||||||
|         <h1>{{ t("assignments") }}</h1> |         <h1>{{ t("assignments") }}</h1> | ||||||
|  |         <div class="loading-div" v-if="isLoading"> | ||||||
|  |             <v-progress-circular indeterminate></v-progress-circular> | ||||||
|  |         </div> | ||||||
|  |         <div v-if="isError"> | ||||||
|  |             <v-empty-state icon="mdi-alert-circle-outline" :text="errorMessage" | ||||||
|  |                 :title="t('error_title')"></v-empty-state> | ||||||
|  |         </div> | ||||||
|  |         <div v-else> | ||||||
|  |             <using-query-result :query-result="assignmentsQuery" | ||||||
|  |                 v-slot="assignmentsResponse: { data: AssignmentsResponse }"> | ||||||
|  |                 <v-btn v-if="isTeacher" color="primary" class="mb-4 center-btn" @click="goToCreateAssignment"> | ||||||
|  |                     {{ t("new-assignment") }} | ||||||
|  |                 </v-btn> | ||||||
|  |                 <v-container> | ||||||
|  |                     <v-table class="table"> | ||||||
|  |                         <thead> | ||||||
|  |                             <tr> | ||||||
|  |                                 <th class="header">{{ t("assignments") }}</th> | ||||||
|  |                                 <th class="header"> | ||||||
|  |                                     {{ t("class") }} | ||||||
|  |                                 </th> | ||||||
|  |                                 <th class="header">{{ t("groups") }}</th> | ||||||
|  |                             </tr> | ||||||
|  |                         </thead> | ||||||
|  |                         <tbody> | ||||||
|  |                             <tr v-for="a in assignmentsResponse.data.assignments as AssignmentDTO[]" | ||||||
|  |                                 :key="a.id + a.within"> | ||||||
|  |                                 <td> | ||||||
|  |                                     <v-btn :to="`/class/${a.within}`" variant="text"> | ||||||
|  |                                         {{ a.title }} | ||||||
|  |                                         <v-icon end> mdi-menu-right </v-icon> | ||||||
|  |                                     </v-btn> | ||||||
|  |                                 </td> | ||||||
|  |                                 <td> | ||||||
|  |                                     <span>{{ a.within }}</span> | ||||||
|  |                                     <!-- <span v-if="!isMdAndDown">{{ c.id }}</span> | ||||||
|  |                                     <span v-else style="cursor: pointer" @click="openCodeDialog(c.id)"><v-icon | ||||||
|  |                                             icon="mdi-eye"></v-icon></span> --> | ||||||
|  |                                 </td> | ||||||
| 
 | 
 | ||||||
|         <v-btn |                                 <td>{{ a.groups.length }}</td> | ||||||
|             v-if="isTeacher" |                             </tr> | ||||||
|             color="primary" |                         </tbody> | ||||||
|             class="mb-4 center-btn" |                     </v-table> | ||||||
|             @click="goToCreateAssignment" |                 </v-container> | ||||||
|         > |             </using-query-result> | ||||||
|             {{ t("new-assignment") }} |         </div> | ||||||
|         </v-btn> |             <div class="assignments-container"> | ||||||
|  |                 <using-query-result :query-result="assignmentsQuery" | ||||||
|  |                     v-slot="assignmentsResponse: { data: AssignmentsResponse }"> | ||||||
|  |                     <v-container> | ||||||
|  |                         <v-row> | ||||||
|  |                             <v-col v-for="assignment in assignmentsResponse.data.assignments as AssignmentDTO[]" | ||||||
|  |                                 :key="assignment.id + assignment.within" cols="12"> | ||||||
|  |                                 <v-card class="assignment-card"> | ||||||
|  |                                     <div class="top-content"> | ||||||
|  |                                         <div class="assignment-title">{{ assignment.title }}</div> | ||||||
|  |                                         <div class="assignment-class"> | ||||||
|  |                                             {{ t("class") }}: | ||||||
|  |                                             <span class="class-name"> | ||||||
|  |                                                 {{ assignment.within }} | ||||||
|  |                                             </span> | ||||||
|  |                                         </div> | ||||||
|  |                                     </div> | ||||||
| 
 | 
 | ||||||
|         <v-container> |                                     <div class="spacer"></div> | ||||||
|             <v-row> |  | ||||||
|                 <v-col |  | ||||||
|                     v-for="assignment in assignments" |  | ||||||
|                     :key="assignment.id" |  | ||||||
|                     cols="12" |  | ||||||
|                 > |  | ||||||
|                     <v-card class="assignment-card"> |  | ||||||
|                         <div class="top-content"> |  | ||||||
|                             <div class="assignment-title">{{ assignment.title }}</div> |  | ||||||
|                             <div class="assignment-class"> |  | ||||||
|                                 {{ t("class") }}: |  | ||||||
|                                 <span class="class-name"> |  | ||||||
|                                     {{ assignment.class.displayName }} |  | ||||||
|                                 </span> |  | ||||||
|                             </div> |  | ||||||
|                         </div> |  | ||||||
| 
 | 
 | ||||||
|                         <div class="spacer"></div> |                                     <div class="button-row"> | ||||||
|  |                                         <v-btn color="primary" variant="text" | ||||||
|  |                                             @click="goToAssignmentDetails(assignment.id, assignment.within)"> | ||||||
|  |                                             {{ t("view-assignment") }} | ||||||
|  |                                         </v-btn> | ||||||
|  |                                         <v-btn v-if="isTeacher" color="red" variant="text" | ||||||
|  |                                             @click="goToDeleteAssignment(assignment.id, assignment.within)"> | ||||||
|  |                                             {{ t("delete") }} | ||||||
|  |                                         </v-btn> | ||||||
|  |                                     </div> | ||||||
|  |                                 </v-card> | ||||||
|  |                             </v-col> | ||||||
|  |                         </v-row> | ||||||
|  |                     </v-container> | ||||||
|  |                 </using-query-result> | ||||||
|  |             </div> | ||||||
|  |     </main> | ||||||
| 
 | 
 | ||||||
|                         <div class="button-row"> |  | ||||||
|                             <v-btn |  | ||||||
|                                 color="primary" |  | ||||||
|                                 variant="text" |  | ||||||
|                                 @click="goToAssignmentDetails(assignment.id, assignment.class.id)" |  | ||||||
|                             > |  | ||||||
|                                 {{ t("view-assignment") }} |  | ||||||
|                             </v-btn> |  | ||||||
|                             <v-btn |  | ||||||
|                                 v-if="isTeacher" |  | ||||||
|                                 color="red" |  | ||||||
|                                 variant="text" |  | ||||||
|                                 @click="goToDeleteAssignment(assignment.id, assignment.class.id)" |  | ||||||
|                             > |  | ||||||
|                                 {{ t("delete") }} |  | ||||||
|                             </v-btn> |  | ||||||
|                         </div> |  | ||||||
|                     </v-card> |  | ||||||
|                 </v-col> |  | ||||||
|             </v-row> |  | ||||||
|         </v-container> |  | ||||||
|     </div> |  | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped> | <style scoped> | ||||||
|     .assignments-container { | .assignments-container { | ||||||
|         width: 100%; |     width: 100%; | ||||||
|         margin: 0 auto; |     margin: 0 auto; | ||||||
|         padding: 2% 4%; |     padding: 2% 4%; | ||||||
|         box-sizing: border-box; |     box-sizing: border-box; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     .center-btn { | .center-btn { | ||||||
|         display: block; |     display: block; | ||||||
|         margin-left: auto; |     margin-left: auto; | ||||||
|         margin-right: auto; |     margin-right: auto; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     .assignment-card { | .assignment-card { | ||||||
|         padding: 1rem; |     padding: 1rem; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     .top-content { | .top-content { | ||||||
|         margin-bottom: 1rem; |     margin-bottom: 1rem; | ||||||
|         word-break: break-word; |     word-break: break-word; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     .spacer { | .spacer { | ||||||
|         flex: 1; |     flex: 1; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     .button-row { | .button-row { | ||||||
|         display: flex; |     display: flex; | ||||||
|         justify-content: flex-end; |     justify-content: flex-end; | ||||||
|         gap: 0.5rem; |     gap: 0.5rem; | ||||||
|         flex-wrap: wrap; |     flex-wrap: wrap; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     .assignment-title { | .assignment-title { | ||||||
|         font-weight: bold; |     font-weight: bold; | ||||||
|         font-size: 1.5rem; |     font-size: 1.5rem; | ||||||
|         margin-bottom: 0.1rem; |     margin-bottom: 0.1rem; | ||||||
|         word-break: break-word; |     word-break: break-word; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     .assignment-class { | .assignment-class { | ||||||
|         color: #666; |     color: #666; | ||||||
|         font-size: 0.95rem; |     font-size: 0.95rem; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     .class-name { | .class-name { | ||||||
|         font-weight: 500; |     font-weight: 500; | ||||||
|         color: #333; |     color: #333; | ||||||
|     } | } | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 Adriaan Jacquet
						Adriaan Jacquet