feat: deadline is editeerbaar
This commit is contained in:
		
							parent
							
								
									912111fce4
								
							
						
					
					
						commit
						f67e3f5a1a
					
				
					 5 changed files with 163 additions and 145 deletions
				
			
		|  | @ -2,15 +2,21 @@ | ||||||
|     import { ref, watch } from "vue"; |     import { ref, watch } from "vue"; | ||||||
|     import { deadlineRules } from "@/utils/assignment-rules.ts"; |     import { deadlineRules } from "@/utils/assignment-rules.ts"; | ||||||
| 
 | 
 | ||||||
|     const emit = defineEmits<(e: "update:deadline", value: Date) => void>(); |     const emit = defineEmits<(e: "update:deadline", value: Date | null) => void>(); | ||||||
|  |     const props = defineProps<{ deadline: Date | null }>(); | ||||||
| 
 | 
 | ||||||
|     const datetime = ref(""); |     const datetime = ref(""); | ||||||
| 
 | 
 | ||||||
|  |     datetime.value = props.deadline ? new Date(props.deadline).toISOString().slice(0, 16) : "" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     // Watch the datetime value and emit the update |     // Watch the datetime value and emit the update | ||||||
|     watch(datetime, (val) => { |     watch(datetime, (val) => { | ||||||
|         const newDate = new Date(val); |         const newDate = new Date(val); | ||||||
|         if (!isNaN(newDate.getTime())) { |         if (!isNaN(newDate.getTime())) { | ||||||
|             emit("update:deadline", newDate); |             emit("update:deadline", newDate); | ||||||
|  |         } else { | ||||||
|  |             emit("update:deadline", null); | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -166,5 +166,11 @@ | ||||||
|     "pathContainsNonExistingLearningObjects": "At least one of the learning objects referenced in this path does not exist.", |     "pathContainsNonExistingLearningObjects": "At least one of the learning objects referenced in this path does not exist.", | ||||||
|     "targetAgesMandatory": "Target ages must be specified.", |     "targetAgesMandatory": "Target ages must be specified.", | ||||||
|     "hintRemoveIfUnconditionalTransition": "(remove this if this should be an unconditional transition)", |     "hintRemoveIfUnconditionalTransition": "(remove this if this should be an unconditional transition)", | ||||||
|     "hintKeywordsSeparatedBySpaces": "Keywords separated by spaces" |     "hintKeywordsSeparatedBySpaces": "Keywords separated by spaces", | ||||||
|  |     "title-required": "Title cannot be empty.", | ||||||
|  |     "class-required": "You must select at least one class.", | ||||||
|  |     "deadline-invalid": "Invalid date or time.", | ||||||
|  |     "deadline-past": "The deadline must be in the future.", | ||||||
|  |     "lp-required": "You must select a learning path.", | ||||||
|  |     "lp-invalied": "The selected learning path doesn't exist." | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,3 +1,7 @@ | ||||||
|  | import {useI18n} from "vue-i18n"; | ||||||
|  | 
 | ||||||
|  | const { t } = useI18n(); | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Validation rule for the assignment title. |  * Validation rule for the assignment title. | ||||||
|  * |  * | ||||||
|  | @ -8,7 +12,7 @@ export const assignmentTitleRules = [ | ||||||
|         if (value?.length >= 1) { |         if (value?.length >= 1) { | ||||||
|             return true; |             return true; | ||||||
|         } // Title must not be empty
 |         } // Title must not be empty
 | ||||||
|         return "Title cannot be empty."; |         return t("title-required"); | ||||||
|     }, |     }, | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
|  | @ -23,7 +27,7 @@ export const classRules = [ | ||||||
|         if (value) { |         if (value) { | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|         return "You must select at least one class."; |         return t("class-required"); | ||||||
|     }, |     }, | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
|  | @ -34,30 +38,18 @@ export const classRules = [ | ||||||
|  */ |  */ | ||||||
| export const deadlineRules = [ | export const deadlineRules = [ | ||||||
|     (value: string): string | boolean => { |     (value: string): string | boolean => { | ||||||
|         if (!value) { |  | ||||||
|             return "You must set a deadline."; |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         const selectedDateTime = new Date(value); |         const selectedDateTime = new Date(value); | ||||||
|         const now = new Date(); |         const now = new Date(); | ||||||
| 
 | 
 | ||||||
|         if (isNaN(selectedDateTime.getTime())) { |         if (isNaN(selectedDateTime.getTime())) { | ||||||
|             return "Invalid date or time."; |             return t("deadline-invalid"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (selectedDateTime <= now) { |         if (selectedDateTime <= now) { | ||||||
|             return "The deadline must be in the future."; |             return t("deadline-past"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return true; |         return true; | ||||||
|     }, |     }, | ||||||
| ]; | ]; | ||||||
| 
 |  | ||||||
| export const descriptionRules = [ |  | ||||||
|     (value: string): string | boolean => { |  | ||||||
|         if (!value || value.trim() === "") { |  | ||||||
|             return "Description cannot be empty."; |  | ||||||
|         } |  | ||||||
|         return true; |  | ||||||
|     }, |  | ||||||
| ]; |  | ||||||
|  |  | ||||||
|  | @ -34,9 +34,20 @@ | ||||||
| 
 | 
 | ||||||
| const selectedClass = ref(undefined); | const selectedClass = ref(undefined); | ||||||
| const assignmentTitle = ref(""); | const assignmentTitle = ref(""); | ||||||
|     const selectedLearningPath = ref(route.query.hruid?.toString() || undefined); |  | ||||||
| 
 | 
 | ||||||
|     const lpIsSelected = route.query.hruid !== undefined; | const selectedLearningPath = ref<LearningPath | undefined>(undefined); | ||||||
|  | const lpIsSelected = ref(false); | ||||||
|  | watch(learningPathsQueryResults.data, (data) => { | ||||||
|  |     const hruidFromRoute = route.query.hruid?.toString(); | ||||||
|  |     if (!hruidFromRoute || !data) return; | ||||||
|  | 
 | ||||||
|  |     // Verify if the hruid given in the url is valid before accepting it | ||||||
|  |     const matchedLP = data.find(lp => lp.hruid === hruidFromRoute); | ||||||
|  |     if (matchedLP) { | ||||||
|  |         selectedLearningPath.value = matchedLP; | ||||||
|  |         lpIsSelected.value = true; | ||||||
|  |     } | ||||||
|  | }); | ||||||
| 
 | 
 | ||||||
| const { mutate, data, isSuccess } = useCreateAssignmentMutation(); | const { mutate, data, isSuccess } = useCreateAssignmentMutation(); | ||||||
| 
 | 
 | ||||||
|  | @ -50,7 +61,7 @@ | ||||||
|     const { valid } = await form.value.validate(); |     const { valid } = await form.value.validate(); | ||||||
|     if (!valid) return; |     if (!valid) return; | ||||||
| 
 | 
 | ||||||
|         const lp = lpIsSelected |     const lp = lpIsSelected.value | ||||||
|         ? route.query.hruid |         ? route.query.hruid | ||||||
|         : selectedLearningPath.value?.hruid; |         : selectedLearningPath.value?.hruid; | ||||||
| 
 | 
 | ||||||
|  | @ -58,12 +69,6 @@ | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|         console.log('Form values:', { |  | ||||||
|             title: assignmentTitle.value, |  | ||||||
|             class: selectedClass.value, |  | ||||||
|             lp: selectedLearningPath.value |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     const assignmentDTO: AssignmentDTO = { |     const assignmentDTO: AssignmentDTO = { | ||||||
|         id: 0, |         id: 0, | ||||||
|         within: selectedClass.value?.id || "", |         within: selectedClass.value?.id || "", | ||||||
|  | @ -80,12 +85,17 @@ | ||||||
| 
 | 
 | ||||||
| const learningPathRules = [ | const learningPathRules = [ | ||||||
|     (value: any) => { |     (value: any) => { | ||||||
|             // Skip validation if LP is selected from query | 
 | ||||||
|             if (route.query.hruid) return true; |         if(lpIsSelected.value) return; | ||||||
|             // Original validation logic | 
 | ||||||
|             return Boolean(value) || 'Learning path is required'; |         if (!value) return t("lp-required"); | ||||||
|  | 
 | ||||||
|  |         const allLPs = learningPathsQueryResults.data.value ?? []; | ||||||
|  |         const valid = allLPs.some(lp => lp.hruid === value?.hruid); | ||||||
|  |         return valid || t("lp-invalid"); | ||||||
|     } |     } | ||||||
| ]; | ]; | ||||||
|  | 
 | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|  | @ -118,7 +128,6 @@ | ||||||
|                         v-slot="{ data }: { data: LearningPath[] }" |                         v-slot="{ data }: { data: LearningPath[] }" | ||||||
|                     > |                     > | ||||||
|                         <v-combobox |                         <v-combobox | ||||||
|                             v-model="selectedLearningPath" |  | ||||||
|                             :items="data" |                             :items="data" | ||||||
|                             :label="t('choose-lp')" |                             :label="t('choose-lp')" | ||||||
|                             :rules="lpIsSelected ? [] : learningPathRules" |                             :rules="lpIsSelected ? [] : learningPathRules" | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import {computed, type Ref, ref, watch, watchEffect} from "vue"; | import {computed, ref, watch, watchEffect} from "vue"; | ||||||
| import {useI18n} from "vue-i18n"; | import {useI18n} from "vue-i18n"; | ||||||
| import { | import { | ||||||
|     useAssignmentQuery, |     useAssignmentQuery, | ||||||
|  | @ -12,11 +12,11 @@ import {useGetLearningPathQuery} from "@/queries/learning-paths.ts"; | ||||||
| import type {Language} from "@/data-objects/language.ts"; | import type {Language} from "@/data-objects/language.ts"; | ||||||
| import type {AssignmentResponse} from "@/controllers/assignments.ts"; | import type {AssignmentResponse} from "@/controllers/assignments.ts"; | ||||||
| import type {GroupDTO, GroupDTOId} from "@dwengo-1/common/interfaces/group"; | import type {GroupDTO, GroupDTOId} from "@dwengo-1/common/interfaces/group"; | ||||||
| import {descriptionRules} from "@/utils/assignment-rules.ts"; |  | ||||||
| import GroupSubmissionStatus from "@/components/GroupSubmissionStatus.vue"; | import GroupSubmissionStatus from "@/components/GroupSubmissionStatus.vue"; | ||||||
| import GroupProgressRow from "@/components/GroupProgressRow.vue"; | import GroupProgressRow from "@/components/GroupProgressRow.vue"; | ||||||
| import type {AssignmentDTO} from "@dwengo-1/common/dist/interfaces/assignment.ts"; | import type {AssignmentDTO} from "@dwengo-1/common/dist/interfaces/assignment.ts"; | ||||||
| import GroupSelector from "@/components/assignments/GroupSelector.vue"; | import GroupSelector from "@/components/assignments/GroupSelector.vue"; | ||||||
|  | import DeadlineSelector from "@/components/assignments/DeadlineSelector.vue"; | ||||||
| 
 | 
 | ||||||
| const props = defineProps<{ | const props = defineProps<{ | ||||||
|     classId: string; |     classId: string; | ||||||
|  | @ -33,6 +33,7 @@ const form = ref(); | ||||||
| 
 | 
 | ||||||
| const editingLearningPath = ref(learningPath); | const editingLearningPath = ref(learningPath); | ||||||
| const description = ref(""); | const description = ref(""); | ||||||
|  | const deadline = ref<Date | null>(null); | ||||||
| const editGroups = ref(false); | const editGroups = ref(false); | ||||||
| 
 | 
 | ||||||
| const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); | const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); | ||||||
|  | @ -51,6 +52,7 @@ watchEffect(() => { | ||||||
|     if (assignment) { |     if (assignment) { | ||||||
|         learningPath.value = assignment.learningPath; |         learningPath.value = assignment.learningPath; | ||||||
|         lang.value = assignment.language as Language; |         lang.value = assignment.language as Language; | ||||||
|  |         deadline.value = assignment.deadline ? new Date(assignment.deadline) : null; | ||||||
| 
 | 
 | ||||||
|         if (lpQueryResult.data.value) { |         if (lpQueryResult.data.value) { | ||||||
|             editingLearningPath.value = lpQueryResult.data.value; |             editingLearningPath.value = lpQueryResult.data.value; | ||||||
|  | @ -128,7 +130,7 @@ async function saveChanges(): Promise<void> { | ||||||
| 
 | 
 | ||||||
|     const assignmentDTO: AssignmentDTO = { |     const assignmentDTO: AssignmentDTO = { | ||||||
|         description: description.value, |         description: description.value, | ||||||
|         //deadline: new Date(),TODO: deadline aanpassen |         deadline: deadline.value ?? null, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     mutate({ |     mutate({ | ||||||
|  | @ -244,7 +246,6 @@ async function handleGroupsUpdated(updatedGroups: string[][]): Promise<void> { | ||||||
|                                         </div> |                                         </div> | ||||||
|                                     </div> |                                     </div> | ||||||
|                                 </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> | ||||||
|  | @ -258,6 +259,7 @@ async function handleGroupsUpdated(updatedGroups: string[][]): Promise<void> { | ||||||
|                                             :to="goToLearningPathLink()" |                                             :to="goToLearningPathLink()" | ||||||
|                                             variant="tonal" |                                             variant="tonal" | ||||||
|                                             color="primary" |                                             color="primary" | ||||||
|  |                                             :disabled="isEditing" | ||||||
|                                         > |                                         > | ||||||
|                                             {{ t("learning-path") }} |                                             {{ t("learning-path") }} | ||||||
|                                         </v-btn> |                                         </v-btn> | ||||||
|  | @ -269,7 +271,11 @@ async function handleGroupsUpdated(updatedGroups: string[][]): Promise<void> { | ||||||
|                                         </v-alert> |                                         </v-alert> | ||||||
|                                     </using-query-result> |                                     </using-query-result> | ||||||
|                                 </v-card-subtitle> |                                 </v-card-subtitle> | ||||||
| 
 |                                 <v-card-text v-if="isEditing"> | ||||||
|  |                                     <deadline-selector | ||||||
|  |                                         v-model:deadline="deadline" | ||||||
|  |                                     /> | ||||||
|  |                                 </v-card-text> | ||||||
|                                 <v-card-text |                                 <v-card-text | ||||||
|                                     v-if="!isEditing" |                                     v-if="!isEditing" | ||||||
|                                     class="description" |                                     class="description" | ||||||
|  | @ -284,7 +290,6 @@ async function handleGroupsUpdated(updatedGroups: string[][]): Promise<void> { | ||||||
|                                         density="compact" |                                         density="compact" | ||||||
|                                         auto-grow |                                         auto-grow | ||||||
|                                         rows="3" |                                         rows="3" | ||||||
|                                         :rules="descriptionRules" |  | ||||||
|                                     ></v-textarea> |                                     ></v-textarea> | ||||||
|                                 </v-card-text> |                                 </v-card-text> | ||||||
|                             </v-card> |                             </v-card> | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 Joyelle Ndagijimana
						Joyelle Ndagijimana