feat(frontend): leerkracht kan alle groepen binnen een assignment zien en leerling kan zijn group zien
This commit is contained in:
		
							parent
							
								
									83f01830e3
								
							
						
					
					
						commit
						20cf276faf
					
				
					 15 changed files with 397 additions and 341 deletions
				
			
		|  | @ -1,226 +0,0 @@ | ||||||
| <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 {AssignmentController} from "@/controllers/assignments.ts"; |  | ||||||
| 
 |  | ||||||
| /*** |  | ||||||
|  TODO: when clicking the assign button from lp page pass the lp-object in a state: |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| const router = useRouter(); |  | ||||||
| const {t, locale} = useI18n(); |  | ||||||
| const role = ref(auth.authState.activeRole); |  | ||||||
| const username = ref<string>(""); |  | ||||||
| 
 |  | ||||||
| async function submitForm(assignmentDTO: AssignmentDTO): Promise<void> { |  | ||||||
|     //TODO: replace with query function |  | ||||||
|     const controller: AssignmentController = new AssignmentController(assignmentDTO.class); |  | ||||||
|     await controller.createAssignment(assignmentDTO); |  | ||||||
| 
 |  | ||||||
|     // Navigate back to all assignments |  | ||||||
|     await router.push('/user/assignment'); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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<LearningPath | null>(props.learningPath ?? null); |  | ||||||
| // Disable combobox when learningPath prop is passed |  | ||||||
| const isLearningPathSelected = props.learningPath !== null; |  | ||||||
| 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]; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| watch(selectedClass, () => { |  | ||||||
|     groups.value = []; |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| async function submitFormHandler(): Promise<void> { |  | ||||||
|     const {valid} = await form.value.validate(); |  | ||||||
|     // Don't submit the form if all rules don't apply |  | ||||||
|     if (!valid) return; |  | ||||||
|     const assignmentDTO: AssignmentDTO = { |  | ||||||
|         id: 0, |  | ||||||
|         class: selectedClass.value?.id || "", |  | ||||||
|         title: assignmentTitle.value, |  | ||||||
|         description: description.value, |  | ||||||
|         learningPath: selectedLearningPath.value?.hruid || "", |  | ||||||
|         language: language.value |  | ||||||
|     } |  | ||||||
|     await submitForm(assignmentDTO); |  | ||||||
| } |  | ||||||
| </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-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-card-text> |  | ||||||
| 
 |  | ||||||
|                     <using-query-result |  | ||||||
|                         :query-result="learningPathsQueryResults" |  | ||||||
|                         v-slot="{ data }: { data: LearningPath[] }" |  | ||||||
|                     > |  | ||||||
|                         <v-card-text> |  | ||||||
|                             <v-combobox |  | ||||||
|                                 v-model="selectedLearningPath" |  | ||||||
|                                 :items="data" |  | ||||||
|                                 :label="t('choose-lp')" |  | ||||||
|                                 :rules="learningPathRules" |  | ||||||
|                                 variant="outlined" |  | ||||||
|                                 clearable |  | ||||||
|                                 hide-details |  | ||||||
|                                 density="compact" |  | ||||||
|                                 append-inner-icon="mdi-magnify" |  | ||||||
|                                 item-title="title" |  | ||||||
|                                 item-value="hruid" |  | ||||||
|                                 required |  | ||||||
|                                 :disabled="isLearningPathSelected" |  | ||||||
|                                 :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-card-text> |  | ||||||
|                             <v-combobox |  | ||||||
|                                 v-model="selectedClass" |  | ||||||
|                                 :items="data?.classes ?? []" |  | ||||||
|                                 :label="t('pick-class')" |  | ||||||
|                                 :rules="classRules" |  | ||||||
|                                 variant="outlined" |  | ||||||
|                                 clearable |  | ||||||
|                                 hide-details |  | ||||||
|                                 density="compact" |  | ||||||
|                                 append-inner-icon="mdi-magnify" |  | ||||||
|                                 item-title="displayName" |  | ||||||
|                                 item-value="id" |  | ||||||
|                                 required |  | ||||||
|                             ></v-combobox> |  | ||||||
|                         </v-card-text> |  | ||||||
|                     </using-query-result> |  | ||||||
| 
 |  | ||||||
|                     <GroupSelector |  | ||||||
|                         :classId="selectedClass?.id" |  | ||||||
|                         :groups="groups" |  | ||||||
|                         @groupCreated="addGroupToList" |  | ||||||
|                     /> |  | ||||||
| 
 |  | ||||||
|                     <!-- Counter for created groups --> |  | ||||||
|                     <v-card-text v-if="groups.length"> |  | ||||||
|                         <strong>Created Groups: {{ groups.length }}</strong> |  | ||||||
|                     </v-card-text> |  | ||||||
|                     <DeadlineSelector v-model:deadline="deadline"/> |  | ||||||
|                     <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-form> |  | ||||||
|         </v-card> |  | ||||||
|     </div> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <style scoped> |  | ||||||
| .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%; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     .step-container { |  | ||||||
|         min-height: 300px; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @media (max-width: 650px) { |  | ||||||
|     .form-card { |  | ||||||
|         width: 95%; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { ref, computed, defineEmits } from "vue"; | import {ref, computed, defineEmits} from "vue"; | ||||||
| import {deadlineRules} from "@/utils/assignment-rules.ts"; | import {deadlineRules} from "@/utils/assignment-rules.ts"; | ||||||
| 
 | 
 | ||||||
| const date = ref(""); | const date = ref(""); | ||||||
|  | @ -11,7 +11,7 @@ const formattedDeadline = computed(() => { | ||||||
|     return `${date.value} ${time.value}`; |     return `${date.value} ${time.value}`; | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const updateDeadline = () => { | function updateDeadline(): void { | ||||||
|     if (date.value && time.value) { |     if (date.value && time.value) { | ||||||
|         emit("update:deadline", formattedDeadline.value); |         emit("update:deadline", formattedDeadline.value); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ function filterStudents(data: StudentsResponse): { title: string, value: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| const createGroup = () => { | function createGroup(): void { | ||||||
|     if (selectedStudents.value.length) { |     if (selectedStudents.value.length) { | ||||||
|         // Extract only usernames (student.value) |         // Extract only usernames (student.value) | ||||||
|         const usernames = selectedStudents.value.map(student => student.value); |         const usernames = selectedStudents.value.map(student => student.value); | ||||||
|  |  | ||||||
|  | @ -70,7 +70,7 @@ | ||||||
|     "create-group": "Gruppe erstellen", |     "create-group": "Gruppe erstellen", | ||||||
|     "class": "klasse", |     "class": "klasse", | ||||||
|     "delete": "löschen", |     "delete": "löschen", | ||||||
|     "view-assignment": "Auftrag anzeigen" |     "view-assignment": "Auftrag anzeigen", | ||||||
|     "legendTeacherExclusive": "Information für Lehrkräfte", |     "legendTeacherExclusive": "Information für Lehrkräfte", | ||||||
|     "code": "code", |     "code": "code", | ||||||
|     "class": "Klasse", |     "class": "Klasse", | ||||||
|  |  | ||||||
|  | @ -70,10 +70,8 @@ | ||||||
|     "create-group": "Create group", |     "create-group": "Create group", | ||||||
|     "class": "class", |     "class": "class", | ||||||
|     "delete": "delete", |     "delete": "delete", | ||||||
|     "view-assignment": "View assignment" |     "view-assignment": "View assignment", | ||||||
|     "read-more": "Read more", |  | ||||||
|     "code": "code", |     "code": "code", | ||||||
|     "class": "class", |  | ||||||
|     "invitations": "invitations", |     "invitations": "invitations", | ||||||
|     "createClass": "create class", |     "createClass": "create class", | ||||||
|     "classname": "classname", |     "classname": "classname", | ||||||
|  |  | ||||||
|  | @ -70,7 +70,7 @@ | ||||||
|     "create-group": "Créer un groupe", |     "create-group": "Créer un groupe", | ||||||
|     "class": "classe", |     "class": "classe", | ||||||
|     "delete": "supprimer", |     "delete": "supprimer", | ||||||
|     "view-assignment": "Voir le travail" |     "view-assignment": "Voir le travail", | ||||||
|     "read-more": "En savoir plus", |     "read-more": "En savoir plus", | ||||||
|     "code": "code", |     "code": "code", | ||||||
|     "class": "classe", |     "class": "classe", | ||||||
|  |  | ||||||
|  | @ -70,7 +70,7 @@ | ||||||
|     "create-group": "Groep aanmaken", |     "create-group": "Groep aanmaken", | ||||||
|     "class": "klas", |     "class": "klas", | ||||||
|     "delete": "verwijderen", |     "delete": "verwijderen", | ||||||
|     "view-assignment": "Opdracht bekijken" |     "view-assignment": "Opdracht bekijken", | ||||||
|     "read-more": "Lees meer", |     "read-more": "Lees meer", | ||||||
|     "code": "code", |     "code": "code", | ||||||
|     "class": "klas", |     "class": "klas", | ||||||
|  |  | ||||||
|  | @ -104,7 +104,7 @@ export function useAssignmentsQuery( | ||||||
| export function useAssignmentQuery( | export function useAssignmentQuery( | ||||||
|     classid: MaybeRefOrGetter<string | undefined>, |     classid: MaybeRefOrGetter<string | undefined>, | ||||||
|     assignmentNumber: MaybeRefOrGetter<number | undefined>, |     assignmentNumber: MaybeRefOrGetter<number | undefined>, | ||||||
| ): UseQueryReturnType<AssignmentsResponse, Error> { | ): UseQueryReturnType<AssignmentResponse, Error> { | ||||||
|     const { cid, an } = toValues(classid, assignmentNumber, 1, true); |     const { cid, an } = toValues(classid, assignmentNumber, 1, true); | ||||||
| 
 | 
 | ||||||
|     return useQuery({ |     return useQuery({ | ||||||
|  |  | ||||||
|  | @ -1,8 +1,9 @@ | ||||||
| import { computed, toValue } from "vue"; | import {computed, type Ref, toValue} from "vue"; | ||||||
| import type { MaybeRefOrGetter } from "vue"; | import type { MaybeRefOrGetter } from "vue"; | ||||||
| import { | import { | ||||||
|  |     type QueryObserverResult, | ||||||
|     useMutation, |     useMutation, | ||||||
|     type UseMutationReturnType, |     type UseMutationReturnType, useQueries, | ||||||
|     useQuery, |     useQuery, | ||||||
|     useQueryClient, |     useQueryClient, | ||||||
|     type UseQueryReturnType, |     type UseQueryReturnType, | ||||||
|  | @ -69,6 +70,20 @@ export function useStudentQuery( | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export function useStudentsByUsernamesQuery( | ||||||
|  |     usernames: MaybeRefOrGetter<string[] | undefined> | ||||||
|  | ): Ref<QueryObserverResult<StudentResponse>[]> { | ||||||
|  |     const resolvedUsernames = toValue(usernames) ?? []; | ||||||
|  | 
 | ||||||
|  |     return useQueries({ | ||||||
|  |         queries: resolvedUsernames?.map((username) => ({ | ||||||
|  |             queryKey: computed(() => studentQueryKey(toValue(username))), | ||||||
|  |             queryFn: async () => studentController.getByUsername(toValue(username)), | ||||||
|  |             enabled: Boolean(toValue(username)), | ||||||
|  |         })), | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export function useStudentClassesQuery( | export function useStudentClassesQuery( | ||||||
|     username: MaybeRefOrGetter<string | undefined>, |     username: MaybeRefOrGetter<string | undefined>, | ||||||
|     full: MaybeRefOrGetter<boolean> = true, |     full: MaybeRefOrGetter<boolean> = true, | ||||||
|  |  | ||||||
|  | @ -88,7 +88,7 @@ const router = createRouter({ | ||||||
|                     component: CreateAssignment, |                     component: CreateAssignment, | ||||||
|                 }, |                 }, | ||||||
|                 { |                 { | ||||||
|                     path: ":id", |                     path: ":classId/:id", | ||||||
|                     name: "SingleAssigment", |                     name: "SingleAssigment", | ||||||
|                     component: SingleAssignment, |                     component: SingleAssignment, | ||||||
|                 }, |                 }, | ||||||
|  |  | ||||||
|  | @ -1,10 +1,225 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import AssignmentForm from "@/components/assignments/AssignmentForm.vue"; | 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 {AssignmentController} from "@/controllers/assignments.ts"; | ||||||
|  | import {useCreateAssignmentMutation} from "@/queries/assignments.ts"; | ||||||
|  | 
 | ||||||
|  | /*** | ||||||
|  |  TODO: when clicking the assign button from lp page pass the lp-object in a state: | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | 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'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 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<LearningPath | null>(window.history.state?.learningPath ?? null); | ||||||
|  | 
 | ||||||
|  | // Disable combobox when learningPath prop is passed | ||||||
|  | const lpIsSelected = window.history.state?.learningPath !== 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]; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | watch(selectedClass, () => { | ||||||
|  |     groups.value = []; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const {mutate, 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("/user/assignment"); | ||||||
|  | } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| <template> | <template> | ||||||
|     <AssignmentForm :learning-path="null"/> |     <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-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-card-text> | ||||||
|  | 
 | ||||||
|  |                     <using-query-result | ||||||
|  |                         :query-result="learningPathsQueryResults" | ||||||
|  |                         v-slot="{ data }: { data: LearningPath[] }" | ||||||
|  |                     > | ||||||
|  |                         <v-card-text> | ||||||
|  |                             <v-combobox | ||||||
|  |                                 v-model="selectedLearningPath" | ||||||
|  |                                 :items="data" | ||||||
|  |                                 :label="t('choose-lp')" | ||||||
|  |                                 :rules="learningPathRules" | ||||||
|  |                                 variant="outlined" | ||||||
|  |                                 clearable | ||||||
|  |                                 hide-details | ||||||
|  |                                 density="compact" | ||||||
|  |                                 append-inner-icon="mdi-magnify" | ||||||
|  |                                 item-title="title" | ||||||
|  |                                 item-value="hruid" | ||||||
|  |                                 required | ||||||
|  |                                 :disabled="lpIsSelected" | ||||||
|  |                                 :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-card-text> | ||||||
|  |                             <v-combobox | ||||||
|  |                                 v-model="selectedClass" | ||||||
|  |                                 :items="data?.classes ?? []" | ||||||
|  |                                 :label="t('pick-class')" | ||||||
|  |                                 :rules="classRules" | ||||||
|  |                                 variant="outlined" | ||||||
|  |                                 clearable | ||||||
|  |                                 hide-details | ||||||
|  |                                 density="compact" | ||||||
|  |                                 append-inner-icon="mdi-magnify" | ||||||
|  |                                 item-title="displayName" | ||||||
|  |                                 item-value="id" | ||||||
|  |                                 required | ||||||
|  |                             ></v-combobox> | ||||||
|  |                         </v-card-text> | ||||||
|  |                     </using-query-result> | ||||||
|  | 
 | ||||||
|  |                     <GroupSelector | ||||||
|  |                         :classId="selectedClass?.id" | ||||||
|  |                         :groups="groups" | ||||||
|  |                         @groupCreated="addGroupToList" | ||||||
|  |                     /> | ||||||
|  | 
 | ||||||
|  |                     <!-- Counter for created groups --> | ||||||
|  |                     <v-card-text v-if="groups.length"> | ||||||
|  |                         <strong>Created Groups: {{ groups.length }}</strong> | ||||||
|  |                     </v-card-text> | ||||||
|  |                     <DeadlineSelector v-model:deadline="deadline"/> | ||||||
|  |                     <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-form> | ||||||
|  |         </v-card> | ||||||
|  |     </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped> | <style scoped> | ||||||
|  | .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%; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .step-container { | ||||||
|  |         min-height: 300px; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @media (max-width: 650px) { | ||||||
|  |     .form-card { | ||||||
|  |         width: 95%; | ||||||
|  |     } | ||||||
|  | } | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
|  | @ -4,27 +4,14 @@ import auth from "@/services/auth/auth-service.ts"; | ||||||
| import {computed, ref} from "vue"; | import {computed, ref} from "vue"; | ||||||
| import StudentAssignment from "@/views/assignments/StudentAssignment.vue"; | import StudentAssignment from "@/views/assignments/StudentAssignment.vue"; | ||||||
| import TeacherAssignment from "@/views/assignments/TeacherAssignment.vue"; | import TeacherAssignment from "@/views/assignments/TeacherAssignment.vue"; | ||||||
| import {asyncComputed} from "@vueuse/core"; |  | ||||||
| import {GroupController} from "@/controllers/groups.ts"; |  | ||||||
| import {useRoute} from "vue-router"; | import {useRoute} from "vue-router"; | ||||||
| 
 | 
 | ||||||
| const role = auth.authState.activeRole; | const role = auth.authState.activeRole; | ||||||
| const isTeacher = computed(() => role === 'teacher'); | const isTeacher = computed(() => role === 'teacher'); | ||||||
| 
 | 
 | ||||||
| // Get the user's username/id |  | ||||||
| const username = asyncComputed(async () => { |  | ||||||
|     const user = await auth.loadUser(); |  | ||||||
|     return user?.profile?.preferred_username ?? undefined |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| const route = useRoute(); | const route = useRoute(); | ||||||
|  | const classId = ref<string>(route.params.classId as string); | ||||||
| const assignmentId = ref(Number(route.params.id)); | const assignmentId = ref(Number(route.params.id)); | ||||||
| const classId = window.history.state?.class_id; |  | ||||||
| 
 |  | ||||||
| const groupController = new GroupController(classId, assignmentId.value); |  | ||||||
| 
 |  | ||||||
| const groupDTOs = asyncComputed(async () => await groupController.getAll(true)); |  | ||||||
| console.log(groupDTOs.value); |  | ||||||
| 
 | 
 | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | @ -32,14 +19,12 @@ console.log(groupDTOs.value); | ||||||
|     <TeacherAssignment |     <TeacherAssignment | ||||||
|         :class-id="classId" |         :class-id="classId" | ||||||
|         :assignment-id="assignmentId" |         :assignment-id="assignmentId" | ||||||
|         :groups="groupDTOs" |  | ||||||
|         v-if="isTeacher" |         v-if="isTeacher" | ||||||
|     > |     > | ||||||
|     </TeacherAssignment> |     </TeacherAssignment> | ||||||
|     <StudentAssignment |     <StudentAssignment | ||||||
|         :class-id="classId" |         :class-id="classId" | ||||||
|         :assignment-id="assignmentId" |         :assignment-id="assignmentId" | ||||||
|         :groups="groupDTOs" |  | ||||||
|         v-else |         v-else | ||||||
|     > |     > | ||||||
|     </StudentAssignment> |     </StudentAssignment> | ||||||
|  |  | ||||||
|  | @ -5,16 +5,13 @@ import {useI18n} from "vue-i18n"; | ||||||
| import {useAssignmentQuery} from "@/queries/assignments.ts"; | import {useAssignmentQuery} from "@/queries/assignments.ts"; | ||||||
| import UsingQueryResult from "@/components/UsingQueryResult.vue"; | import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||||
| import type {AssignmentResponse} from "@/controllers/assignments.ts"; | import type {AssignmentResponse} from "@/controllers/assignments.ts"; | ||||||
| import type {GroupDTO} from "@dwengo-1/common/interfaces/group"; |  | ||||||
| import {asyncComputed} from "@vueuse/core"; | import {asyncComputed} from "@vueuse/core"; | ||||||
| import {useStudentsByUsernamesQuery} from "@/queries/students.ts"; | import {useStudentsByUsernamesQuery} from "@/queries/students.ts"; | ||||||
| import {AssignmentDTO} from "@dwengo-1/common/dist/interfaces/assignment.ts"; | import {useGroupsQuery} from "@/queries/groups.ts"; | ||||||
| import {StudentDTO} from "@dwengo-1/common/dist/interfaces/student.ts"; |  | ||||||
| 
 | 
 | ||||||
| const props = defineProps<{ | const props = defineProps<{ | ||||||
|     classId: string |     classId: string | ||||||
|     assignmentId: number |     assignmentId: number | ||||||
|     groups: GroupDTO[] | undefined |  | ||||||
| }>(); | }>(); | ||||||
| 
 | 
 | ||||||
| const {t, locale} = useI18n(); | const {t, locale} = useI18n(); | ||||||
|  | @ -26,23 +23,19 @@ const username = asyncComputed(async () => { | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); | const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); | ||||||
|  | const submitted = ref(false);//TODO: update by fetching submissions and check if group submitted | ||||||
| 
 | 
 | ||||||
| const submitted = ref(true);//TODO: update by fetching submissions and check group | const groupsQueryResult = useGroupsQuery(props.classId, props.assignmentId, true); | ||||||
| 
 | const group = computed(() => | ||||||
| const submitAssignment = async () => { |         groupsQueryResult?.data.value?.groups.find(group => | ||||||
|     //TODO |             group.members?.some(m => m.username === username.value) | ||||||
| }; |         ) | ||||||
| 
 |  | ||||||
| const group = computed(() => { |  | ||||||
|     return props?.groups?.find(group => |  | ||||||
|         group.members.some(m => m.username === username.value) |  | ||||||
|     ); |  | ||||||
|     /** For testing |     /** For testing | ||||||
|     return {assignment: 1, |      return {assignment: 1, | ||||||
|         groupNumber: 1, |      groupNumber: 1, | ||||||
|         members: ["testleerling1"]} |      members: ["testleerling1"]} | ||||||
|         */ |      */ | ||||||
| }); | ); | ||||||
| 
 | 
 | ||||||
