feat: assignment description en lp editen is mogelijk
This commit is contained in:
		
							parent
							
								
									f83d5b54c0
								
							
						
					
					
						commit
						cc31effd61
					
				
					 4 changed files with 353 additions and 290 deletions
				
			
		
							
								
								
									
										47
									
								
								frontend/src/components/GroupProgressRow.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								frontend/src/components/GroupProgressRow.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import { useGetLearningPathQuery } from "@/queries/learning-paths.ts"; | ||||||
|  | import { computed } from "vue"; | ||||||
|  | import type { Language } from "@/data-objects/language.ts"; | ||||||
|  | import { calculateProgress } from "@/utils/assignment-utils.ts"; | ||||||
|  | 
 | ||||||
|  | const props = defineProps<{ | ||||||
|  |     groupNumber: number; | ||||||
|  |     learningPath: string; | ||||||
|  |     language: Language; | ||||||
|  |     assignmentId: number; | ||||||
|  |     classId: string; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | const query = useGetLearningPathQuery( | ||||||
|  |     () => props.learningPath, | ||||||
|  |     () => props.language, | ||||||
|  |     () => ({ | ||||||
|  |         forGroup: props.groupNumber, | ||||||
|  |         assignmentNo: props.assignmentId, | ||||||
|  |         classId: props.classId, | ||||||
|  |     }), | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | const progress = computed(() => { | ||||||
|  |     if (!query.data.value) return 0; | ||||||
|  |     return calculateProgress(query.data.value); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const progressColor = computed(() => { | ||||||
|  |     if (progress.value < 50) return "error"; | ||||||
|  |     if (progress.value < 80) return "warning"; | ||||||
|  |     return "success"; | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |     <v-progress-linear | ||||||
|  |         :model-value="progress" | ||||||
|  |         :color="progressColor" | ||||||
|  |         height="25" | ||||||
|  |     > | ||||||
|  |         <template v-slot:default="{ value }"> | ||||||
|  |             <strong>{{ Math.ceil(value) }}%</strong> | ||||||
|  |         </template> | ||||||
|  |     </v-progress-linear> | ||||||
|  | </template> | ||||||
							
								
								
									
										37
									
								
								frontend/src/components/GroupSubmissionStatus.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								frontend/src/components/GroupSubmissionStatus.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import { useI18n } from "vue-i18n"; | ||||||
|  | import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||||
|  | import { useAssignmentSubmissionsQuery } from "@/queries/assignments.ts"; | ||||||
|  | import type { SubmissionsResponse } from "@/controllers/submissions.ts"; | ||||||
|  | 
 | ||||||
|  | const props = defineProps<{ | ||||||
|  |     group: object; | ||||||
|  |     assignmentId: number; | ||||||
|  |     classId: string; | ||||||
|  |     goToGroupSubmissionLink: (groupNo: number) => void; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | const { t } = useI18n(); | ||||||
|  | const submissionsQuery = useAssignmentSubmissionsQuery( | ||||||
|  |     () => props.classId, | ||||||
|  |     () => props.assignmentId, | ||||||
|  |     () => props.group.originalGroupNo, | ||||||
|  |     () => true, | ||||||
|  | ); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |     <using-query-result | ||||||
|  |         :query-result="submissionsQuery" | ||||||
|  |         v-slot="{ data }: { data: SubmissionsResponse }" | ||||||
|  |     > | ||||||
|  |         <v-btn | ||||||
|  |             :color="data.submissions.length > 0 ? 'green' : 'red'" | ||||||
|  |             variant="text" | ||||||
|  |             :to="data.submissions.length > 0 ? goToGroupSubmissionLink(props.group.groupNo) : undefined" | ||||||
|  |             :disabled="data.submissions.length === 0" | ||||||
|  |         > | ||||||
|  |             {{ data.submissions.length > 0 ? t("submission") : t("noSubmissionsYet") }} | ||||||
|  |         </v-btn> | ||||||
|  |     </using-query-result> | ||||||
|  | </template> | ||||||
|  | @ -181,7 +181,7 @@ export function useAssignmentSubmissionsQuery( | ||||||
| 
 | 
 | ||||||
|     return useQuery({ |     return useQuery({ | ||||||
|         queryKey: computed(() => assignmentSubmissionsQueryKey(cid!, an!, f)), |         queryKey: computed(() => assignmentSubmissionsQueryKey(cid!, an!, f)), | ||||||
|         queryFn: async () => new AssignmentController(cid!).getSubmissions(gn!, f), |         queryFn: async () => new AssignmentController(cid!).getSubmissions(an!, f), | ||||||
|         enabled: () => checkEnabled(cid, an, gn), |         enabled: () => checkEnabled(cid, an, gn), | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,111 +1,140 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import { computed, type Ref, ref } from "vue"; | import {computed, type Ref, ref, watch, watchEffect} from "vue"; | ||||||
|     import { useI18n } from "vue-i18n"; | import {useI18n} from "vue-i18n"; | ||||||
|     import { useAssignmentQuery, useDeleteAssignmentMutation } from "@/queries/assignments.ts"; | import { | ||||||
|     import UsingQueryResult from "@/components/UsingQueryResult.vue"; |     useAssignmentQuery, | ||||||
|     import { useGroupsQuery } from "@/queries/groups.ts"; |     useDeleteAssignmentMutation, | ||||||
|     import { useGetAllLearningPaths, useGetLearningPathQuery } from "@/queries/learning-paths.ts"; |     useUpdateAssignmentMutation | ||||||
|     import type { Language } from "@/data-objects/language.ts"; | } from "@/queries/assignments.ts"; | ||||||
|     import type { AssignmentResponse } from "@/controllers/assignments.ts"; | import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||||
|     import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; | import {useGroupsQuery} from "@/queries/groups.ts"; | ||||||
|     import type { StudentDTO } from "@dwengo-1/common/interfaces/student"; | import {useGetAllLearningPaths, useGetLearningPathQuery} from "@/queries/learning-paths.ts"; | ||||||
|     import type { LearningPath } from "@/data-objects/learning-paths/learning-path"; | import type {Language} from "@/data-objects/language.ts"; | ||||||
|  | import type {AssignmentResponse} from "@/controllers/assignments.ts"; | ||||||
|  | import type {GroupDTO, GroupDTOId} from "@dwengo-1/common/interfaces/group"; | ||||||
|  | import type {LearningPath} from "@/data-objects/learning-paths/learning-path"; | ||||||
|  | import {descriptionRules, learningPathRules} from "@/utils/assignment-rules.ts"; | ||||||
|  | import GroupSubmissionStatus from "@/components/GroupSubmissionStatus.vue" | ||||||
|  | import GroupProgressRow from "@/components/GroupProgressRow.vue" | ||||||
|  | import type {AssignmentDTO} from "@dwengo-1/common/dist/interfaces/assignment.ts"; | ||||||
|  | import router from "@/router"; | ||||||
| 
 | 
 | ||||||
|     const props = defineProps<{ | const props = defineProps<{ | ||||||
|         classId: string; |     classId: string; | ||||||
|         assignmentId: number; |     assignmentId: number; | ||||||
|         useGroupsWithProgress: ( |     useGroupsWithProgress: ( | ||||||
|             groups: Ref<GroupDTO[]>, |         groups: Ref<GroupDTO[]>, | ||||||
|             hruid: Ref<string>, |         hruid: Ref<string>, | ||||||
|             language: Ref<Language>, |         language: Ref<Language>, | ||||||
|         ) => { groupProgressMap: Map<number, number> }; |     ) => { groupProgressMap: Map<number, number> }; | ||||||
|     }>(); | }>(); | ||||||
| 
 | 
 | ||||||
|     const isEditing = ref(false); | const isEditing = ref(false); | ||||||
| 
 | 
 | ||||||
|     const { t, locale } = useI18n(); | const {t} = useI18n(); | ||||||
|     const language = computed(() => locale.value); | const lang = ref(); | ||||||
|     const groups = ref(); | const groups = ref<GroupDTO[] | GroupDTOId[]>([]); | ||||||
|     const learningPath = ref(); | const learningPath = ref(); | ||||||
|     const editingLearningPath = ref(learningPath); |  | ||||||
|     const description = ref(""); |  | ||||||
| 
 | 
 | ||||||
|     const assignmentQueryResult = useAssignmentQuery(props.classId, props.assignmentId); | const editingLearningPath = ref(learningPath); | ||||||
|     learningPath.value = assignmentQueryResult.data.value?.assignment?.learningPath; | const description = ref(""); | ||||||
|     const learningPathsQueryResults = useGetAllLearningPaths(language); |  | ||||||
| 
 | 
 | ||||||
|     // Get learning path object |  | ||||||
|     const lpQueryResult = useGetLearningPathQuery( |  | ||||||
|         computed(() => assignmentQueryResult.data.value?.assignment?.learningPath ?? ""), |  | ||||||
|         computed(() => language.value as Language), |  | ||||||
|     ); |  | ||||||
| 
 | 
 | ||||||
|     const learningPathRules = [ | const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); | ||||||
|         (value: { hruid: string; title: string }): string | boolean => { | // Get learning path object | ||||||
|             if (value && value.hruid) { | const lpQueryResult = useGetLearningPathQuery( | ||||||
|                 return true; // Valid if hruid is present |     computed(() => assignmentQueryResult.data.value?.assignment?.learningPath ?? ""), | ||||||
|             } |     computed(() => assignmentQueryResult.data.value?.assignment?.language as Language), | ||||||
|             return "You must select a learning path."; |  | ||||||
|         }, |  | ||||||
|     ]; |  | ||||||
| 
 |  | ||||||
|     // Get all the groups withing the assignment |  | ||||||
|     const groupsQueryResult = useGroupsQuery(props.classId, props.assignmentId, true); |  | ||||||
|     groups.value = groupsQueryResult.data.value?.groups; |  | ||||||
| 
 |  | ||||||
|     /* Crashes right now cause api data has inexistent hruid TODO: uncomment later and use it in progress bar |  | ||||||
| Const {groupProgressMap} = props.useGroupsWithProgress( |  | ||||||
|     groups, |  | ||||||
|     learningPath, |  | ||||||
|     language |  | ||||||
| ); | ); | ||||||
| */ |  | ||||||
| 
 | 
 | ||||||
|     const allGroups = computed(() => { | // Get all the groups withing the assignment | ||||||
|         const groups = groupsQueryResult.data.value?.groups; | const groupsQueryResult = useGroupsQuery(props.classId, props.assignmentId, true); | ||||||
|         if (!groups) return []; | groups.value = groupsQueryResult.data.value?.groups ?? []; | ||||||
| 
 | 
 | ||||||
|         return groups.map((group) => ({ | watchEffect(() => { | ||||||
|             name: `${t("group")} ${group.groupNumber}`, |     learningPath.value = assignmentQueryResult.data.value?.assignment?.learningPath; | ||||||
|             progress: 0, //GroupProgressMap[group.groupNumber], |     lang.value = assignmentQueryResult.data.value?.assignment?.language as Language; | ||||||
|             members: group.members, | }); | ||||||
|             submitted: false, //TODO: fetch from submission |  | ||||||
|         })); |  | ||||||
|     }); |  | ||||||
| 
 | 
 | ||||||
|     const dialog = ref(false); | const allGroups = computed(() => { | ||||||
|     const selectedGroup = ref({}); |     const groups = groupsQueryResult.data.value?.groups; | ||||||
| 
 | 
 | ||||||
|     function openGroupDetails(group): void { |     if (!groups) return []; | ||||||
|         selectedGroup.value = group; |  | ||||||
|         dialog.value = true; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     const headers = computed(() => [ |     // Sort by original groupNumber | ||||||
|         { title: t("group"), align: "start", key: "name" }, |     const sortedGroups = [...groups].sort((a, b) => a.groupNumber - b.groupNumber); | ||||||
|         { title: t("progress"), align: "center", key: "progress" }, |  | ||||||
|         { title: t("submission"), align: "center", key: "submission" }, |  | ||||||
|     ]); |  | ||||||
| 
 | 
 | ||||||
|     const { mutate } = useDeleteAssignmentMutation(); |     // Assign new sequential numbers starting from 1 | ||||||
|  |     return sortedGroups.map((group, index) => ({ | ||||||
|  |         groupNo: index + 1, // New group number that will be used | ||||||
|  |         name: `${t("group")} ${index + 1}`, | ||||||
|  |         members: group.members, | ||||||
|  |         originalGroupNo: group.groupNumber, // Keep original number if needed | ||||||
|  |     })); | ||||||
|  | }); | ||||||
| 
 | 
 | ||||||
|     async function deleteAssignment(num: number, clsId: string): Promise<void> { | const dialog = ref(false); | ||||||
|         mutate( | const selectedGroup = ref({}); | ||||||
|             { cid: clsId, an: num }, | 
 | ||||||
|             { | function openGroupDetails(group): void { | ||||||
|                 onSuccess: () => { |     selectedGroup.value = group; | ||||||
|                     window.location.href = "/user/assignment"; |     dialog.value = true; | ||||||
|                 }, | } | ||||||
|  | 
 | ||||||
|  | async function deleteAssignment(num: number, clsId: string): Promise<void> { | ||||||
|  |     const {mutate} = useDeleteAssignmentMutation(); | ||||||
|  |     mutate( | ||||||
|  |         {cid: clsId, an: num}, | ||||||
|  |         { | ||||||
|  |             onSuccess: () => { | ||||||
|  |                 window.location.href = "/user/assignment"; | ||||||
|             }, |             }, | ||||||
|         ); |         }, | ||||||
|     } |     ); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     function saveChanges() { | function goToLearningPathLink(): string | undefined { | ||||||
|         //TODO |     const assignment = assignmentQueryResult.data.value?.assignment; | ||||||
|         const new_learningpath = editingLearningPath.value; |     const lp = lpQueryResult.data.value; | ||||||
|         const new_description = description.value; | 
 | ||||||
|         isEditing.value = false; |     if (!assignment || !lp) return undefined; | ||||||
|  | 
 | ||||||
|  |     return `/learningPath/${lp.hruid}/${assignment.language}/${lp.startNode.learningobjectHruid}?assignmentNo=${props.assignmentId}&classId=${props.classId}`; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function goToGroupSubmissionLink(groupNo: number): string | undefined { | ||||||
|  |     const lp = lpQueryResult.data.value; | ||||||
|  |     if (!lp) return undefined; | ||||||
|  | 
 | ||||||
|  |     return `/learningPath/${lp.hruid}/${lp.language}/${lp.startNode.learningobjectHruid}?forGroup=${groupNo}&assignmentNo=${props.assignmentId}&classId=${props.classId}`; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const learningPathsQueryResults = useGetAllLearningPaths(lang); | ||||||
|  | 
 | ||||||
|  | const { mutate, data, isSuccess } = useUpdateAssignmentMutation(); | ||||||
|  | 
 | ||||||
|  | watch([isSuccess, data], ([success, newData]) => { | ||||||
|  |     if (success && newData?.assignment) { | ||||||
|  |         window.location.reload(); | ||||||
|     } |     } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | async function saveChanges(): Promise<void> { | ||||||
|  |     isEditing.value = false; | ||||||
|  |     //const { valid } = await form.value.validate(); | ||||||
|  |     //if (!valid) return; | ||||||
|  | 
 | ||||||
|  |     const lp = learningPath.value; | ||||||
|  | 
 | ||||||
|  |     const assignmentDTO: AssignmentDTO = { | ||||||
|  |         id: assignmentQueryResult.data.value?.assignment.id, | ||||||
|  |         description: description.value, | ||||||
|  |         learningPath: lp || "", | ||||||
|  |         deadline: new Date(), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     mutate({ cid: assignmentQueryResult.data.value?.assignment.within, an: assignmentQueryResult.data.value?.assignment.id, data: assignmentDTO }); | ||||||
|  | } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|  | @ -162,7 +191,8 @@ Const {groupProgressMap} = props.useGroupsWithProgress( | ||||||
|                                             variant="text" |                                             variant="text" | ||||||
|                                             class="top-right-btn" |                                             class="top-right-btn" | ||||||
|                                             @click="() => {isEditing = false; editingLearningPath=learningPath}" |                                             @click="() => {isEditing = false; editingLearningPath=learningPath}" | ||||||
|                                             >{{ t("cancel") }}</v-btn |                                         >{{ t("cancel") }} | ||||||
|  |                                         </v-btn | ||||||
|                                         > |                                         > | ||||||
| 
 | 
 | ||||||
|                                         <v-btn |                                         <v-btn | ||||||
|  | @ -193,8 +223,9 @@ Const {groupProgressMap} = props.useGroupsWithProgress( | ||||||
|                             </div> |                             </div> | ||||||
| 
 | 
 | ||||||
|                             <v-card-title class="text-h4 assignmentTopTitle">{{ |                             <v-card-title class="text-h4 assignmentTopTitle">{{ | ||||||
|                                 assignmentResponse.data.assignment.title |                                     assignmentResponse.data.assignment.title | ||||||
|                             }}</v-card-title> |                                 }} | ||||||
|  |                             </v-card-title> | ||||||
|                             <v-card-subtitle |                             <v-card-subtitle | ||||||
|                                 v-if="!isEditing" |                                 v-if="!isEditing" | ||||||
|                                 class="subtitle-section" |                                 class="subtitle-section" | ||||||
|  | @ -205,7 +236,7 @@ Const {groupProgressMap} = props.useGroupsWithProgress( | ||||||
|                                 > |                                 > | ||||||
|                                     <v-btn |                                     <v-btn | ||||||
|                                         v-if="lpData" |                                         v-if="lpData" | ||||||
|                                         :to="`/learningPath/${lpData.hruid}/${language}/${lpData.startNode.learningobjectHruid}?assignmentNo=${assignmentId}&classId=${classId}`" |                                         :to="goToLearningPathLink()" | ||||||
|                                         variant="tonal" |                                         variant="tonal" | ||||||
|                                         color="primary" |                                         color="primary" | ||||||
|                                     > |                                     > | ||||||
|  | @ -258,50 +289,6 @@ Const {groupProgressMap} = props.useGroupsWithProgress( | ||||||
|                                 ></v-textarea> |                                 ></v-textarea> | ||||||
|                             </v-card-text> |                             </v-card-text> | ||||||
| 
 | 
 | ||||||
|                             <v-card-text class="group-section"> |  | ||||||
|                                 <h3>{{ t("groups") }}</h3> |  | ||||||
|                                 <div class="table-scroll"> |  | ||||||
|                                     <v-data-table |  | ||||||
|                                         :headers="headers" |  | ||||||
|                                         :items="allGroups" |  | ||||||
|                                         item-key="id" |  | ||||||
|                                         class="elevation-1" |  | ||||||
|                                     > |  | ||||||
|                                         <template #[`item.name`]="{ item }"> |  | ||||||
|                                             <v-btn |  | ||||||
|                                                 @click="openGroupDetails(item)" |  | ||||||
|                                                 variant="text" |  | ||||||
|                                                 color="primary" |  | ||||||
|                                             > |  | ||||||
|                                                 {{ item.name }} |  | ||||||
|                                             </v-btn> |  | ||||||
|                                         </template> |  | ||||||
| 
 |  | ||||||
|                                         <template #[`item.progress`]="{ item }"> |  | ||||||
|                                             <v-progress-linear |  | ||||||
|                                                 :model-value="item.progress" |  | ||||||
|                                                 color="blue-grey" |  | ||||||
|                                                 height="25" |  | ||||||
|                                             > |  | ||||||
|                                                 <template v-slot:default="{ value }"> |  | ||||||
|                                                     <strong>{{ Math.ceil(value) }}%</strong> |  | ||||||
|                                                 </template> |  | ||||||
|                                             </v-progress-linear> |  | ||||||
|                                         </template> |  | ||||||
| 
 |  | ||||||
|                                         <template #[`item.submission`]="{ item }"> |  | ||||||
|                                             <v-btn |  | ||||||
|                                                 :to="item.submitted ? `${props.assignmentId}/submissions/` : undefined" |  | ||||||
|                                                 :color="item.submitted ? 'green' : 'red'" |  | ||||||
|                                                 variant="text" |  | ||||||
|                                                 class="text-capitalize" |  | ||||||
|                                             > |  | ||||||
|                                                 {{ item.submitted ? t("see-submission") : t("no-submission") }} |  | ||||||
|                                             </v-btn> |  | ||||||
|                                         </template> |  | ||||||
|                                     </v-data-table> |  | ||||||
|                                 </div> |  | ||||||
|                             </v-card-text> |  | ||||||
| 
 | 
 | ||||||
|                             <v-dialog |                             <v-dialog | ||||||
|                                 v-model="dialog" |                                 v-model="dialog" | ||||||
|  | @ -317,7 +304,7 @@ Const {groupProgressMap} = props.useGroupsWithProgress( | ||||||
|                                             > |                                             > | ||||||
|                                                 <v-list-item-content> |                                                 <v-list-item-content> | ||||||
|                                                     <v-list-item-title |                                                     <v-list-item-title | ||||||
|                                                         >{{ member.firstName + " " + member.lastName }} |                                                     >{{ member.firstName + " " + member.lastName }} | ||||||
|                                                     </v-list-item-title> |                                                     </v-list-item-title> | ||||||
|                                                 </v-list-item-content> |                                                 </v-list-item-content> | ||||||
|                                             </v-list-item> |                                             </v-list-item> | ||||||
|  | @ -327,22 +314,12 @@ Const {groupProgressMap} = props.useGroupsWithProgress( | ||||||
|                                         <v-btn |                                         <v-btn | ||||||
|                                             color="primary" |                                             color="primary" | ||||||
|                                             @click="dialog = false" |                                             @click="dialog = false" | ||||||
|                                             >Close</v-btn |                                         >Close | ||||||
|  |                                         </v-btn | ||||||
|                                         > |                                         > | ||||||
|                                     </v-card-actions> |                                     </v-card-actions> | ||||||
|                                 </v-card> |                                 </v-card> | ||||||
|                             </v-dialog> |                             </v-dialog> | ||||||
|                             <!-- |  | ||||||
|                         <v-card-actions class="justify-end"> |  | ||||||
|                             <v-btn |  | ||||||
|                                 size="large" |  | ||||||
|                                 color="success" |  | ||||||
|                                 variant="text" |  | ||||||
|                             > |  | ||||||
|                                 {{ t("view-submissions") }} |  | ||||||
|                             </v-btn> |  | ||||||
|                         </v-card-actions> |  | ||||||
|                         --> |  | ||||||
|                         </v-card> |                         </v-card> | ||||||
|                     </v-col> |                     </v-col> | ||||||
|                     <v-col |                     <v-col | ||||||
|  | @ -353,61 +330,63 @@ Const {groupProgressMap} = props.useGroupsWithProgress( | ||||||
|                     > |                     > | ||||||
|                         <v-table class="table"> |                         <v-table class="table"> | ||||||
|                             <thead> |                             <thead> | ||||||
|                                 <tr> |                             <tr> | ||||||
|                                     <th class="header">{{ t("group") }}</th> |                                 <th class="header">{{ t("group") }}</th> | ||||||
|                                     <th class="header"> |                                 <th class="header">{{ t("progress") }}</th> | ||||||
|                                         {{ t("progress") }} |                                 <th class="header">{{ t("submission") }}</th> | ||||||
|                                     </th> |                                 <th class="header"> | ||||||
|                                     <th class="header">{{ t("Members") }}</th> |                                     <v-icon>mdi-pencil</v-icon> | ||||||
|                                     <th class="header"> |                                 </th> | ||||||
|                                         <v-btn |                             </tr> | ||||||
|                                             :to="`/assignment/${assignmentResponse.data.assignment.within}/${assignmentResponse.data.assignment.id}`" |  | ||||||
|                                             variant="text" |  | ||||||
|                                         > |  | ||||||
|                                             <v-icon> mdi-pencil </v-icon> |  | ||||||
|                                         </v-btn> |  | ||||||
|                                     </th> |  | ||||||
|                                 </tr> |  | ||||||
|                             </thead> |                             </thead> | ||||||
|                             <tbody> |                             <tbody> | ||||||
|                                 <tr |                             <tr | ||||||
|                                     v-for="g in assignmentResponse.data.assignment.groups as GroupDTO[]" |                                 v-for="g in allGroups" | ||||||
|                                     :key="g.groupNumber" |                                 :key="g.originalGroupNo" | ||||||
|                                 > |                             > | ||||||
|                                     <td> |                                 <td> | ||||||
|                                         <v-btn |                                     <v-btn | ||||||
|                                             :to="`/assignment/${assignmentResponse.data.assignment.within}/${assignmentResponse.data.assignment.id}`" |                                         @click="openGroupDetails(g)" | ||||||
|                                             variant="text" |                                         variant="text" | ||||||
|                                         > |                                     > | ||||||
|                                             {{ g.groupNumber }} |                                         {{ g.name }} | ||||||
|                                             <v-icon end> mdi-menu-right </v-icon> |                                         <v-icon end>mdi-menu-right</v-icon> | ||||||
|                                         </v-btn> |                                     </v-btn> | ||||||
|                                     </td> |                                 </td> | ||||||
|                                     <td> | 
 | ||||||
|                                         <v-progress-linear |                                 <td> | ||||||
|                                             :model-value="0" |                                     <GroupProgressRow | ||||||
|                                             color="blue-grey" |                                         :group-number="g.originalGroupNo" | ||||||
|                                             height="25" |                                         :learning-path="learningPath" | ||||||
|                                         > |                                         :language="lang" | ||||||
|                                             <template v-slot:default="{ value }"> |                                         :assignment-id="assignmentId" | ||||||
|                                                 <strong>{{ Math.ceil(value) }}%</strong> |                                         :class-id="classId" | ||||||
|                                             </template> |                                     /> | ||||||
|                                         </v-progress-linear> |                                 </td> | ||||||
|                                     </td> | 
 | ||||||
|                                     <td> |                                 <td> | ||||||
|                                         {{ (g.members! as StudentDTO[]).map((member) => member.username).join(", ") }} |                                     <GroupSubmissionStatus | ||||||
|                                     </td> |                                         :group="g" | ||||||
|                                     <td> |                                         :assignment-id="assignmentId" | ||||||
|                                         <v-btn |                                         :class-id="classId" | ||||||
|                                             :to="`/assignment/${assignmentResponse.data.assignment.within}/${assignmentResponse.data.assignment.id}`" |                                         :language="lang" | ||||||
|                                             variant="text" |                                         :go-to-group-submission-link="goToGroupSubmissionLink" | ||||||
|                                         > |                                     /> | ||||||
|                                             <v-icon color="red"> mdi-delete </v-icon> |                                 </td> | ||||||
|                                         </v-btn> | 
 | ||||||
|                                     </td> |                                 <!-- Edit icon --> | ||||||
|                                 </tr> |                                 <td> | ||||||
|  |                                     <v-btn | ||||||
|  |                                         to="/user" | ||||||
|  |                                         variant="text" | ||||||
|  |                                     > | ||||||
|  |                                         <v-icon color="red"> mdi-delete</v-icon> | ||||||
|  |                                     </v-btn> | ||||||
|  |                                 </td> | ||||||
|  |                             </tr> | ||||||
|                             </tbody> |                             </tbody> | ||||||
|                         </v-table> |                         </v-table> | ||||||
|  | 
 | ||||||
|                     </v-col> |                     </v-col> | ||||||
|                 </v-row> |                 </v-row> | ||||||
|             </v-container> |             </v-container> | ||||||
|  | @ -416,112 +395,112 @@ Const {groupProgressMap} = props.useGroupsWithProgress( | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped> | <style scoped> | ||||||
|     @import "@/assets/assignment.css"; | @import "@/assets/assignment.css"; | ||||||
| 
 | 
 | ||||||
|     .table-scroll { | .table-scroll { | ||||||
|         overflow-x: auto; |     overflow-x: auto; | ||||||
|         -webkit-overflow-scrolling: touch; |     -webkit-overflow-scrolling: touch; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     .header { | .header { | ||||||
|         font-weight: bold !important; |     font-weight: bold !important; | ||||||
|         background-color: #0e6942; |     background-color: #0e6942; | ||||||
|         color: white; |     color: white; | ||||||
|         padding: 10px; |     padding: 10px; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     table thead th:first-child { | table thead th:first-child { | ||||||
|         border-top-left-radius: 10px; |     border-top-left-radius: 10px; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     .table thead th:last-child { | .table thead th:last-child { | ||||||
|         border-top-right-radius: 10px; |     border-top-right-radius: 10px; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     .table tbody tr:nth-child(odd) { | .table tbody tr:nth-child(odd) { | ||||||
|         background-color: white; |     background-color: white; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     .table tbody tr:nth-child(even) { | .table tbody tr:nth-child(even) { | ||||||
|         background-color: #f6faf2; |     background-color: #f6faf2; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     td, | td, | ||||||
|     th { | th { | ||||||
|         border-bottom: 1px solid #0e6942; |     border-bottom: 1px solid #0e6942; | ||||||
|         border-top: 1px solid #0e6942; |     border-top: 1px solid #0e6942; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     .table { | .table { | ||||||
|         width: 90%; |     width: 90%; | ||||||
|         padding-top: 10px; |     padding-top: 10px; | ||||||
|         border-collapse: collapse; |     border-collapse: collapse; | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|  | h1 { | ||||||
|  |     color: #0e6942; | ||||||
|  |     text-transform: uppercase; | ||||||
|  |     font-weight: bolder; | ||||||
|  |     padding-top: 2%; | ||||||
|  |     font-size: 50px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | h2 { | ||||||
|  |     color: #0e6942; | ||||||
|  |     font-size: 30px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .join { | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: column; | ||||||
|  |     gap: 20px; | ||||||
|  |     margin-top: 50px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .link { | ||||||
|  |     color: #0b75bb; | ||||||
|  |     text-decoration: underline; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | main { | ||||||
|  |     margin-left: 30px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @media screen and (max-width: 850px) { | ||||||
|     h1 { |     h1 { | ||||||
|         color: #0e6942; |         text-align: center; | ||||||
|         text-transform: uppercase; |         padding-left: 0; | ||||||
|         font-weight: bolder; |  | ||||||
|         padding-top: 2%; |  | ||||||
|         font-size: 50px; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     h2 { |  | ||||||
|         color: #0e6942; |  | ||||||
|         font-size: 30px; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     .join { |     .join { | ||||||
|         display: flex; |         text-align: center; | ||||||
|         flex-direction: column; |         align-items: center; | ||||||
|         gap: 20px; |         margin-left: 0; | ||||||
|         margin-top: 50px; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     .link { |     .sheet { | ||||||
|         color: #0b75bb; |         width: 100%; | ||||||
|         text-decoration: underline; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     main { |     main { | ||||||
|         margin-left: 30px; |         display: flex; | ||||||
|  |         flex-direction: column; | ||||||
|  |         align-items: center; | ||||||
|  |         justify-content: center; | ||||||
|  |         margin: 5px; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @media screen and (max-width: 850px) { |     .custom-breakpoint { | ||||||
|         h1 { |         flex-direction: column !important; | ||||||
|             text-align: center; |  | ||||||
|             padding-left: 0; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         .join { |  | ||||||
|             text-align: center; |  | ||||||
|             align-items: center; |  | ||||||
|             margin-left: 0; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         .sheet { |  | ||||||
|             width: 100%; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         main { |  | ||||||
|             display: flex; |  | ||||||
|             flex-direction: column; |  | ||||||
|             align-items: center; |  | ||||||
|             justify-content: center; |  | ||||||
|             margin: 5px; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         .custom-breakpoint { |  | ||||||
|             flex-direction: column !important; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         .table { |  | ||||||
|             width: 100%; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         .responsive-col { |  | ||||||
|             max-width: 100% !important; |  | ||||||
|             flex-basis: 100% !important; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     .table { | ||||||
|  |         width: 100%; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .responsive-col { | ||||||
|  |         max-width: 100% !important; | ||||||
|  |         flex-basis: 100% !important; | ||||||
|  |     } | ||||||
|  | } | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 Joyelle Ndagijimana
						Joyelle Ndagijimana