style: fix linting issues met Prettier
This commit is contained in:
		
							parent
							
								
									ef5c51b463
								
							
						
					
					
						commit
						11600b8be4
					
				
					 11 changed files with 578 additions and 541 deletions
				
			
		|  | @ -1,21 +1,21 @@ | |||
| <script setup lang="ts"> | ||||
| import {ref, computed, defineEmits} from "vue"; | ||||
| import {deadlineRules} from "@/utils/assignment-rules.ts"; | ||||
|     import { ref, computed, defineEmits } from "vue"; | ||||
|     import { deadlineRules } from "@/utils/assignment-rules.ts"; | ||||
| 
 | ||||
| const date = ref(""); | ||||
| const time = ref("23:59"); | ||||
| const emit = defineEmits(["update:deadline"]); | ||||
|     const date = ref(""); | ||||
|     const time = ref("23:59"); | ||||
|     const emit = defineEmits(["update:deadline"]); | ||||
| 
 | ||||
| const formattedDeadline = computed(() => { | ||||
|     if (!date.value || !time.value) return ""; | ||||
|     return `${date.value} ${time.value}`; | ||||
| }); | ||||
|     const formattedDeadline = computed(() => { | ||||
|         if (!date.value || !time.value) return ""; | ||||
|         return `${date.value} ${time.value}`; | ||||
|     }); | ||||
| 
 | ||||
