feat: kleine correctie in form component
This commit is contained in:
		
							parent
							
								
									36f7cfa4bd
								
							
						
					
					
						commit
						83b15a392d
					
				
					 3 changed files with 64 additions and 53 deletions
				
			
		|  | @ -4,37 +4,30 @@ import { useI18n } from 'vue-i18n'; | ||||||
| 
 | 
 | ||||||
| const props = defineProps({ | const props = defineProps({ | ||||||
|     students: Array, // All students |     students: Array, // All students | ||||||
|     availableClasses: Array, // Selected classes |     availableClass: Object, // Selected class | ||||||
|     groups: Array, // All groups |     groups: Array, // All groups | ||||||
| }); | }); | ||||||
| const emit = defineEmits(['groupCreated']); | const emit = defineEmits(['groupCreated']); | ||||||
| const { t } = useI18n(); | const { t } = useI18n(); | ||||||
| 
 | 
 | ||||||
| const selectedClass = ref(null); |  | ||||||
| const selectedStudents = ref([]); | const selectedStudents = ref([]); | ||||||
| 
 | 
 | ||||||
| // Filter students based on the selected class and exclude students already in a group | // Filter students based on the selected class and exclude students already in a group | ||||||
| const filteredStudents = computed(() => { | const filteredStudents = computed(() => { | ||||||
|     if (selectedClass.value) { |     if (props.availableClass) { | ||||||
|         // Find the class based on selectedClass id |         const studentsInClass = props.availableClass.students.map(st => ({ | ||||||
|         const selected = props.availableClasses.find(cl => cl.id === selectedClass.value.id); |             title: `${st.firstName} ${st.lastName}`, | ||||||
|         if (selected) { |             value: st.username, | ||||||
|             // Get all students from the selected class |         })); | ||||||
|             const studentsInClass = selected.students.map(st => ({ |  | ||||||
|                 title: `${st.firstName} ${st.lastName}`, |  | ||||||
|                 value: st.username, |  | ||||||
|             })); |  | ||||||
| 
 | 
 | ||||||
|             // Get the list of students already in any group |         const studentsInGroups = props.groups.flat(); | ||||||
|             const studentsInGroups = props.groups.flat(); |  | ||||||
| 
 | 
 | ||||||
|             // Filter out students that are already in a group |         return studentsInClass.filter(student => !studentsInGroups.includes(student.value)); | ||||||
|             return studentsInClass.filter(student => !studentsInGroups.includes(student.value)); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|     return []; |     return []; | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| const createGroup = () => { | const createGroup = () => { | ||||||
|     if (selectedStudents.value.length) { |     if (selectedStudents.value.length) { | ||||||
|         // Extract only usernames (student.value) |         // Extract only usernames (student.value) | ||||||
|  | @ -48,19 +41,6 @@ const createGroup = () => { | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|     <v-card-text> |     <v-card-text> | ||||||
|         <v-combobox |  | ||||||
|             v-model="selectedClass" |  | ||||||
|             :items="props.availableClasses" |  | ||||||
|             item-title="displayName" |  | ||||||
|             item-value="id" |  | ||||||
|             :label="t('pick-class')" |  | ||||||
|             variant="outlined" |  | ||||||
|             clearable |  | ||||||
|             hide-details |  | ||||||
|             density="compact" |  | ||||||
|             class="mb-4" |  | ||||||
|         ></v-combobox> |  | ||||||
| 
 |  | ||||||
|         <v-combobox |         <v-combobox | ||||||
|             v-model="selectedStudents" |             v-model="selectedStudents" | ||||||
|             :items="filteredStudents" |             :items="filteredStudents" | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
|  * @param selectedClasses - The selected classes, an array of class objects. |  * @param selectedClasses - The selected classes, an array of class objects. | ||||||
|  * @param groups - An array of groups, each containing student IDs. |  * @param groups - An array of groups, each containing student IDs. | ||||||
|  * @param deadline - The deadline of the assignment in ISO format. |  * @param deadline - The deadline of the assignment in ISO format. | ||||||
|  |  * @param description - The description of the aasignment | ||||||
|  * Sends a POST request to the backend with the form data. |  * Sends a POST request to the backend with the form data. | ||||||
|  */ |  */ | ||||||
| export const submitForm = async ( | export const submitForm = async ( | ||||||
|  | @ -13,14 +14,16 @@ export const submitForm = async ( | ||||||
|     selectedLearningPath: any, |     selectedLearningPath: any, | ||||||
|     selectedClasses: any[], |     selectedClasses: any[], | ||||||
|     groups: string[][], |     groups: string[][], | ||||||
|     deadline: string |     deadline: string, | ||||||
|  |     description: string | ||||||
| ) => { | ) => { | ||||||
|     const formData = { |     const formData = { | ||||||
|         title: assignmentTitle, |         title: assignmentTitle, | ||||||
|         hruid: selectedLearningPath?.hruid, |         hruid: selectedLearningPath?.hruid, | ||||||
|         classes: selectedClasses.map(cl => cl.value), |         classes: selectedClasses.map(cl => cl.value), | ||||||
|         groups: groups, |         groups: groups, | ||||||
|         deadline: deadline |         deadline: deadline, | ||||||
|  |         description: description | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     try { |     try { | ||||||
|  | @ -93,3 +96,10 @@ export const deadlineRules = [ | ||||||
|         return true; |         return true; | ||||||
|     }, |     }, | ||||||
| ]; | ]; | ||||||
|  | 
 | ||||||
|  | export const descriptionRules = [ | ||||||
|  |     (value: string) => { | ||||||
|  |         if (!value || value.trim() === "") return "Description cannot be empty."; | ||||||
|  |         return true; | ||||||
|  |     }, | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | @ -3,7 +3,13 @@ | ||||||
|     import {computed, onMounted, ref, watch} from "vue"; |     import {computed, onMounted, ref, watch} from "vue"; | ||||||
|     import GroupSelector from "@/components/GroupSelector.vue"; |     import GroupSelector from "@/components/GroupSelector.vue"; | ||||||
|     import {classes} from "@/utils/tempData.ts"; |     import {classes} from "@/utils/tempData.ts"; | ||||||
|     import {assignmentTitleRules, classesRules, learningPathRules, submitForm} from "@/utils/assignmentForm.ts"; |     import { | ||||||
|  |         assignmentTitleRules, | ||||||
|  |         classesRules, | ||||||
|  |         descriptionRules, | ||||||
|  |         learningPathRules, | ||||||
|  |         submitForm | ||||||
|  |     } from "@/utils/assignmentForm.ts"; | ||||||
|     import DeadlineSelector from "@/components/DeadlineSelector.vue"; |     import DeadlineSelector from "@/components/DeadlineSelector.vue"; | ||||||
| 
 | 
 | ||||||
|     const {t, locale} = useI18n(); |     const {t, locale} = useI18n(); | ||||||
|  | @ -16,27 +22,28 @@ | ||||||
| 
 | 
 | ||||||
|     const assignmentTitle = ref(''); |     const assignmentTitle = ref(''); | ||||||
|     const deadline = ref(null); |     const deadline = ref(null); | ||||||
|  |     const description = ref(''); | ||||||
|     const allLearningPaths = ref([]); |     const allLearningPaths = ref([]); | ||||||
|     const filteredLearningPaths = ref([]); |     const filteredLearningPaths = ref([]); | ||||||
|     const selectedLearningPath = ref(null); |     const selectedLearningPath = ref(null); | ||||||
|     const allClasses = ref([...classes.map(cl => ({title: cl.displayName, value: cl.id}))]); |     const allClasses = ref([...classes.map(cl => ({title: cl.displayName, value: cl.id}))]); | ||||||
|     const selectedClasses = ref([]); |     const selectedClass = ref(null); | ||||||
|     const groups = ref<string[][]>([]); |     const groups = ref<string[][]>([]); | ||||||
| 
 | 
 | ||||||
|     const availableClasses = computed(() => { |     const availableClass = computed(() => { | ||||||
|         //TODO: replace by real data |         //TODO: replace by real data | ||||||
|         return classes.filter(cl => selectedClasses.value.some(c => c.value === cl.id)); |         return classes.find(cl => selectedClass.value?.value === cl.id) || null; | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const allStudents = computed(() => { |     const allStudents = computed(() => { | ||||||
|         //TODO: replace by real data |         //TODO: replace by real data | ||||||
|         return classes |         if (!selectedClass.value) return []; | ||||||
|             .filter(cl => selectedClasses.value.some(c => c.value === cl.id)) |         const cl = classes.find(c => c.id === selectedClass.value.value); | ||||||
|             .flatMap(cl => cl.students.map(st => ({ |         return cl ? cl.students.map(st => ({ | ||||||
|                 title: `${st.firstName} ${st.lastName}`, |             title: `${st.firstName} ${st.lastName}`, | ||||||
|                 value: st.username, |             value: st.username, | ||||||
|                 classes: cl |             classes: cl | ||||||
|             }))); |         })) : []; | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -74,6 +81,10 @@ | ||||||
|         {immediate: true} |         {immediate: true} | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  |     watch(selectedClass, () => { | ||||||
|  |         groups.value = []; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     const searchResults = computed(() => { |     const searchResults = computed(() => { | ||||||
|         return filteredLearningPaths.value.filter((lp: { hruid: string; title: string }) => |         return filteredLearningPaths.value.filter((lp: { hruid: string; title: string }) => | ||||||
|             lp.title.toLowerCase().includes(searchQuery.value.toLowerCase()) |             lp.title.toLowerCase().includes(searchQuery.value.toLowerCase()) | ||||||
|  | @ -84,10 +95,9 @@ | ||||||
| 
 | 
 | ||||||
|     const submitFormHandler = async () => { |     const submitFormHandler = async () => { | ||||||
|         const { valid } = await form.value.validate(); |         const { valid } = await form.value.validate(); | ||||||
|         console.log(valid); |         // Don't submit thr form if all rules don't apply | ||||||
|         console.log(deadline); |  | ||||||
|         if (!valid) return; |         if (!valid) return; | ||||||
|         submitForm(assignmentTitle.value, selectedLearningPath.value, selectedClasses.value, groups.value, deadline.value); |         submitForm(assignmentTitle.value, selectedLearningPath.value, selectedClass.value, groups.value, deadline.value, description.value); | ||||||
|     }; |     }; | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | @ -123,21 +133,20 @@ | ||||||
| 
 | 
 | ||||||
|                     <v-card-text> |                     <v-card-text> | ||||||
|                         <v-combobox |                         <v-combobox | ||||||
|                             v-model="selectedClasses" |                             v-model="selectedClass" | ||||||
|                             :items="allClasses" |                             :items="allClasses" | ||||||
|                             :label="t('choose-classes')" |                             :label="t('pick-class')" | ||||||
|                             :rules="classesRules" |                             :rules="classesRules" | ||||||
|                             variant="outlined" |                             variant="outlined" | ||||||
|                             clearable |                             clearable | ||||||
|                             multiple |  | ||||||
|                             hide-details |                             hide-details | ||||||
|                             density="compact" |                             density="compact" | ||||||
|                             chips |  | ||||||
|                             append-inner-icon="mdi-magnify" |                             append-inner-icon="mdi-magnify" | ||||||
|                             item-title="title" |                             item-title="title" | ||||||
|                             item-value="value" |                             item-value="value" | ||||||
|                             required |                             required | ||||||
|                         ></v-combobox> |                         ></v-combobox> | ||||||
|  | 
 | ||||||
|                     </v-card-text> |                     </v-card-text> | ||||||
| 
 | 
 | ||||||
|                     <DeadlineSelector v-model:deadline="deadline" /> |                     <DeadlineSelector v-model:deadline="deadline" /> | ||||||
|  | @ -146,7 +155,7 @@ | ||||||
| 
 | 
 | ||||||
|                     <GroupSelector |                     <GroupSelector | ||||||
|                         :students="allStudents" |                         :students="allStudents" | ||||||
|                         :availableClasses="availableClasses" |                         :availableClass="availableClass" | ||||||
|                         :groups="groups" |                         :groups="groups" | ||||||
|                         @groupCreated="addGroupToList" |                         @groupCreated="addGroupToList" | ||||||
|                     /> |                     /> | ||||||
|  | @ -156,8 +165,21 @@ | ||||||
|                         <strong>Created Groups: {{ groups.length }}</strong> |                         <strong>Created Groups: {{ groups.length }}</strong> | ||||||
|                     </v-card-text> |                     </v-card-text> | ||||||
| 
 | 
 | ||||||
|  |                     <v-card-text> | ||||||
|  |                         <v-textarea | ||||||
|  |                             v-model="description" | ||||||
|  |                             :label="t('description')" | ||||||
|  |                             variant="outlined" | ||||||
|  |                             density="compact" | ||||||
|  |                             auto-grow | ||||||
|  |                             rows="3" | ||||||
|  |                             :rules="descriptionRules" | ||||||
|  |                         ></v-textarea> | ||||||
|  |                     </v-card-text> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |                     <v-btn class="mt-2" color="secondary" type="submit" block>Submit</v-btn> | ||||||
|                 </v-container> |                 </v-container> | ||||||
|                 <v-btn class="mt-2" color="secondary" type="submit" block>Submit</v-btn> |  | ||||||
|             </v-form> |             </v-form> | ||||||
|         </v-card> |         </v-card> | ||||||
|     </div> |     </div> | ||||||
|  | @ -169,7 +191,6 @@ | ||||||
|     flex-direction: column; |     flex-direction: column; | ||||||
|     align-items: center; |     align-items: center; | ||||||
|     justify-content: center; |     justify-content: center; | ||||||
|     height: 100vh; |  | ||||||
|     text-align: center; |     text-align: center; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -212,7 +233,7 @@ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Responsive adjustments */ | /* Responsive adjustments */ | ||||||
| @media (max-width: 700px) { | @media (max-width: 650px) { | ||||||
|     .form-card { |     .form-card { | ||||||
|         width: 95%; |         width: 95%; | ||||||
|     } |     } | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 Joyelle Ndagijimana
						Joyelle Ndagijimana