feat: bezig met edit groups
This commit is contained in:
		
							parent
							
								
									cc31effd61
								
							
						
					
					
						commit
						936a34b709
					
				
					 4 changed files with 351 additions and 266 deletions
				
			
		|  | @ -27,6 +27,7 @@ | |||
|         "vue": "^3.5.13", | ||||
|         "vue-i18n": "^11.1.2", | ||||
|         "vue-router": "^4.5.0", | ||||
|         "vuedraggable": "^2.24.3", | ||||
|         "vuetify": "^3.7.12", | ||||
|         "wait-on": "^8.0.3" | ||||
|     }, | ||||
|  |  | |||
|  | @ -1,75 +1,114 @@ | |||
| <script setup lang="ts"> | ||||
|     import { ref } from "vue"; | ||||
| import { ref, } from "vue"; | ||||
| import draggable from "vuedraggable"; | ||||
| import { useI18n } from "vue-i18n"; | ||||
|     import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||
|     import type { StudentsResponse } from "@/controllers/students.ts"; | ||||
|     import { useClassStudentsQuery } from "@/queries/classes.ts"; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
|     classId: string | undefined; | ||||
|     groups: string[][]; | ||||
| }>(); | ||||
|     const emit = defineEmits(["groupCreated"]); | ||||
| const emit = defineEmits(["done", "groupsUpdated"]); | ||||
| const { t } = useI18n(); | ||||
| 
 | ||||
|     const selectedStudents = ref([]); | ||||
| const groupList = ref(props.groups.map(g => [...g])); // deep copy | ||||
| const unassigned = ref<string[]>([]); // voor vrije studenten | ||||
| 
 | ||||
|     const studentQueryResult = useClassStudentsQuery(() => props.classId, true); | ||||
| 
 | ||||