| function updateDeadline(): void { | ||||
|     if (date.value && time.value) { | ||||
|         emit("update:deadline", formattedDeadline.value); | ||||
|     function updateDeadline(): void { | ||||
|         if (date.value && time.value) { | ||||
|             emit("update:deadline", formattedDeadline.value); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|  | @ -46,6 +46,4 @@ function updateDeadline(): void { | |||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
| 
 | ||||
| </style> | ||||
| <style scoped></style> | ||||
|  |  | |||
|  | @ -1,51 +1,49 @@ | |||
| <script setup lang="ts"> | ||||
| import {ref, defineProps, defineEmits} from 'vue'; | ||||
| import {useI18n} from 'vue-i18n'; | ||||
| import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||
| import type {StudentsResponse} from "@/controllers/students.ts"; | ||||
| import {useClassStudentsQuery} from "@/queries/classes.ts"; | ||||
|     import { ref, defineProps, defineEmits } from "vue"; | ||||
|     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 {t} = useI18n(); | ||||
|     const props = defineProps<{ | ||||
|         classId: string | undefined; | ||||
|         groups: string[][]; | ||||
|     }>(); | ||||
|     const emit = defineEmits(["groupCreated"]); | ||||
|     const { t } = useI18n(); | ||||
| 
 | ||||
| const selectedStudents = ref([]); | ||||
|     const selectedStudents = ref([]); | ||||
| 
 | ||||
| const studentQueryResult = useClassStudentsQuery(() => props.classId, true); | ||||
|     const studentQueryResult = useClassStudentsQuery(() => props.classId, true); | ||||
| 
 | ||||
| function filterStudents(data: StudentsResponse): { title: string, value: string }[] { | ||||
|     const students = data.students; | ||||
|     const studentsInGroups = props.groups.flat(); | ||||
|     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 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 | ||||
|         return students | ||||
|             ?.map((st) => ({ | ||||
|                 title: `${st.firstName} ${st.lastName}`, | ||||
|                 value: st.username, | ||||
|             })) | ||||
|             .filter((student) => !studentsInGroups.includes(student.value)); | ||||
|     } | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
|     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 | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|     <using-query-result | ||||
|         :query-result="studentQueryResult" | ||||
|         v-slot="{ data }: { data: StudentsResponse }" | ||||
|     > | ||||
|         <h3>{{ t('create-groups') }}</h3> | ||||
|         <h3>{{ t("create-groups") }}</h3> | ||||
|         <v-card-text> | ||||
|             <v-combobox | ||||
|                 v-model="selectedStudents" | ||||
|  | @ -62,14 +60,16 @@ function createGroup(): void { | |||
|                 append-inner-icon="mdi-magnify" | ||||
|             ></v-combobox> | ||||
| 
 | ||||
|             <v-btn @click="createGroup" color="primary" class="mt-2" size="small"> | ||||
|                 {{ t('create-group') }} | ||||
|             <v-btn | ||||
|                 @click="createGroup" | ||||
|                 color="primary" | ||||
|                 class="mt-2" | ||||
|                 size="small" | ||||
|             > | ||||
|                 {{ t("create-group") }} | ||||
|             </v-btn> | ||||
|         </v-card-text> | ||||
|     </using-query-result> | ||||
| 
 | ||||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
| 
 | ||||
| </style> | ||||
| <style scoped></style> | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| import {type MaybeRefOrGetter, toValue} from "vue"; | ||||
| import type {Language} from "@/data-objects/language.ts"; | ||||
| import {useQuery, type UseQueryReturnType} from "@tanstack/vue-query"; | ||||
| import {getLearningPathController} from "@/controllers/controllers"; | ||||
| import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||
| import { type MaybeRefOrGetter, toValue } from "vue"; | ||||
| import type { Language } from "@/data-objects/language.ts"; | ||||
| import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; | ||||
| import { getLearningPathController } from "@/controllers/controllers"; | ||||
| import type { LearningPath } from "@/data-objects/learning-paths/learning-path.ts"; | ||||
| 
 | ||||
| export const LEARNING_PATH_KEY = "learningPath"; | ||||
| const learningPathController = getLearningPathController(); | ||||
|  | @ -47,7 +47,8 @@ export function useSearchLearningPathQuery( | |||
|     }); | ||||
| } | ||||
| 
 | ||||
| export function useGetAllLearningPaths(language: MaybeRefOrGetter<string | undefined> | ||||
| export function useGetAllLearningPaths( | ||||
|     language: MaybeRefOrGetter<string | undefined>, | ||||
| ): UseQueryReturnType<LearningPath[], Error> { | ||||
|     return useQuery({ | ||||
|         queryKey: [LEARNING_PATH_KEY, "getAllLearningPaths", language], | ||||
|  | @ -55,6 +56,6 @@ export function useGetAllLearningPaths(language: MaybeRefOrGetter<string | undef | |||
|             const lang = toValue(language); | ||||
|             return learningPathController.getAllLearningPaths(lang); | ||||
|         }, | ||||
|         enabled: () => Boolean(toValue(language)) | ||||
|         enabled: () => Boolean(toValue(language)), | ||||
|     }); | ||||
| } | ||||
|  |  | |||
|  | @ -1,9 +1,10 @@ | |||
| import {computed, type Ref, toValue} from "vue"; | ||||
| import { computed, type Ref, toValue } from "vue"; | ||||
| import type { MaybeRefOrGetter } from "vue"; | ||||
| import { | ||||
|     type QueryObserverResult, | ||||
|     useMutation, | ||||
|     type UseMutationReturnType, useQueries, | ||||
|     type UseMutationReturnType, | ||||
|     useQueries, | ||||
|     useQuery, | ||||
|     useQueryClient, | ||||
|     type UseQueryReturnType, | ||||
|  | @ -72,7 +73,7 @@ export function useStudentQuery( | |||
| } | ||||
| 
 | ||||
| export function useStudentsByUsernamesQuery( | ||||
|     usernames: MaybeRefOrGetter<string[] | undefined> | ||||
|     usernames: MaybeRefOrGetter<string[] | undefined>, | ||||
| ): Ref<QueryObserverResult<StudentResponse>[]> { | ||||
|     const resolvedUsernames = toValue(usernames) ?? []; | ||||
| 
 | ||||
|  |  | |||
|  | @ -73,7 +73,7 @@ const router = createRouter({ | |||
|         }, | ||||
|         { | ||||
|             path: "/assignment", | ||||
|             meta: {requiresAuth: true}, | ||||
|             meta: { requiresAuth: true }, | ||||
|             children: [ | ||||
|                 { | ||||
|                     path: "create", | ||||
|  | @ -85,7 +85,7 @@ const router = createRouter({ | |||
|                     name: "SingleAssigment", | ||||
|                     component: SingleAssignment, | ||||
|                 }, | ||||
|             ] | ||||
|             ], | ||||
|         }, | ||||
|         { | ||||
|             path: "/class/:id", | ||||
|  |  | |||
|  | @ -5,8 +5,10 @@ | |||
|  */ | ||||
| export const assignmentTitleRules = [ | ||||
|     (value: string): string | boolean => { | ||||
|         if (value?.length >= 1) {return true;}  // Title must not be empty
 | ||||
|         return 'Title cannot be empty.'; | ||||
|         if (value?.length >= 1) { | ||||
|             return true; | ||||
|         } // Title must not be empty
 | ||||
|         return "Title cannot be empty."; | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
|  | @ -18,9 +20,9 @@ export const assignmentTitleRules = [ | |||
| export const learningPathRules = [ | ||||
|     (value: { hruid: string; title: string }): string | boolean => { | ||||
|         if (value && value.hruid) { | ||||
|             return true;  // Valid if hruid is present
 | ||||
|             return true; // Valid if hruid is present
 | ||||
|         } | ||||
|         return 'You must select a learning path.'; | ||||
|         return "You must select a learning path."; | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
|  | @ -31,8 +33,10 @@ export const learningPathRules = [ | |||
|  */ | ||||
| export const classRules = [ | ||||
|     (value: string): string | boolean => { | ||||
|         if (value) {return true;} | ||||
|         return 'You must select at least one class.'; | ||||
|         if (value) { | ||||
|             return true; | ||||
|         } | ||||
|         return "You must select at least one class."; | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
|  | @ -43,14 +47,20 @@ export const classRules = [ | |||
|  */ | ||||
| export const deadlineRules = [ | ||||
|     (value: string): string | boolean => { | ||||
|         if (!value) {return "You must set a deadline.";} | ||||
|         if (!value) { | ||||
|             return "You must set a deadline."; | ||||
|         } | ||||
| 
 | ||||
|         const selectedDateTime = new Date(value); | ||||
|         const now = new Date(); | ||||
| 
 | ||||
|         if (isNaN(selectedDateTime.getTime())) {return "Invalid date or time.";} | ||||
|         if (isNaN(selectedDateTime.getTime())) { | ||||
|             return "Invalid date or time."; | ||||
|         } | ||||
| 
 | ||||
|         if (selectedDateTime <= now) {return "The deadline must be in the future.";} | ||||
|         if (selectedDateTime <= now) { | ||||
|             return "The deadline must be in the future."; | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
|     }, | ||||
|  | @ -58,7 +68,9 @@ export const deadlineRules = [ | |||
| 
 | ||||
| export const descriptionRules = [ | ||||
|     (value: string): string | boolean => { | ||||
|         if (!value || value.trim() === "") {return "Description cannot be empty.";} | ||||
|         if (!value || value.trim() === "") { | ||||
|             return "Description cannot be empty."; | ||||
|         } | ||||
|         return true; | ||||
|     }, | ||||
| ]; | ||||
|  |  | |||
|  | @ -1,101 +1,111 @@ | |||
| <script setup lang="ts"> | ||||
| import {useI18n} from "vue-i18n"; | ||||
| import {computed, onMounted, ref, watch} from "vue"; | ||||
| import GroupSelector from "@/components/assignments/GroupSelector.vue"; | ||||
| import {assignmentTitleRules, classRules, descriptionRules, learningPathRules} from "@/utils/assignment-rules.ts"; | ||||
| import DeadlineSelector from "@/components/assignments/DeadlineSelector.vue"; | ||||
| import auth from "@/services/auth/auth-service.ts"; | ||||
| import {useTeacherClassesQuery} from "@/queries/teachers.ts"; | ||||
| import {useRouter} from "vue-router"; | ||||
| import {useGetAllLearningPaths} from "@/queries/learning-paths.ts"; | ||||
| import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||
| import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||
| import type {ClassesResponse} from "@/controllers/classes.ts"; | ||||
| import type {AssignmentDTO} from "@dwengo-1/common/interfaces/assignment"; | ||||
| import {useCreateAssignmentMutation} from "@/queries/assignments.ts"; | ||||
| import {useRoute} from "vue-router"; | ||||
|     import { useI18n } from "vue-i18n"; | ||||
|     import { computed, onMounted, ref, watch } from "vue"; | ||||
|     import GroupSelector from "@/components/assignments/GroupSelector.vue"; | ||||
|     import { assignmentTitleRules, classRules, descriptionRules, learningPathRules } from "@/utils/assignment-rules.ts"; | ||||
|     import DeadlineSelector from "@/components/assignments/DeadlineSelector.vue"; | ||||
|     import auth from "@/services/auth/auth-service.ts"; | ||||
|     import { useTeacherClassesQuery } from "@/queries/teachers.ts"; | ||||
|     import { useRouter } from "vue-router"; | ||||
|     import { useGetAllLearningPaths } from "@/queries/learning-paths.ts"; | ||||
|     import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||
|     import type { LearningPath } from "@/data-objects/learning-paths/learning-path.ts"; | ||||
|     import type { ClassesResponse } from "@/controllers/classes.ts"; | ||||
|     import type { AssignmentDTO } from "@dwengo-1/common/interfaces/assignment"; | ||||
|     import { useCreateAssignmentMutation } from "@/queries/assignments.ts"; | ||||
|     import { useRoute } from "vue-router"; | ||||
| 
 | ||||
|     const route = useRoute(); | ||||
|     const router = useRouter(); | ||||
|     const { t, locale } = useI18n(); | ||||
|     const role = ref(auth.authState.activeRole); | ||||
|     const username = ref<string>(""); | ||||
| 
 | ||||
| const route = useRoute(); | ||||
| const router = useRouter(); | ||||
| const {t, locale} = useI18n(); | ||||
| const role = ref(auth.authState.activeRole); | ||||
| const username = ref<string>(""); | ||||
|     onMounted(async () => { | ||||
|         // Redirect student | ||||
|         if (role.value === "student") { | ||||
|             await router.push("/user"); | ||||
|         } | ||||
| 
 | ||||
| onMounted(async () => { | ||||
|     // Redirect student | ||||
|     if (role.value === 'student') { | ||||
|         await router.push('/user'); | ||||
|         // Get the user's username | ||||
|         const user = await auth.loadUser(); | ||||
|         username.value = user?.profile?.preferred_username ?? ""; | ||||
|     }); | ||||
| 
 | ||||
|     const language = computed(() => locale.value); | ||||
|     const form = ref(); | ||||
| 
 | ||||
|     //Fetch all learning paths | ||||
|     const learningPathsQueryResults = useGetAllLearningPaths(language); | ||||
| 
 | ||||
|     // Fetch and store all the teacher's classes | ||||
|     const classesQueryResults = useTeacherClassesQuery(username, true); | ||||
| 
 | ||||
|     const selectedClass = ref(undefined); | ||||
| 
 | ||||
|     const assignmentTitle = ref(""); | ||||
|     const selectedLearningPath = ref(route.query.hruid || undefined); | ||||
| 
 | ||||
|     // Disable combobox when learningPath prop is passed | ||||
|     const lpIsSelected = route.query.hruid !== undefined; | ||||
|     const deadline = ref(null); | ||||
|     const description = ref(""); | ||||
|     const groups = ref<string[][]>([]); | ||||
| 
 | ||||
|     // New group is added to the list | ||||
|     function addGroupToList(students: string[]): void { | ||||
|         if (students.length) { | ||||
|             groups.value = [...groups.value, students]; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Get the user's username | ||||
|     const user = await auth.loadUser(); | ||||
|     username.value = user?.profile?.preferred_username ?? ""; | ||||
| }); | ||||
|     watch(selectedClass, () => { | ||||
|         groups.value = []; | ||||
|     }); | ||||
| 
 | ||||
|     const { mutate, data, isSuccess } = useCreateAssignmentMutation(); | ||||
| 
 | ||||
| const language = computed(() => locale.value); | ||||
| const form = ref(); | ||||
|     async function submitFormHandler(): Promise<void> { | ||||
|         const { valid } = await form.value.validate(); | ||||
|         if (!valid) return; | ||||
| 
 | ||||
| //Fetch all learning paths | ||||
| const learningPathsQueryResults = useGetAllLearningPaths(language); | ||||
|         const assignmentDTO: AssignmentDTO = { | ||||
|             id: 0, | ||||
|             within: selectedClass.value?.id || "", | ||||
|             title: assignmentTitle.value, | ||||
|             description: description.value, | ||||
|             learningPath: selectedLearningPath.value?.hruid || "", | ||||
|             language: language.value, | ||||
|             groups: groups.value, | ||||
|         }; | ||||
| 
 | ||||
| // Fetch and store all the teacher's classes | ||||
| const classesQueryResults = useTeacherClassesQuery(username, true); | ||||
| 
 | ||||
| const selectedClass = ref(undefined); | ||||
| 
 | ||||
| const assignmentTitle = ref(''); | ||||
| const selectedLearningPath = ref(route.query.hruid || undefined); | ||||
| 
 | ||||
| // Disable combobox when learningPath prop is passed | ||||
| const lpIsSelected = route.query.hruid !== undefined; | ||||
| const deadline = ref(null); | ||||
| const description = ref(''); | ||||
| const groups = ref<string[][]>([]); | ||||
| 
 | ||||
| // New group is added to the list | ||||
| function addGroupToList(students: string[]): void { | ||||
|     if (students.length) { | ||||
|         groups.value = [...groups.value, students]; | ||||
|         mutate({ cid: assignmentDTO.within, data: assignmentDTO }); | ||||
|         if (isSuccess) | ||||
|             await router.push(`/assignment/class/${data.value?.assignment.within}/${data.value?.assignment.id}`); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| watch(selectedClass, () => { | ||||
|     groups.value = []; | ||||
| }); | ||||
| 
 | ||||
| const {mutate, data, isSuccess} = useCreateAssignmentMutation(); | ||||
| 
 | ||||
| async function submitFormHandler(): Promise<void> { | ||||
|     const {valid} = await form.value.validate(); | ||||
|     if (!valid) return; | ||||
| 
 | ||||
|     const assignmentDTO: AssignmentDTO = { | ||||
|         id: 0, | ||||
|         within: selectedClass.value?.id || "", | ||||
|         title: assignmentTitle.value, | ||||
|         description: description.value, | ||||
|         learningPath: selectedLearningPath.value?.hruid || "", | ||||
|         language: language.value, | ||||
|         groups: groups.value | ||||
|     }; | ||||
| 
 | ||||
|     mutate({cid: assignmentDTO.within, data: assignmentDTO}); | ||||
|     if (isSuccess) await router.push(`/assignment/class/${data.value?.assignment.within}/${data.value?.assignment.id}`); | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
| <template> | ||||
|     <div class="main-container"> | ||||
|         <h1 class="title">{{ t("new-assignment") }}</h1> | ||||
|         <v-card class="form-card"> | ||||
|             <v-form ref="form" class="form-container" validate-on="submit lazy" @submit.prevent="submitFormHandler"> | ||||
|             <v-form | ||||
|                 ref="form" | ||||
|                 class="form-container" | ||||
|                 validate-on="submit lazy" | ||||
|                 @submit.prevent="submitFormHandler" | ||||
|             > | ||||
|                 <v-container class="step-container"> | ||||
|                     <v-card-text> | ||||
|                         <v-text-field v-model="assignmentTitle" :label="t('title')" :rules="assignmentTitleRules" | ||||
|                                       density="compact" variant="outlined" clearable required></v-text-field> | ||||
|                         <v-text-field | ||||
|                             v-model="assignmentTitle" | ||||
|                             :label="t('title')" | ||||
|                             :rules="assignmentTitleRules" | ||||
|                             density="compact" | ||||
|                             variant="outlined" | ||||
|                             clearable | ||||
|                             required | ||||
|                         ></v-text-field> | ||||
|                     </v-card-text> | ||||
| 
 | ||||
|                     <using-query-result | ||||
|  | @ -117,14 +127,16 @@ async function submitFormHandler(): Promise<void> { | |||
|                                 item-value="hruid" | ||||
|                                 required | ||||
|                                 :disabled="lpIsSelected" | ||||
|                                 :filter="(item, query: string) => item.title.toLowerCase().includes(query.toLowerCase())" | ||||
|                                 :filter=" | ||||
|                                     (item, query: string) => item.title.toLowerCase().includes(query.toLowerCase()) | ||||
|                                 " | ||||
|                             ></v-combobox> | ||||
|                         </v-card-text> | ||||
|                     </using-query-result> | ||||
| 
 | ||||
|                     <using-query-result | ||||
|                         :query-result="classesQueryResults" | ||||
|                         v-slot="{ data }: {data: ClassesResponse}" | ||||
|                         v-slot="{ data }: { data: ClassesResponse }" | ||||
|                     > | ||||
|                         <v-card-text> | ||||
|                             <v-combobox | ||||
|  | @ -154,7 +166,7 @@ async function submitFormHandler(): Promise<void> { | |||
|                     <v-card-text v-if="groups.length"> | ||||
|                         <strong>Created Groups: {{ groups.length }}</strong> | ||||
|                     </v-card-text> | ||||
|                     <DeadlineSelector v-model:deadline="deadline"/> | ||||
|                     <DeadlineSelector v-model:deadline="deadline" /> | ||||
|                     <v-card-text> | ||||
|                         <v-textarea | ||||
|                             v-model="description" | ||||
|  | @ -167,11 +179,20 @@ async function submitFormHandler(): Promise<void> { | |||
|                         ></v-textarea> | ||||
|                     </v-card-text> | ||||
|                     <v-card-text> | ||||
|                         <v-btn class="mt-2" color="secondary" type="submit" block>{{ t("submit") }}</v-btn> | ||||
|                         <v-btn to="/user/assignment" color="grey" block>{{ t("cancel") }}</v-btn> | ||||
|                         <v-btn | ||||
|                             class="mt-2" | ||||
|                             color="secondary" | ||||
|                             type="submit" | ||||
|                             block | ||||
|                             >{{ t("submit") }}</v-btn | ||||
|                         > | ||||
|                         <v-btn | ||||
|                             to="/user/assignment" | ||||
|                             color="grey" | ||||
|                             block | ||||
|                             >{{ t("cancel") }}</v-btn | ||||
|                         > | ||||
|                     </v-card-text> | ||||
| 
 | ||||
| 
 | ||||
|                 </v-container> | ||||
|             </v-form> | ||||
|         </v-card> | ||||
|  | @ -179,50 +200,50 @@ async function submitFormHandler(): Promise<void> { | |||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
| .main-container { | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|     text-align: center; | ||||
| } | ||||
|     .main-container { | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         text-align: center; | ||||
|     } | ||||
| 
 | ||||
| .form-card { | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|     width: 55%; | ||||
|     /*padding: 1%;*/ | ||||
| } | ||||
| 
 | ||||
| .form-container { | ||||
|     width: 100%; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
| } | ||||
| 
 | ||||
| .step-container { | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     flex-direction: column; | ||||
|     min-height: 200px; | ||||
| } | ||||
| 
 | ||||
| @media (max-width: 1000px) { | ||||
|     .form-card { | ||||
|         width: 70%; | ||||
|         padding: 1%; | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         width: 55%; | ||||
|         /*padding: 1%;*/ | ||||
|     } | ||||
| 
 | ||||
|     .form-container { | ||||
|         width: 100%; | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|     } | ||||
| 
 | ||||
|     .step-container { | ||||
|         min-height: 300px; | ||||
|         display: flex; | ||||
|         justify-content: center; | ||||
|         flex-direction: column; | ||||
|         min-height: 200px; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @media (max-width: 650px) { | ||||
|     .form-card { | ||||
|         width: 95%; | ||||
|     @media (max-width: 1000px) { | ||||
|         .form-card { | ||||
|             width: 70%; | ||||
|             padding: 1%; | ||||
|         } | ||||
| 
 | ||||
|         .step-container { | ||||
|             min-height: 300px; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @media (max-width: 650px) { | ||||
|         .form-card { | ||||
|             width: 95%; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </style> | ||||
|  |  | |||
|  | @ -1,60 +1,58 @@ | |||
| <script setup lang="ts"> | ||||
|     import auth from "@/services/auth/auth-service.ts"; | ||||
|     import { computed, type Ref, ref, watchEffect } from "vue"; | ||||
|     import StudentAssignment from "@/views/assignments/StudentAssignment.vue"; | ||||
|     import TeacherAssignment from "@/views/assignments/TeacherAssignment.vue"; | ||||
|     import { useRoute } from "vue-router"; | ||||
|     import type { Language } from "@/data-objects/language.ts"; | ||||
|     import { useGetLearningPathQuery } from "@/queries/learning-paths.ts"; | ||||
|     import type { LearningPath } from "@/data-objects/learning-paths/learning-path.ts"; | ||||
|     import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; | ||||
| 
 | ||||
| import auth from "@/services/auth/auth-service.ts"; | ||||
| import {computed, type Ref, ref, watchEffect} from "vue"; | ||||
| import StudentAssignment from "@/views/assignments/StudentAssignment.vue"; | ||||
| import TeacherAssignment from "@/views/assignments/TeacherAssignment.vue"; | ||||
| import {useRoute} from "vue-router"; | ||||
| import type {Language} from "@/data-objects/language.ts"; | ||||
| import {useGetLearningPathQuery} from "@/queries/learning-paths.ts"; | ||||
| import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||
| import type {GroupDTO} from "@dwengo-1/common/interfaces/group"; | ||||
|     const role = auth.authState.activeRole; | ||||
|     const isTeacher = computed(() => role === "teacher"); | ||||
| 
 | ||||
| const role = auth.authState.activeRole; | ||||
| const isTeacher = computed(() => role === 'teacher'); | ||||
|     const route = useRoute(); | ||||
|     const classId = ref<string>(route.params.classId as string); | ||||
|     const assignmentId = ref(Number(route.params.id)); | ||||
| 
 | ||||
| const route = useRoute(); | ||||
| const classId = ref<string>(route.params.classId as string); | ||||
| const assignmentId = ref(Number(route.params.id)); | ||||
|     function useGroupsWithProgress( | ||||
|         groups: Ref<GroupDTO[]>, | ||||
|         hruid: Ref<string>, | ||||
|         language: Ref<string>, | ||||
|     ): { groupProgressMap: Map<number, number> } { | ||||
|         const groupProgressMap: Map<number, number> = new Map<number, number>(); | ||||
| 
 | ||||
| function useGroupsWithProgress( | ||||
|     groups: Ref<GroupDTO[]>, | ||||
|     hruid: Ref<string>, | ||||
|     language: Ref<string> | ||||
| ): { groupProgressMap: Map<number, number> } { | ||||
|     const groupProgressMap: Map<number, number> = new Map<number, number>(); | ||||
|         watchEffect(() => { | ||||
|             // Clear existing entries to avoid stale data | ||||
|             groupProgressMap.clear(); | ||||
| 
 | ||||
|     watchEffect(() => { | ||||
|         // Clear existing entries to avoid stale data | ||||
|         groupProgressMap.clear(); | ||||
|             const lang = ref(language.value as Language); | ||||
| 
 | ||||
|         const lang = ref(language.value as Language); | ||||
|             groups.value.forEach((group) => { | ||||
|                 const groupKey = group.groupNumber; | ||||
|                 const forGroup = ref({ | ||||
|                     forGroup: groupKey, | ||||
|                     assignmentNo: assignmentId, | ||||
|                     classId: classId, | ||||
|                 }); | ||||
| 
 | ||||
|         groups.value.forEach((group) => { | ||||
|             const groupKey = group.groupNumber; | ||||
|             const forGroup = ref({ | ||||
|                 forGroup: groupKey, | ||||
|                 assignmentNo: assignmentId, | ||||
|                 classId: classId, | ||||
|                 const query = useGetLearningPathQuery(hruid.value, lang, forGroup); | ||||
| 
 | ||||
|                 const data = query.data.value; | ||||
| 
 | ||||
|                 groupProgressMap.set(groupKey, data ? calculateProgress(data) : 0); | ||||
|             }); | ||||
| 
 | ||||
|             const query = useGetLearningPathQuery(hruid.value, lang, forGroup); | ||||
| 
 | ||||
|             const data = query.data.value; | ||||
| 
 | ||||
|             groupProgressMap.set(groupKey, data ? calculateProgress(data) : 0); | ||||
|         }); | ||||
|     }); | ||||
| 
 | ||||
|     return { | ||||
|         groupProgressMap, | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| function calculateProgress(lp: LearningPath): number { | ||||
|     return ((lp.amountOfNodes - lp.amountOfNodesLeft) / lp.amountOfNodes) * 100; | ||||
| } | ||||
|         return { | ||||
|             groupProgressMap, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     function calculateProgress(lp: LearningPath): number { | ||||
|         return ((lp.amountOfNodes - lp.amountOfNodesLeft) / lp.amountOfNodes) * 100; | ||||
|     } | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|  | @ -74,6 +72,4 @@ function calculateProgress(lp: LearningPath): number { | |||
|     </StudentAssignment> | ||||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
| </style> | ||||
| 
 | ||||
| <style scoped></style> | ||||
|  |  | |||
|  | @ -1,58 +1,56 @@ | |||
| <script setup lang="ts"> | ||||
| import {ref, computed, defineProps, type Ref} from "vue"; | ||||
| import auth from "@/services/auth/auth-service.ts"; | ||||
| import {useI18n} from "vue-i18n"; | ||||
| import {useAssignmentQuery} from "@/queries/assignments.ts"; | ||||
| import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||
| import type {AssignmentResponse} from "@/controllers/assignments.ts"; | ||||
| import {asyncComputed} from "@vueuse/core"; | ||||
| import {useStudentsByUsernamesQuery} from "@/queries/students.ts"; | ||||
| import {useGroupsQuery} from "@/queries/groups.ts"; | ||||
| import {useGetLearningPathQuery} from "@/queries/learning-paths.ts"; | ||||
| import type {Language} from "@/data-objects/language.ts"; | ||||
| import type {GroupDTO} from "@dwengo-1/common/interfaces/group"; | ||||
|     import { ref, computed, defineProps, type Ref } from "vue"; | ||||
|     import auth from "@/services/auth/auth-service.ts"; | ||||
|     import { useI18n } from "vue-i18n"; | ||||
|     import { useAssignmentQuery } from "@/queries/assignments.ts"; | ||||
|     import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||
|     import type { AssignmentResponse } from "@/controllers/assignments.ts"; | ||||
|     import { asyncComputed } from "@vueuse/core"; | ||||
|     import { useStudentsByUsernamesQuery } from "@/queries/students.ts"; | ||||
|     import { useGroupsQuery } from "@/queries/groups.ts"; | ||||
|     import { useGetLearningPathQuery } from "@/queries/learning-paths.ts"; | ||||
|     import type { Language } from "@/data-objects/language.ts"; | ||||
|     import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
|     classId: string | ||||
|     assignmentId: number, | ||||
|     useGroupsWithProgress: ( | ||||
|         groups: Ref<GroupDTO[]>, | ||||
|         hruid: Ref<string>, | ||||
|         language: Ref<Language> | ||||
|     ) => { groupProgressMap: Map<number, number> }; | ||||
| }>(); | ||||
|     const props = defineProps<{ | ||||
|         classId: string; | ||||
|         assignmentId: number; | ||||
|         useGroupsWithProgress: ( | ||||
|             groups: Ref<GroupDTO[]>, | ||||
|             hruid: Ref<string>, | ||||
|             language: Ref<Language>, | ||||
|         ) => { groupProgressMap: Map<number, number> }; | ||||
|     }>(); | ||||
| 
 | ||||
| const {t, locale} = useI18n(); | ||||
| const language = ref<Language>(locale.value as Language); | ||||
| const learningPath = ref(); | ||||
| // Get the user's username/id | ||||
| const username = asyncComputed(async () => { | ||||
|     const user = await auth.loadUser(); | ||||
|     return user?.profile?.preferred_username ?? undefined | ||||
| }); | ||||
|     const { t, locale } = useI18n(); | ||||
|     const language = ref<Language>(locale.value as Language); | ||||
|     const learningPath = ref(); | ||||
|     // Get the user's username/id | ||||
|     const username = asyncComputed(async () => { | ||||
|         const user = await auth.loadUser(); | ||||
|         return user?.profile?.preferred_username ?? undefined; | ||||
|     }); | ||||
| 
 | ||||
| const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); | ||||
| learningPath.value = assignmentQueryResult.data.value?.assignment?.learningPath; | ||||
|     const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); | ||||
|     learningPath.value = assignmentQueryResult.data.value?.assignment?.learningPath; | ||||
| 
 | ||||
| const submitted = ref(false);//TODO: update by fetching submissions and check if group submitted | ||||
|     const submitted = ref(false); //TODO: update by fetching submissions and check if group submitted | ||||
| 
 | ||||
| const lpQueryResult = useGetLearningPathQuery( | ||||
|     computed(() => assignmentQueryResult.data.value?.assignment?.learningPath ?? ""), | ||||
|     computed(() => language.value) | ||||
| ); | ||||
|     const lpQueryResult = useGetLearningPathQuery( | ||||
|         computed(() => assignmentQueryResult.data.value?.assignment?.learningPath ?? ""), | ||||
|         computed(() => language.value), | ||||
|     ); | ||||
| 
 | ||||
|     const groupsQueryResult = useGroupsQuery(props.classId, props.assignmentId, true); | ||||
|     const group = computed(() => | ||||
|         groupsQueryResult?.data.value?.groups.find((group) => | ||||
|             group.members?.some((m) => m.username === username.value), | ||||
|         ), | ||||
|     ); | ||||
| 
 | ||||
| const groupsQueryResult = useGroupsQuery(props.classId, props.assignmentId, true); | ||||
| const group = computed(() => | ||||
|     groupsQueryResult?.data.value?.groups.find(group => | ||||
|         group.members?.some(m => m.username === username.value) | ||||
|     ) | ||||
| ); | ||||
| 
 | ||||
| 
 | ||||
| const _groupArray = computed(() => (group.value ? [group.value] : [])); | ||||
| const progressValue = ref(0); | ||||
| /* Crashes right now cause api data has inexistent hruid TODO: uncomment later and use it in progress bar | ||||
|     const _groupArray = computed(() => (group.value ? [group.value] : [])); | ||||
|     const progressValue = ref(0); | ||||
|     /* Crashes right now cause api data has inexistent hruid TODO: uncomment later and use it in progress bar | ||||
| Const {groupProgressMap} = props.useGroupsWithProgress( | ||||
|     groupArray, | ||||
|     learningPath, | ||||
|  | @ -60,19 +58,20 @@ Const {groupProgressMap} = props.useGroupsWithProgress( | |||
| ); | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| // Assuming group.value.members is a list of usernames TODO: case when it's StudentDTO's | ||||
| const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as string[]); | ||||
| 
 | ||||
|     // Assuming group.value.members is a list of usernames TODO: case when it's StudentDTO's | ||||
|     const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as string[]); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|     <div class="container"> | ||||
|         <using-query-result | ||||
|             :query-result="assignmentQueryResult" | ||||
|             v-slot="{ data }: {data: AssignmentResponse}" | ||||
|             v-slot="{ data }: { data: AssignmentResponse }" | ||||
|         > | ||||
|             <v-card v-if="data" class="assignment-card"> | ||||
|             <v-card | ||||
|                 v-if="data" | ||||
|                 class="assignment-card" | ||||
|             > | ||||
|                 <div class="top-buttons"> | ||||
|                     <v-btn | ||||
|                         icon | ||||
|  | @ -99,10 +98,11 @@ const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as | |||
|                         :query-result="lpQueryResult" | ||||
|                         v-slot="{ data: lpData }" | ||||
|                     > | ||||
|                         <v-btn v-if="lpData" | ||||
|                                :to="`/learningPath/${lpData.hruid}/${language}/${lpData.startNode.learningobjectHruid}`" | ||||
|                                variant="tonal" | ||||
|                                color="primary" | ||||
|                         <v-btn | ||||
|                             v-if="lpData" | ||||
|                             :to="`/learningPath/${lpData.hruid}/${language}/${lpData.startNode.learningobjectHruid}`" | ||||
|                             variant="tonal" | ||||
|                             color="primary" | ||||
|                         > | ||||
|                             {{ t("learning-path") }} | ||||
|                         </v-btn> | ||||
|  | @ -113,7 +113,10 @@ const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as | |||
|                     {{ data.assignment.description }} | ||||
|                 </v-card-text> | ||||
|                 <v-card-text> | ||||
|                     <v-row align="center" no-gutters> | ||||
|                     <v-row | ||||
|                         align="center" | ||||
|                         no-gutters | ||||
|                     > | ||||
|                         <v-col cols="auto"> | ||||
|                             <span class="progress-label">{{ t("progress") + ": " }}</span> | ||||
|                         </v-col> | ||||
|  | @ -136,12 +139,14 @@ const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as | |||
|                     <h3>{{ t("group") }}</h3> | ||||
|                     <div v-if="studentQueries"> | ||||
|                         <ul> | ||||
|                             <li v-for="student in group?.members" :key="student.username"> | ||||
|                                 {{ student.firstName + ' ' + student.lastName }} | ||||
|                             <li | ||||
|                                 v-for="student in group?.members" | ||||
|                                 :key="student.username" | ||||
|                             > | ||||
|                                 {{ student.firstName + " " + student.lastName }} | ||||
|                             </li> | ||||
|                         </ul> | ||||
|                     </div> | ||||
| 
 | ||||
|                 </v-card-text> | ||||
|             </v-card> | ||||
|         </using-query-result> | ||||
|  | @ -149,16 +154,14 @@ const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as | |||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
| @import "@/assets/assignment.css"; | ||||
|     @import "@/assets/assignment.css"; | ||||
| 
 | ||||
| .progress-label { | ||||
|     font-weight: bold; | ||||
|     margin-right: 5px; | ||||
| } | ||||
| 
 | ||||
| .progress-bar { | ||||
|     width: 40%; | ||||
| } | ||||
|     .progress-label { | ||||
|         font-weight: bold; | ||||
|         margin-right: 5px; | ||||
|     } | ||||
| 
 | ||||
|     .progress-bar { | ||||
|         width: 40%; | ||||
|     } | ||||
| </style> | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,43 +1,43 @@ | |||
| <script setup lang="ts"> | ||||
| import {computed, defineProps, type Ref, ref} from "vue"; | ||||
| import {useI18n} from "vue-i18n"; | ||||
| import {useAssignmentQuery, useDeleteAssignmentMutation} from "@/queries/assignments.ts"; | ||||
| import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||
| import {useGroupsQuery} from "@/queries/groups.ts"; | ||||
| import {useGetLearningPathQuery} from "@/queries/learning-paths.ts"; | ||||
| import type {Language} from "@/data-objects/language.ts"; | ||||
| import router from "@/router"; | ||||
| import type {AssignmentResponse} from "@/controllers/assignments.ts"; | ||||
| import type {GroupDTO} from "@dwengo-1/common/interfaces/group"; | ||||
|     import { computed, defineProps, type Ref, ref } from "vue"; | ||||
|     import { useI18n } from "vue-i18n"; | ||||
|     import { useAssignmentQuery, useDeleteAssignmentMutation } from "@/queries/assignments.ts"; | ||||
|     import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||
|     import { useGroupsQuery } from "@/queries/groups.ts"; | ||||
|     import { useGetLearningPathQuery } from "@/queries/learning-paths.ts"; | ||||
|     import type { Language } from "@/data-objects/language.ts"; | ||||
|     import router from "@/router"; | ||||
|     import type { AssignmentResponse } from "@/controllers/assignments.ts"; | ||||
|     import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
|     classId: string | ||||
|     assignmentId: number, | ||||
|     useGroupsWithProgress: ( | ||||
|         groups: Ref<GroupDTO[]>, | ||||
|         hruid: Ref<string>, | ||||
|         language: Ref<Language> | ||||
|     ) => { groupProgressMap: Map<number, number> }; | ||||
| }>(); | ||||
|     const props = defineProps<{ | ||||
|         classId: string; | ||||
|         assignmentId: number; | ||||
|         useGroupsWithProgress: ( | ||||
|             groups: Ref<GroupDTO[]>, | ||||
|             hruid: Ref<string>, | ||||
|             language: Ref<Language>, | ||||
|         ) => { groupProgressMap: Map<number, number> }; | ||||
|     }>(); | ||||
| 
 | ||||
| const {t, locale} = useI18n(); | ||||
| const language = computed(() => locale.value); | ||||
| const groups = ref(); | ||||
| const learningPath = ref(); | ||||
|     const { t, locale } = useI18n(); | ||||
|     const language = computed(() => locale.value); | ||||
|     const groups = ref(); | ||||
|     const learningPath = ref(); | ||||
| 
 | ||||
| const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); | ||||
| learningPath.value = assignmentQueryResult.data.value?.assignment?.learningPath; | ||||
| // Get learning path object | ||||
| const lpQueryResult = useGetLearningPathQuery( | ||||
|     computed(() => assignmentQueryResult.data.value?.assignment?.learningPath ?? ""), | ||||
|     computed(() => language.value as Language) | ||||
| ); | ||||
|     const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); | ||||
|     learningPath.value = assignmentQueryResult.data.value?.assignment?.learningPath; | ||||
|     // Get learning path object | ||||
|     const lpQueryResult = useGetLearningPathQuery( | ||||
|         computed(() => assignmentQueryResult.data.value?.assignment?.learningPath ?? ""), | ||||
|         computed(() => language.value as Language), | ||||
|     ); | ||||
| 
 | ||||
| // Get all the groups withing the assignment | ||||
| const groupsQueryResult = useGroupsQuery(props.classId, props.assignmentId, true); | ||||
| groups.value = groupsQueryResult.data.value?.groups; | ||||
|     // 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 | ||||
|     /* Crashes right now cause api data has inexistent hruid TODO: uncomment later and use it in progress bar | ||||
| Const {groupProgressMap} = props.useGroupsWithProgress( | ||||
|     groups, | ||||
|     learningPath, | ||||
|  | @ -45,55 +45,54 @@ Const {groupProgressMap} = props.useGroupsWithProgress( | |||
| ); | ||||
| */ | ||||
| 
 | ||||
|     const allGroups = computed(() => { | ||||
|         const groups = groupsQueryResult.data.value?.groups; | ||||
|         if (!groups) return []; | ||||
| 
 | ||||
| const allGroups = computed(() => { | ||||
|     const groups = groupsQueryResult.data.value?.groups; | ||||
|     if (!groups) return []; | ||||
| 
 | ||||
|     return groups.map(group => ({ | ||||
|         name: `${t('group')} ${group.groupNumber}`, | ||||
|         progress: 0,//GroupProgressMap[group.groupNumber], | ||||
|         members: group.members, | ||||
|         submitted: false,//TODO: fetch from submission | ||||
|     })); | ||||
| }); | ||||
| 
 | ||||
| const dialog = ref(false); | ||||
| const selectedGroup = ref({}); | ||||
| 
 | ||||
| function openGroupDetails(group): void { | ||||
|     selectedGroup.value = group; | ||||
|     dialog.value = true; | ||||
| } | ||||
| 
 | ||||
| const headers = computed(() => [ | ||||
|     { title: t('group'), align: 'start', key: 'name' }, | ||||
|     { title: t('progress'), align: 'center', key: 'progress' }, | ||||
|     { title: t('submission'), align: 'center', key: 'submission' } | ||||
| ]); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| const {mutate, isSuccess} = useDeleteAssignmentMutation(); | ||||
| 
 | ||||
| async function deleteAssignment(num: number, clsId: string): Promise<void> { | ||||
|     mutate({ | ||||
|         cid: clsId, | ||||
|         an: num | ||||
|         return groups.map((group) => ({ | ||||
|             name: `${t("group")} ${group.groupNumber}`, | ||||
|             progress: 0, //GroupProgressMap[group.groupNumber], | ||||
|             members: group.members, | ||||
|             submitted: false, //TODO: fetch from submission | ||||
|         })); | ||||
|     }); | ||||
| 
 | ||||
|     if (isSuccess) await router.push("/user/assignments"); | ||||
| } | ||||
|     const dialog = ref(false); | ||||
|     const selectedGroup = ref({}); | ||||
| 
 | ||||
|     function openGroupDetails(group): void { | ||||
|         selectedGroup.value = group; | ||||
|         dialog.value = true; | ||||
|     } | ||||
| 
 | ||||
|     const headers = computed(() => [ | ||||
|         { title: t("group"), align: "start", key: "name" }, | ||||
|         { title: t("progress"), align: "center", key: "progress" }, | ||||
|         { title: t("submission"), align: "center", key: "submission" }, | ||||
|     ]); | ||||
| 
 | ||||
|     const { mutate, isSuccess } = useDeleteAssignmentMutation(); | ||||
| 
 | ||||
|     async function deleteAssignment(num: number, clsId: string): Promise<void> { | ||||
|         mutate({ | ||||
|             cid: clsId, | ||||
|             an: num, | ||||
|         }); | ||||
| 
 | ||||
|         if (isSuccess) await router.push("/user/assignments"); | ||||
|     } | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|     <div class="container"> | ||||
|         <using-query-result | ||||
|             :query-result="assignmentQueryResult" | ||||
|             v-slot="{ data }: {data: AssignmentResponse}" | ||||
|             v-slot="{ data }: { data: AssignmentResponse }" | ||||
|         > | ||||
|             <v-card v-if="data" class="assignment-card"> | ||||
|             <v-card | ||||
|                 v-if="data" | ||||
|                 class="assignment-card" | ||||
|             > | ||||
|                 <div class="top-buttons"> | ||||
|                     <v-btn | ||||
|                         icon | ||||
|  | @ -119,15 +118,15 @@ async function deleteAssignment(num: number, clsId: string): Promise<void> { | |||
|                         :query-result="lpQueryResult" | ||||
|                         v-slot="{ data: lpData }" | ||||
|                     > | ||||
|                         <v-btn v-if="lpData" | ||||
|                                :to="`/learningPath/${lpData.hruid}/${language}/${lpData.startNode.learningobjectHruid}`" | ||||
|                                variant="tonal" | ||||
|                                color="primary" | ||||
|                         <v-btn | ||||
|                             v-if="lpData" | ||||
|                             :to="`/learningPath/${lpData.hruid}/${language}/${lpData.startNode.learningobjectHruid}`" | ||||
|                             variant="tonal" | ||||
|                             color="primary" | ||||
|                         > | ||||
|                             {{ t("learning-path") }} | ||||
|                         </v-btn> | ||||
|                     </using-query-result> | ||||
| 
 | ||||
|                 </v-card-subtitle> | ||||
| 
 | ||||
|                 <v-card-text class="description"> | ||||
|  | @ -144,7 +143,11 @@ async function deleteAssignment(num: number, clsId: string): Promise<void> { | |||
|                             class="elevation-1" | ||||
|                         > | ||||
|                             <template #[`item.name`]="{ item }"> | ||||
|                                 <v-btn @click="openGroupDetails(item)" variant="text" color="primary"> | ||||
|                                 <v-btn | ||||
|                                     @click="openGroupDetails(item)" | ||||
|                                     variant="text" | ||||
|                                     color="primary" | ||||
|                                 > | ||||
|                                     {{ item.name }} | ||||
|                                 </v-btn> | ||||
|                             </template> | ||||
|  | @ -168,17 +171,19 @@ async function deleteAssignment(num: number, clsId: string): Promise<void> { | |||
|                                     variant="text" | ||||
|                                     class="text-capitalize" | ||||
|                                 > | ||||
|                                     {{ item.submitted ? t('see-submission') : t('no-submission') }} | ||||
|                                     {{ item.submitted ? t("see-submission") : t("no-submission") }} | ||||
|                                 </v-btn> | ||||
|                             </template> | ||||
| 
 | ||||
|                         </v-data-table> | ||||
|                     </div> | ||||
|                 </v-card-text> | ||||
| 
 | ||||
|                 <v-dialog v-model="dialog" max-width="50%"> | ||||
|                 <v-dialog | ||||
|                     v-model="dialog" | ||||
|                     max-width="50%" | ||||
|                 > | ||||
|                     <v-card> | ||||
|                         <v-card-title class="headline">{{t("members")}}</v-card-title> | ||||
|                         <v-card-title class="headline">{{ t("members") }}</v-card-title> | ||||
|                         <v-card-text> | ||||
|                             <v-list> | ||||
|                                 <v-list-item | ||||
|  | @ -186,16 +191,19 @@ async function deleteAssignment(num: number, clsId: string): Promise<void> { | |||
|                                     :key="index" | ||||
|                                 > | ||||
|                                     <v-list-item-content> | ||||
|                                         <v-list-item-title>{{ | ||||
|                                                 member.firstName + ' ' + member.lastName | ||||
|                                             }} | ||||
|                                         <v-list-item-title | ||||
|                                             >{{ member.firstName + " " + member.lastName }} | ||||
|                                         </v-list-item-title> | ||||
|                                     </v-list-item-content> | ||||
|                                 </v-list-item> | ||||
|                             </v-list> | ||||
|                         </v-card-text> | ||||
|                         <v-card-actions> | ||||
|                             <v-btn color="primary" @click="dialog = false">Close</v-btn> | ||||
|                             <v-btn | ||||
|                                 color="primary" | ||||
|                                 @click="dialog = false" | ||||
|                                 >Close</v-btn | ||||
|                             > | ||||
|                         </v-card-actions> | ||||
|                     </v-card> | ||||
|                 </v-dialog> | ||||
|  | @ -216,11 +224,10 @@ async function deleteAssignment(num: number, clsId: string): Promise<void> { | |||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
| @import "@/assets/assignment.css"; | ||||
|     @import "@/assets/assignment.css"; | ||||
| 
 | ||||
| .table-scroll { | ||||
|     overflow-x: auto; | ||||
|     -webkit-overflow-scrolling: touch; | ||||
| } | ||||
|     .table-scroll { | ||||
|         overflow-x: auto; | ||||
|         -webkit-overflow-scrolling: touch; | ||||
|     } | ||||
| </style> | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,87 +1,85 @@ | |||
| <script setup lang="ts"> | ||||
| import {ref, computed, onMounted} from 'vue'; | ||||
| import {useI18n} from 'vue-i18n'; | ||||
| import {useRouter} from 'vue-router'; | ||||
| import auth from "@/services/auth/auth-service.ts"; | ||||
| import {useTeacherClassesQuery} from "@/queries/teachers.ts"; | ||||
| import {useStudentClassesQuery} from "@/queries/students.ts"; | ||||
| import {ClassController} from "@/controllers/classes.ts"; | ||||
| import type {ClassDTO} from "@dwengo-1/common/interfaces/class"; | ||||
| import {asyncComputed} from "@vueuse/core"; | ||||
| import {useDeleteAssignmentMutation} from "@/queries/assignments.ts"; | ||||
|     import { ref, computed, onMounted } from "vue"; | ||||
|     import { useI18n } from "vue-i18n"; | ||||
|     import { useRouter } from "vue-router"; | ||||
|     import auth from "@/services/auth/auth-service.ts"; | ||||
|     import { useTeacherClassesQuery } from "@/queries/teachers.ts"; | ||||
|     import { useStudentClassesQuery } from "@/queries/students.ts"; | ||||
|     import { ClassController } from "@/controllers/classes.ts"; | ||||
|     import type { ClassDTO } from "@dwengo-1/common/interfaces/class"; | ||||
|     import { asyncComputed } from "@vueuse/core"; | ||||
|     import { useDeleteAssignmentMutation } from "@/queries/assignments.ts"; | ||||
| 
 | ||||
| const {t} = useI18n(); | ||||
| const router = useRouter(); | ||||
|     const { t } = useI18n(); | ||||
|     const router = useRouter(); | ||||
| 
 | ||||
| const role = ref(auth.authState.activeRole); | ||||
| const username = ref<string>(""); | ||||
|     const role = ref(auth.authState.activeRole); | ||||
|     const username = ref<string>(""); | ||||
| 
 | ||||
| const isTeacher = computed(() => role.value === 'teacher'); | ||||
|     const isTeacher = computed(() => role.value === "teacher"); | ||||
| 
 | ||||
| // Fetch and store all the teacher's classes | ||||
| let classesQueryResults = undefined; | ||||
|     // Fetch and store all the teacher's classes | ||||
|     let classesQueryResults = undefined; | ||||
| 
 | ||||
| if (isTeacher.value) { | ||||
|     classesQueryResults = useTeacherClassesQuery(username, true) | ||||
| } else { | ||||
|     classesQueryResults = useStudentClassesQuery(username, true); | ||||
| } | ||||
|     if (isTeacher.value) { | ||||
|         classesQueryResults = useTeacherClassesQuery(username, true); | ||||
|     } else { | ||||
|         classesQueryResults = useStudentClassesQuery(username, true); | ||||
|     } | ||||
| 
 | ||||
| //TODO: remove later | ||||
| const classController = new ClassController(); | ||||
|     //TODO: remove later | ||||
|     const classController = new ClassController(); | ||||
| 
 | ||||
|     //TODO: replace by query that fetches all user's assignment | ||||
|     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, | ||||
|                 })); | ||||
|             }), | ||||
|         ); | ||||
| 
 | ||||
| //TODO: replace by query that fetches all user's assignment | ||||
| 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(); | ||||
|     }, []); | ||||
| 
 | ||||
|     return result.flat(); | ||||
| }, []); | ||||
|     async function goToCreateAssignment(): Promise<void> { | ||||
|         await router.push("/assignment/create"); | ||||
|     } | ||||
| 
 | ||||
|     async function goToAssignmentDetails(id: number, clsId: string): Promise<void> { | ||||
|         await router.push(`/assignment/${clsId}/${id}`); | ||||
|     } | ||||
| 
 | ||||
| async function goToCreateAssignment(): Promise<void> { | ||||
|     await router.push('/assignment/create'); | ||||
| } | ||||
|     const { mutate, isSuccess } = useDeleteAssignmentMutation(); | ||||
| 
 | ||||
| async function goToAssignmentDetails(id: number, clsId: string): Promise<void> { | ||||
|     await router.push(`/assignment/${clsId}/${id}`); | ||||
| } | ||||
|     async function goToDeleteAssignment(num: number, clsId: string): Promise<void> { | ||||
|         mutate({ | ||||
|             cid: clsId, | ||||
|             an: num, | ||||
|         }); | ||||
| 
 | ||||
| const {mutate, isSuccess} = useDeleteAssignmentMutation(); | ||||
|         if (isSuccess) await router.push("/user/assignment"); | ||||
|     } | ||||
| 
 | ||||
| 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 ?? ""; | ||||
|     }); | ||||
| 
 | ||||
|     if (isSuccess) await router.push("/user/assignment"); | ||||
| } | ||||
| 
 | ||||
| onMounted(async () => { | ||||
|     const user = await auth.loadUser(); | ||||
|     username.value = user?.profile?.preferred_username ?? ""; | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|     <div class="assignments-container"> | ||||
|         <h1>{{ t('assignments') }}</h1> | ||||
|         <h1>{{ t("assignments") }}</h1> | ||||
| 
 | ||||
|         <v-btn | ||||
|             v-if="isTeacher" | ||||
|  | @ -89,7 +87,7 @@ onMounted(async () => { | |||
|             class="mb-4 center-btn" | ||||
|             @click="goToCreateAssignment" | ||||
|         > | ||||
|             {{ t('new-assignment') }} | ||||
|             {{ t("new-assignment") }} | ||||
|         </v-btn> | ||||
| 
 | ||||
|         <v-container> | ||||
|  | @ -103,94 +101,94 @@ onMounted(async () => { | |||
|                         <div class="top-content"> | ||||
|                             <div class="assignment-title">{{ assignment.title }}</div> | ||||
|                             <div class="assignment-class"> | ||||
|                                 {{ t('class') }}: | ||||
|                                 {{ t("class") }}: | ||||
|                                 <span class="class-name"> | ||||
|                                       {{ assignment.class.displayName }} | ||||
|                                     </span> | ||||
|                                     {{ 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.class.id)"> | ||||
|                                 {{ t('view-assignment') }} | ||||
|                             <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 | ||||
|                                 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> | ||||
| 
 | ||||
| 
 | ||||
| <style scoped> | ||||
| .assignments-container { | ||||
|     width: 100%; | ||||
|     margin: 0 auto; | ||||
|     padding: 2% 4%; | ||||
|     box-sizing: border-box; | ||||
| } | ||||
|     .assignments-container { | ||||
|         width: 100%; | ||||
|         margin: 0 auto; | ||||
|         padding: 2% 4%; | ||||
|         box-sizing: border-box; | ||||
|     } | ||||
| 
 | ||||
| .center-btn { | ||||
|     display: block; | ||||
|     margin-left: auto; | ||||
|     margin-right: auto; | ||||
| } | ||||
|     .center-btn { | ||||
|         display: block; | ||||
|         margin-left: auto; | ||||
|         margin-right: auto; | ||||
|     } | ||||
| 
 | ||||
|     .assignment-card { | ||||
|         padding: 1rem; | ||||
|     } | ||||
| 
 | ||||
| .assignment-card { | ||||
|     padding: 1rem; | ||||
| } | ||||
|     .card-content { | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         height: 100%; | ||||
|         min-height: 150px; | ||||
|     } | ||||
| 
 | ||||
| .card-content { | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     height: 100%; | ||||
|     min-height: 150px; | ||||
| } | ||||
|     .top-content { | ||||
|         margin-bottom: 1rem; | ||||
|         word-break: break-word; | ||||
|     } | ||||
| 
 | ||||
| .top-content { | ||||
|     margin-bottom: 1rem; | ||||
|     word-break: break-word; | ||||
| } | ||||
|     .spacer { | ||||
|         flex: 1; | ||||
|     } | ||||
| 
 | ||||
| .spacer { | ||||
|     flex: 1; | ||||
| } | ||||
|     .button-row { | ||||
|         display: flex; | ||||
|         justify-content: flex-end; | ||||
|         gap: 0.5rem; | ||||
|         flex-wrap: wrap; | ||||
|     } | ||||
| 
 | ||||
| .button-row { | ||||
|     display: flex; | ||||
|     justify-content: flex-end; | ||||
|     gap: 0.5rem; | ||||
|     flex-wrap: wrap; | ||||
| } | ||||
| 
 | ||||
| .assignment-title { | ||||
|     font-weight: bold; | ||||
|     font-size: 1.5rem; | ||||
|     margin-bottom: 0.1rem; | ||||
|     word-break: break-word; | ||||
| } | ||||
| 
 | ||||
| .assignment-class { | ||||
|     color: #666; | ||||
|     font-size: 0.95rem; | ||||
| } | ||||
| 
 | ||||
| .class-name { | ||||
|     font-weight: 500; | ||||
|     color: #333; | ||||
| } | ||||
|     .assignment-title { | ||||
|         font-weight: bold; | ||||
|         font-size: 1.5rem; | ||||
|         margin-bottom: 0.1rem; | ||||
|         word-break: break-word; | ||||
|     } | ||||
| 
 | ||||
|     .assignment-class { | ||||
|         color: #666; | ||||
|         font-size: 0.95rem; | ||||
|     } | ||||
| 
 | ||||
|     .class-name { | ||||
|         font-weight: 500; | ||||
|         color: #333; | ||||
|     } | ||||
| </style> | ||||
|  |  | |||
		Reference in a new issue
	
	 Lint Action
						Lint Action