| // Assuming group.value.members is a list of usernames TODO: case when it's StudentDTO's | // Assuming group.value.members is a list of usernames TODO: case when it's StudentDTO's | ||||||
| const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as string[]); | const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as string[]); | ||||||
|  | @ -75,10 +68,10 @@ const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as | ||||||
|                         {{ t("submitted") }} |                         {{ t("submitted") }} | ||||||
|                     </v-chip> |                     </v-chip> | ||||||
|                 </div> |                 </div> | ||||||
|                 <v-card-title class="text-h4">{{ data.title }}</v-card-title> |                 <v-card-title class="text-h4">{{ data.assignment.title }}</v-card-title> | ||||||
|                 <v-card-subtitle class="subtitle-section"> |                 <v-card-subtitle class="subtitle-section"> | ||||||
|                     <v-btn |                     <v-btn | ||||||
|                         :to="`/learningPath/${language}/${data.learningPath}`" |                         :to="`/learningPath/${language}/${data.assignment.learningPath}`" | ||||||
|                         variant="tonal" |                         variant="tonal" | ||||||
|                         color="primary" |                         color="primary" | ||||||
|                     > |                     > | ||||||
|  | @ -87,30 +80,20 @@ const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as | ||||||
|                 </v-card-subtitle> |                 </v-card-subtitle> | ||||||
| 
 | 
 | ||||||