|     function filterStudents(data: StudentsResponse): { title: string; value: string }[] { | ||||
|         const students = data.students; | ||||
|         const studentsInGroups = props.groups.flat(); | ||||
| 
 | ||||
|         return students | ||||
|             ?.map((st) => ({ | ||||
|                 title: `${st.firstName} ${st.lastName}`, | ||||
|                 value: st.username, | ||||
|             })) | ||||
|             .filter((student) => !studentsInGroups.includes(student.value)); | ||||
| function addNewGroup() { | ||||
|     groupList.value.push([]); | ||||
| } | ||||
| 
 | ||||
|     function createGroup(): void { | ||||
|         if (selectedStudents.value.length) { | ||||
|             // Extract only usernames (student.value) | ||||
|             const usernames = selectedStudents.value.map((student) => student.value); | ||||
|             emit("groupCreated", usernames); | ||||
|             selectedStudents.value = []; // Reset selection after creating group | ||||
| function removeGroup(index: number) { | ||||
|     unassigned.value.push(...groupList.value[index]); | ||||
|     groupList.value.splice(index, 1); | ||||
| } | ||||
| 
 | ||||
| function saveChanges() { | ||||
|     emit("groupsUpdated", groupList.value); | ||||
|     emit("done"); | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|     <using-query-result | ||||
|         :query-result="studentQueryResult" | ||||
|         v-slot="{ data }: { data: StudentsResponse }" | ||||
|     > | ||||
|         <h3>{{ t("create-groups") }}</h3> | ||||
|     <v-card> | ||||
|         <v-card-title>{{ t("edit-groups") }}</v-card-title> | ||||
|         <v-card-text> | ||||
|             <v-combobox | ||||
|                 v-model="selectedStudents" | ||||
|                 :items="filterStudents(data)" | ||||
|                 item-title="title" | ||||
|                 item-value="value" | ||||
|                 :label="t('choose-students')" | ||||
|                 variant="outlined" | ||||
|                 clearable | ||||
|                 multiple | ||||
|                 hide-details | ||||
|                 density="compact" | ||||
|                 chips | ||||
|                 append-inner-icon="mdi-magnify" | ||||
|             ></v-combobox> | ||||
|             <v-row> | ||||
|                 <!-- Ongegroepeerde studenten --> | ||||
|                 <v-col cols="12" sm="4"> | ||||
|                     <h4>{{ t("unassigned") }}</h4> | ||||
|                     <draggable | ||||
|                         v-model="unassigned" | ||||
|                         group="students" | ||||
|                         item-key="username" | ||||
|                         class="group-box" | ||||
|                     > | ||||
|                         <template #item="{ element }"> | ||||
|                             <v-chip>{{ element }}</v-chip> | ||||
|                         </template> | ||||
|                     </draggable> | ||||
|                 </v-col> | ||||
| 
 | ||||
|                 <!-- Bestaande groepen --> | ||||
|                 <v-col | ||||
|                     v-for="(group, i) in groupList" | ||||
|                     :key="i" | ||||
|                     cols="12" | ||||
|                     sm="4" | ||||
|                 > | ||||
|                     <h4>{{ t("group") }} {{ i + 1 }}</h4> | ||||
|                     <draggable | ||||
|                         v-model="groupList[i]" | ||||
|                         group="students" | ||||
|                         item-key="username" | ||||
|                         class="group-box" | ||||
|                     > | ||||
|                         <template #item="{ element }"> | ||||
|                             <v-chip>{{ element }}</v-chip> | ||||
|                         </template> | ||||
|                     </draggable> | ||||
| 
 | ||||
|                     <v-btn | ||||
|                 @click="createGroup" | ||||
|                 color="primary" | ||||
|                         color="error" | ||||
|                         size="x-small" | ||||
|                         @click="removeGroup(i)" | ||||
|                         class="mt-2" | ||||
|                 size="small" | ||||
|                     > | ||||
|                 {{ t("create-group") }} | ||||
|                         {{ t("remove-group") }} | ||||
|                     </v-btn> | ||||
|                 </v-col> | ||||
|             </v-row> | ||||
| 
 | ||||
|             <v-btn | ||||
|                 color="primary" | ||||
|                 class="mt-4" | ||||
|                 @click="addNewGroup" | ||||
|             > | ||||
|                 {{ t("add-group") }} | ||||
|             </v-btn> | ||||
|         </v-card-text> | ||||
|     </using-query-result> | ||||
|         <v-card-actions> | ||||
|             <v-btn | ||||
|                 color="success" | ||||
|                 @click="saveChanges" | ||||
|             > | ||||
|                 {{ t("save") }} | ||||
|             </v-btn> | ||||
|             <v-btn | ||||
|                 @click="$emit('done')" | ||||
|                 variant="text" | ||||
|             > | ||||
|                 {{ t("cancel") }} | ||||
|             </v-btn> | ||||
|         </v-card-actions> | ||||
|     </v-card> | ||||
| </template> | ||||
| 
 | ||||
| <style scoped></style> | ||||
| <style scoped> | ||||
| .group-box { | ||||
|     min-height: 100px; | ||||
|     border: 1px dashed #ccc; | ||||
|     padding: 8px; | ||||
|     margin-bottom: 16px; | ||||
|     background-color: #fafafa; | ||||
| } | ||||
| </style> | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ 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"; | ||||
| import GroupSelector from "@/components/assignments/GroupSelector.vue"; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
|     classId: string; | ||||
|  | @ -35,9 +35,12 @@ const {t} = useI18n(); | |||
| const lang = ref(); | ||||
| const groups = ref<GroupDTO[] | GroupDTOId[]>([]); | ||||
| const learningPath = ref(); | ||||
| const form = ref(); | ||||
| 
 | ||||
| 
 | ||||
| const editingLearningPath = ref(learningPath); | ||||
| const description = ref(""); | ||||
| const editGroups = ref(false); | ||||
| 
 | ||||
| 
 | ||||
| const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); | ||||
|  | @ -120,9 +123,10 @@ watch([isSuccess, data], ([success, newData]) => { | |||
| }); | ||||
| 
 | ||||
| async function saveChanges(): Promise<void> { | ||||
|     const {valid} = await form.value.validate(); | ||||
|     if (!valid) return; | ||||
| 
 | ||||
|     isEditing.value = false; | ||||
|     //const { valid } = await form.value.validate(); | ||||
|     //if (!valid) return; | ||||
| 
 | ||||
|     const lp = learningPath.value; | ||||
| 
 | ||||
|  | @ -133,8 +137,14 @@ async function saveChanges(): Promise<void> { | |||
|         deadline: new Date(), | ||||
|     }; | ||||
| 
 | ||||
|     mutate({ cid: assignmentQueryResult.data.value?.assignment.within, an: assignmentQueryResult.data.value?.assignment.id, data: assignmentDTO }); | ||||
|     mutate({ | ||||
|         cid: assignmentQueryResult.data.value?.assignment.within, | ||||
|         an: assignmentQueryResult.data.value?.assignment.id, | ||||
|         data: assignmentDTO | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|  | @ -157,6 +167,7 @@ async function saveChanges(): Promise<void> { | |||
|                         md="6" | ||||
|                         class="responsive-col" | ||||
|                     > | ||||
|                         <v-form ref="form" validate-on="submit lazy" @submit.prevent="saveChanges"> | ||||
|                             <v-card | ||||
|                                 v-if="assignmentResponse" | ||||
|                                 class="assignment-card" | ||||
|  | @ -288,8 +299,10 @@ async function saveChanges(): Promise<void> { | |||
|                                         :rules="descriptionRules" | ||||
|                                     ></v-textarea> | ||||
|                                 </v-card-text> | ||||
|                             </v-card> | ||||
|                         </v-form> | ||||
| 
 | ||||
| 
 | ||||
|                         <!-- A pop up to show group members --> | ||||
|                         <v-dialog | ||||
|                             v-model="dialog" | ||||
|                             max-width="50%" | ||||
|  | @ -320,8 +333,10 @@ async function saveChanges(): Promise<void> { | |||
|                                 </v-card-actions> | ||||
|                             </v-card> | ||||
|                         </v-dialog> | ||||
|                         </v-card> | ||||
|                     </v-col> | ||||
| 
 | ||||
|                     <!-- The second column of the screen --> | ||||
|                     <template v-if="!editGroups"> | ||||
|                         <v-col | ||||
|                             cols="12" | ||||
|                             sm="6" | ||||
|  | @ -335,7 +350,13 @@ async function saveChanges(): Promise<void> { | |||
|                                     <th class="header">{{ t("progress") }}</th> | ||||
|                                     <th class="header">{{ t("submission") }}</th> | ||||
|                                     <th class="header"> | ||||
|                                         <v-btn | ||||
|                                             @click="editGroups = true" | ||||
|                                             variant="text" | ||||
|                                         > | ||||
|                                             <v-icon>mdi-pencil</v-icon> | ||||
|                                         </v-btn> | ||||
| 
 | ||||
|                                     </th> | ||||
|                                 </tr> | ||||
|                                 </thead> | ||||
|  | @ -377,17 +398,25 @@ async function saveChanges(): Promise<void> { | |||
|                                     <!-- Edit icon --> | ||||
|                                     <td> | ||||
|                                         <v-btn | ||||
|                                         to="/user" | ||||
|                                             @click="" | ||||
|                                             variant="text" | ||||
|                                         > | ||||
|                                             <v-icon color="red">mdi-delete</v-icon> | ||||
|                                         </v-btn> | ||||
| 
 | ||||
|                                     </td> | ||||
|                                 </tr> | ||||
|                                 </tbody> | ||||
|                             </v-table> | ||||
| 
 | ||||
|                         </v-col> | ||||
|                     </template> | ||||
|                     <template v-else> | ||||
|                         <GroupSelector | ||||
|                             :groups="allGroups" | ||||
|                             :class-id="classId" | ||||
|                             @groupsUpdated="handleUpdatedGroups" | ||||
|                         /> | ||||
|                     </template> | ||||
|                 </v-row> | ||||
|             </v-container> | ||||
|         </using-query-result> | ||||
|  |  | |||
							
								
								
									
										16
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										16
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							|  | @ -107,6 +107,7 @@ | |||
|                 "vue": "^3.5.13", | ||||
|                 "vue-i18n": "^11.1.2", | ||||
|                 "vue-router": "^4.5.0", | ||||
|                 "vuedraggable": "^2.24.3", | ||||
|                 "vuetify": "^3.7.12", | ||||
|                 "wait-on": "^8.0.3" | ||||
|             }, | ||||
|  | @ -7599,6 +7600,12 @@ | |||
|                 "node": ">= 6.0.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/sortablejs": { | ||||
|             "version": "1.10.2", | ||||
|             "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz", | ||||
|             "integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==", | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/source-map-js": { | ||||
|             "version": "1.2.1", | ||||
|             "license": "BSD-3-Clause", | ||||
|  | @ -8970,6 +8977,15 @@ | |||
|                 "typescript": ">=5.0.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/vuedraggable": { | ||||
|             "version": "2.24.3", | ||||
|             "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz", | ||||
|             "integrity": "sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==", | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "sortablejs": "1.10.2" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/vuetify": { | ||||
|             "version": "3.8.2", | ||||
|             "license": "MIT", | ||||
|  |  | |||
		Reference in a new issue
	
	 Joyelle Ndagijimana
						Joyelle Ndagijimana