|                 <v-card-text class="description"> |                 <v-card-text class="description"> | ||||||
|                     {{ data.description }} |                     {{ data.assignment.description }} | ||||||
|                 </v-card-text> |                 </v-card-text> | ||||||
| 
 | 
 | ||||||
|                 <v-card-text class="group-section"> |                 <v-card-text class="group-section"> | ||||||
|                     <h3>{{ t("group") }}</h3> |                     <h3>{{ t("group") }}</h3> | ||||||
|                     <div v-if="studentQueries"> |                     <div v-if="studentQueries"> | ||||||
|                         <ul> |                         <ul> | ||||||
|                             <li v-for="student in studentQueries" :key="student.data?.student.id"> |                             <li v-for="student in group?.members" :key="student.username"> | ||||||
|                                 {{ student.data?.student.firstName + ' ' + student.data?.student.lastName }} |                                 {{ student.firstName + ' ' + student.lastName }} | ||||||
|                             </li> |                             </li> | ||||||
|                         </ul> |                         </ul> | ||||||
|                     </div> |                     </div> | ||||||
| 
 | 
 | ||||||
|                 </v-card-text> |                 </v-card-text> | ||||||
|                 <v-card-actions class="justify-end"> |  | ||||||
|                     <v-btn |  | ||||||
|                         size="large" |  | ||||||
|                         color="success" |  | ||||||
|                         variant="flat" |  | ||||||
|                         @click="submitAssignment" |  | ||||||
|                     > |  | ||||||
|                         {{ t("submit") }} |  | ||||||
|                     </v-btn> |  | ||||||
|                 </v-card-actions> |  | ||||||
| 
 | 
 | ||||||
|             </v-card> |             </v-card> | ||||||
|         </using-query-result> |         </using-query-result> | ||||||
|  |  | ||||||
|  | @ -1,15 +1,17 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import {computed, defineProps} from "vue"; | import {computed, defineProps, ref} from "vue"; | ||||||
| import {useI18n} from "vue-i18n"; | import {useI18n} from "vue-i18n"; | ||||||
| import {AssignmentController, type AssignmentResponse} from "@/controllers/assignments.ts"; | import {AssignmentController, type AssignmentResponse} from "@/controllers/assignments.ts"; | ||||||
| import {useAssignmentQuery} from "@/queries/assignments.ts"; | import {useAssignmentQuery} from "@/queries/assignments.ts"; | ||||||
| import UsingQueryResult from "@/components/UsingQueryResult.vue"; | import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||||
| import type {GroupDTO} from "@dwengo-1/common/interfaces/group"; | import {useGroupsQuery} from "@/queries/groups.ts"; | ||||||
|  | import {useGetLearningPathQuery} from "@/queries/learning-paths.ts"; | ||||||
|  | import type {Language} from "@/data-objects/language.ts"; | ||||||
|  | import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||||
| 
 | 
 | ||||||
| const props = defineProps<{ | const props = defineProps<{ | ||||||
|     classId: string |     classId: string | ||||||
|     assignmentId: number |     assignmentId: number | ||||||
|     groups: GroupDTO[] | undefined |  | ||||||
| }>(); | }>(); | ||||||
| 
 | 
 | ||||||
| const {t, locale} = useI18n(); | const {t, locale} = useI18n(); | ||||||
|  | @ -17,10 +19,43 @@ const language = computed(() => locale.value); | ||||||
| const controller = new AssignmentController(props.classId); | const controller = new AssignmentController(props.classId); | ||||||
| 
 | 
 | ||||||
| const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); | const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); | ||||||
|  | const groupsQueryResult = useGroupsQuery(props.classId, props.assignmentId, true); | ||||||
| 
 | 
 | ||||||
| const deleteAssignment = async () => { | const lpQueryResult = useGetLearningPathQuery( | ||||||
|     await controller.deleteAssignment(props.assignmentId.value); |     computed(() => assignmentQueryResult.data.value?.assignment?.learningPath ?? ""), | ||||||
| }; |     computed(() => language.value as Language) | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | const allGroups = computed(() => { | ||||||
|  |     const groups = groupsQueryResult.data.value?.groups; | ||||||
|  |     if (!groups) return []; | ||||||
|  | 
 | ||||||
|  |     return groups.map(group => ({ | ||||||
|  |         name: `${t('group')} ${group.groupNumber}`, | ||||||
|  |         progress: 0, | ||||||
|  |         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 = ref([ | ||||||
|  |     {title: t('group'), align: 'start', key: 'name'}, | ||||||
|  |     {title: t('progress'), align: 'center', key: 'progress'}, | ||||||
|  |     {title: t('submission'), align: 'center', key: 'submission'} | ||||||
|  | ]); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | async function deleteAssignment(): Promise<void> { | ||||||
|  |     await controller.deleteAssignment(props.assignmentId); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | @ -50,45 +85,93 @@ const deleteAssignment = async () => { | ||||||
|                         <v-icon>mdi-delete</v-icon> |                         <v-icon>mdi-delete</v-icon> | ||||||
|                     </v-btn> |                     </v-btn> | ||||||
|                 </div> |                 </div> | ||||||
|                 <v-card-title class="text-h4">{{ data.title }}</v-card-title> |                 <v-card-title class="text-h4">{{ data.assignment.title }}</v-card-title> | ||||||
|                 <v-card-subtitle class="subtitle-section"> |                 <v-card-subtitle class="subtitle-section"> | ||||||
|                     <v-btn |                     <using-query-result | ||||||
|                         :to="`/learningPath/${language}/${data.learningPath}`" |                         :query-result="lpQueryResult" | ||||||
|                         variant="tonal" |                         v-slot="{ data: lpData }" | ||||||
|                         color="primary" |  | ||||||
|                     > |                     > | ||||||
|                         {{ t("learning-path") }} |                         <v-btn v-if="lpData" | ||||||
|                     </v-btn> |                                :to="`/learningPath/${language}/${lpData.hruid}/${lpData.startNode.learningobjectHruid}`" | ||||||
|  |                                variant="tonal" | ||||||
|  |                                color="primary" | ||||||
|  |                         > | ||||||
|  |                             {{ t("learning-path") }} | ||||||
|  |                         </v-btn> | ||||||
|  |                     </using-query-result> | ||||||
|  | 
 | ||||||
|                 </v-card-subtitle> |                 </v-card-subtitle> | ||||||
| 
 | 
 | ||||||
|                 <v-card-text class="description"> |                 <v-card-text class="description"> | ||||||
|                     {{ data.description }} |                     {{ data.assignment.description }} | ||||||
|                 </v-card-text> |                 </v-card-text> | ||||||
| 
 | 
 | ||||||
|                 <v-card-text class="group-section"> |                 <v-card-text class="group-section"> | ||||||
|                     <h3>{{ t("group") }}</h3> |                     <h3>{{ t("groups") }}</h3> | ||||||
|  |                     <div class="table-scroll"> | ||||||
|  |                         <v-data-table | ||||||
|  |                             :headers="headers" | ||||||
|  |                             :items="allGroups" | ||||||
|  |                             item-key="id" | ||||||
|  |                             class="elevation-1" | ||||||
|  |                         > | ||||||
|  |                             <template v-slot:item.name="{ item }"> | ||||||
|  |                                 <v-btn @click="openGroupDetails(item)" variant="text" color="primary"> | ||||||
|  |                                     {{ item.name }} | ||||||
|  |                                 </v-btn> | ||||||
|  |                             </template> | ||||||
| 
 | 
 | ||||||
|                     <!-- Teacher view |                             <template v-slot:item.progress="{ item }"> | ||||||
|                     <div v-if="isTeacher"> |                                 <v-progress-linear | ||||||
|                         <v-expansion-panels> |                                     :model-value="item.progress" | ||||||
|                             <v-expansion-panel |                                     color="blue-grey" | ||||||
|                                 v-for="(group, index) in assignment.groups" |                                     height="25" | ||||||
|                                 :key="group.id" |                                 > | ||||||
|                             > |                                     <template v-slot:default="{ value }"> | ||||||
|                                 <v-expansion-panel-title> |                                         <strong>{{ Math.ceil(value) }}%</strong> | ||||||
|                                     {{ t("group") }} {{ index + 1 }} |                                     </template> | ||||||
|                                 </v-expansion-panel-title> |                                 </v-progress-linear> | ||||||
|                                 <v-expansion-panel-text> |                             </template> | ||||||
|                                     <ul> | 
 | ||||||
|                                         <li v-for="student in group.members" :key="student.username"> |                             <template v-slot:item.submission="{ item }"> | ||||||
|                                             {{ student.firstName + ' ' + student.lastName }} |                                 <v-btn | ||||||
|                                         </li> |                                     :to="item.submitted ? `${props.assignmentId}/submissions/` : undefined" | ||||||
|                                     </ul> |                                     :color="item.submitted ? 'green' : 'red'" | ||||||
|                                 </v-expansion-panel-text> |                                     variant="text" | ||||||
|                             </v-expansion-panel> |                                     class="text-capitalize" | ||||||
|                         </v-expansion-panels> |                                 > | ||||||
|                     </div>--> |                                     {{ item.submitted ? t('see-submission') : t('not-submitted') }} | ||||||
|  |                                 </v-btn> | ||||||
|  |                             </template> | ||||||
|  | 
 | ||||||
|  |                         </v-data-table> | ||||||
|  |                     </div> | ||||||
|                 </v-card-text> |                 </v-card-text> | ||||||
|  | 
 | ||||||
|  |                 <v-dialog v-model="dialog" max-width="50%"> | ||||||
|  |                     <v-card> | ||||||
|  |                         <v-card-title class="headline">Group Members</v-card-title> | ||||||
|  |                         <v-card-text> | ||||||
|  |                             <v-list> | ||||||
|  |                                 <v-list-item | ||||||
|  |                                     v-for="(member, index) in selectedGroup.members" | ||||||
|  |                                     :key="index" | ||||||
|  |                                 > | ||||||
|  |                                     <v-list-item-content> | ||||||
|  |                                         <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-card-actions> | ||||||
|  |                     </v-card> | ||||||
|  |                 </v-dialog> | ||||||
|  |                 <!-- | ||||||
|                 <v-card-actions class="justify-end"> |                 <v-card-actions class="justify-end"> | ||||||
|                     <v-btn |                     <v-btn | ||||||
|                         size="large" |                         size="large" | ||||||
|  | @ -98,6 +181,7 @@ const deleteAssignment = async () => { | ||||||
|                         {{ t("view-submissions") }} |                         {{ t("view-submissions") }} | ||||||
|                     </v-btn> |                     </v-btn> | ||||||
|                 </v-card-actions> |                 </v-card-actions> | ||||||
|  |                 --> | ||||||
|             </v-card> |             </v-card> | ||||||
|         </using-query-result> |         </using-query-result> | ||||||
|     </div> |     </div> | ||||||
|  | @ -105,5 +189,10 @@ const deleteAssignment = async () => { | ||||||
| 
 | 
 | ||||||
| <style scoped> | <style scoped> | ||||||
| @import "@/assets/assignment.css"; | @import "@/assets/assignment.css"; | ||||||
|  | 
 | ||||||
|  | .table-scroll { | ||||||
|  |     overflow-x: auto; | ||||||
|  |     -webkit-overflow-scrolling: touch; | ||||||
|  | } | ||||||
| </style> | </style> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -27,25 +27,25 @@ if (isTeacher.value) { | ||||||
|     classesQueryResults = useStudentClassesQuery(username, true); |     classesQueryResults = useStudentClassesQuery(username, true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| //TODO: replace with query from classes | //TODO: remove later | ||||||
| const classController = new ClassController(); | const classController = new ClassController(); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| //TODO: replace by query that fetches all user's assignment | //TODO: replace by query that fetches all user's assignment | ||||||
| const assignments = asyncComputed(async () => { | const assignments = asyncComputed(async () => { | ||||||
|     const classes = classesQueryResults?.data?.value?.classes; |     const classes = classesQueryResults?.data?.value?.classes; | ||||||
|     if (!classes) return []; |     if (!classes) return []; | ||||||
|     const result = await Promise.all( |     const result = await Promise.all( | ||||||
|         (classes as ClassDTO[]).map(async (cls) => { |         (classes as ClassDTO[]).map(async (cls) => { | ||||||
|             //TODO: replace by class queries |  | ||||||
|             const {assignments} = await classController.getAssignments(cls.id); |             const {assignments} = await classController.getAssignments(cls.id); | ||||||
|             return assignments.map(a => ({ |             return assignments.map(a => ({ | ||||||
|                 id: a.id, |                 id: a.id, | ||||||
|                 class: cls, // replace by the whole ClassDTO object |                 class: cls, | ||||||
|                 title: a.title, |                 title: a.title, | ||||||
|                 description: a.description, |                 description: a.description, | ||||||
|                 learningPath: a.learningPath, |                 learningPath: a.learningPath, | ||||||
|                 language: a.language, |                 language: a.language, | ||||||
|                 groups: [] |                 groups: a.groups | ||||||
|             })); |             })); | ||||||
|         }) |         }) | ||||||
|     ); |     ); | ||||||
|  | @ -54,23 +54,20 @@ const assignments = asyncComputed(async () => { | ||||||
| }, []); | }, []); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| const goToCreateAssignment = async () => { | async function goToCreateAssignment(): Promise<void> { | ||||||
|     await router.push('/assignment/create'); |     await router.push('/assignment/create'); | ||||||
| }; | } | ||||||
| 
 | 
 | ||||||
| const goToAssignmentDetails = async (id: number, class_id: string) => { | async function goToAssignmentDetails(id: number, clsId: string): Promise<void> { | ||||||
|     await router.push({ |     await router.push(`/assignment/${clsId}/${id}`); | ||||||
|         path: `/assignment/${id}`, | } | ||||||
|         state: {class_id}, |  | ||||||
|     }); |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| const goToDeleteAssignment = async (id: number, class_id: string) => { | async function goToDeleteAssignment(id: number, clsId: string): Promise<void> { | ||||||
|     //TODO: replace with query |     //TODO: replace with query | ||||||
|     const controller = new AssignmentController(class_id); |     const controller = new AssignmentController(clsId); | ||||||
|     await controller.deleteAssignment(id); |     await controller.deleteAssignment(id); | ||||||
| }; | } | ||||||
| 
 | 
 | ||||||
| onMounted(async () => { | onMounted(async () => { | ||||||
|     const user = await auth.loadUser(); |     const user = await auth.loadUser(); | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 Joyelle Ndagijimana
						Joyelle Ndagijimana