feat: bericht in assignment pagina van student als er nog geen groepen bestaan

This commit is contained in:
Joyelle Ndagijimana 2025-05-15 23:02:07 +02:00
parent 5805294f4c
commit f83d5b54c0
4 changed files with 112 additions and 99 deletions

View file

@ -121,5 +121,6 @@
"invite": "invite", "invite": "invite",
"assignmentIndicator": "ASSIGNMENT", "assignmentIndicator": "ASSIGNMENT",
"searchAllLearningPathsTitle": "Search all learning paths", "searchAllLearningPathsTitle": "Search all learning paths",
"searchAllLearningPathsDescription": "You didn't find what you were looking for? Click here to search our whole database of available learning paths." "searchAllLearningPathsDescription": "You didn't find what you were looking for? Click here to search our whole database of available learning paths.",
"not-in-group-message": "You are not part of a group yet"
} }

View file

@ -0,0 +1,5 @@
import type { LearningPath } from "@/data-objects/learning-paths/learning-path.ts";
export function calculateProgress(lp: LearningPath): number {
return ((lp.amountOfNodes - lp.amountOfNodesLeft) / lp.amountOfNodes) * 100;
}

View file

@ -1,75 +1,93 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, type Ref } from "vue"; import {ref, computed, watchEffect} from "vue";
import auth from "@/services/auth/auth-service.ts"; import auth from "@/services/auth/auth-service.ts";
import { useI18n } from "vue-i18n"; 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 { asyncComputed } from "@vueuse/core"; import {asyncComputed} from "@vueuse/core";
import { useStudentsByUsernamesQuery } from "@/queries/students.ts"; import {useStudentsByUsernamesQuery} from "@/queries/students.ts";
import { useGroupsQuery } from "@/queries/groups.ts"; import {useGroupsQuery} from "@/queries/groups.ts";
import { useGetLearningPathQuery } from "@/queries/learning-paths.ts"; import {useGetLearningPathQuery} from "@/queries/learning-paths.ts";
import type { Language } from "@/data-objects/language.ts"; import type {Language} from "@/data-objects/language.ts";
import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; import {calculateProgress} from "@/utils/assignment-utils.ts";
const props = defineProps<{ const props = defineProps<{
classId: string; classId: string;
assignmentId: number; assignmentId: number;
useGroupsWithProgress: ( }>();
groups: Ref<GroupDTO[]>,
hruid: Ref<string>,
language: Ref<Language>,
) => { groupProgressMap: Map<number, number> };
}>();
const { t, locale } = useI18n(); const {t} = useI18n();
const language = ref<Language>(locale.value as Language); const lang = ref();
const learningPath = ref(); const learningPath = ref();
// Get the user's username/id // Get the user's username/id
const username = asyncComputed(async () => { const username = asyncComputed(async () => {
const user = await auth.loadUser(); const user = await auth.loadUser();
return user?.profile?.preferred_username ?? undefined; return user?.profile?.preferred_username ?? undefined;
}); });
const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId); const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId);
learningPath.value = assignmentQueryResult.data.value?.assignment?.learningPath;
const groupsQueryResult = useGroupsQuery(props.classId, props.assignmentId, true);
const group = computed(() => {
const groups = groupsQueryResult.data.value?.groups;
if (!groups) return undefined;
// Sort by original groupNumber
const sortedGroups = [...groups].sort((a, b) => a.groupNumber - b.groupNumber);
return sortedGroups
.map((group, index) => ({
...group,
groupNo: index + 1, // Renumbered index
}))
.find((group) => group.members?.some((m) => m.username === username.value));
});
watchEffect(() => {
learningPath.value = assignmentQueryResult.data.value?.assignment?.learningPath; learningPath.value = assignmentQueryResult.data.value?.assignment?.learningPath;
lang.value = assignmentQueryResult.data.value?.assignment?.language as Language;
});
const submitted = ref(false); //TODO: update by fetching submissions and check if group submitted const learningPathParams = computed(() => {
if (!group.value || !learningPath.value || !lang.value) return undefined;
const lpQueryResult = useGetLearningPathQuery( return {
computed(() => assignmentQueryResult.data.value?.assignment?.learningPath ?? ""), forGroup: group.value.groupNumber,
computed(() => language.value), assignmentNo: props.assignmentId,
); classId: props.classId,
};
});
const groupsQueryResult = useGroupsQuery(props.classId, props.assignmentId, true); const lpQueryResult = useGetLearningPathQuery(
const group = computed(() => () => learningPath.value,
groupsQueryResult?.data.value?.groups.find((group) => () => lang.value,
group.members?.some((m) => m.username === username.value), () => learningPathParams.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 {groupProgressMap} = props.useGroupsWithProgress(
groupArray,
learningPath,
language
); );
*/
// 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 progressColor = computed(() => {
const progress = calculateProgress(lpQueryResult.data.value);
if (progress >= 100) return "success";
if (progress >= 50) return "warning";
return "error";
});
const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as string[] ?? undefined);
</script> </script>
<template> <template>
<div class="container"> <div class="container">
<using-query-result <using-query-result
:query-result="assignmentQueryResult" :query-result="assignmentQueryResult"
v-slot="{ data }: { data: AssignmentResponse }" v-slot="assignmentResponse : { data: AssignmentResponse }"
> >
<v-card <v-card
v-if="data" v-if="assignmentResponse"
class="assignment-card" class="assignment-card"
> >
<div class="top-buttons"> <div class="top-buttons">
@ -81,17 +99,11 @@ language
> >
<v-icon>mdi-arrow-left</v-icon> <v-icon>mdi-arrow-left</v-icon>
</v-btn> </v-btn>
<v-chip
v-if="submitted"
class="ma-2 top-right-btn"
label
color="success"
>
{{ t("submitted") }}
</v-chip>
</div> </div>
<v-card-title class="text-h4 assignmentTopTitle">{{ data.assignment.title }}</v-card-title> <v-card-title class="text-h4 assignmentTopTitle">{{
assignmentResponse.data.assignment.title
}}
</v-card-title>
<v-card-subtitle class="subtitle-section"> <v-card-subtitle class="subtitle-section">
<using-query-result <using-query-result
@ -100,30 +112,31 @@ language
> >
<v-btn <v-btn
v-if="lpData" v-if="lpData"
:to="`/learningPath/${lpData.hruid}/${language}/${lpData.startNode.learningobjectHruid}?forGroup=${group?.groupNumber}&assignmentNo=${assignmentId}&classId=${classId}`" :to="group ? `/learningPath/${lpData.hruid}/${assignmentResponse.data.assignment?.language}/${lpData.startNode.learningobjectHruid}?forGroup=${0}&assignmentNo=${assignmentId}&classId=${classId}` : undefined"
:disabled="!group"
variant="tonal" variant="tonal"
color="primary" color="primary"
> >
{{ t("learning-path") }} {{ t("learning-path") }}
</v-btn> </v-btn>
</using-query-result> </using-query-result>
</v-card-subtitle> </v-card-subtitle>
<v-card-text class="description"> <v-card-text class="description">
{{ data.assignment.description }} {{ assignmentResponse.data.assignment.description }}
</v-card-text> </v-card-text>
<v-card-text> <v-card-text>
<v-row <v-card-text>
align="center" <h3 class="mb-2">{{ t("progress") }}</h3>
no-gutters <using-query-result
:query-result="lpQueryResult"
v-slot="{ data: learningPData }"
> >
<v-col cols="auto">
<span class="progress-label">{{ t("progress") + ": " }}</span>
</v-col>
<v-col>
<v-progress-linear <v-progress-linear
:model-value="progressValue" v-if="group"
color="primary" :model-value="calculateProgress(learningPData)"
:color="progressColor"
height="20" height="20"
class="progress-bar" class="progress-bar"
> >
@ -131,37 +144,40 @@ language
<strong>{{ Math.ceil(value) }}%</strong> <strong>{{ Math.ceil(value) }}%</strong>
</template> </template>
</v-progress-linear> </v-progress-linear>
</v-col> </using-query-result>
</v-row> </v-card-text>
</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="group && studentQueries">
<ul> <ul>
<li <li
v-for="student in group?.members" v-for="student in group.members"
:key="student.username" :key="student.username"
> >
{{ student.firstName + " " + student.lastName }} {{ student.firstName + " " + student.lastName }}
</li> </li>
</ul> </ul>
</div> </div>
<div v-else>
<v-alert type="info" variant="text">
{{ t("not-in-group-message") }}
</v-alert>
</div>
</v-card-text> </v-card-text>
</v-card> </v-card>
</using-query-result> </using-query-result>
</div> </div>
</template> </template>
<style scoped> <style scoped>
@import "@/assets/assignment.css"; @import "@/assets/assignment.css";
.progress-label { .progress-bar {
font-weight: bold;
margin-right: 5px;
}
.progress-bar {
width: 40%; width: 40%;
} }
</style> </style>

View file

@ -49,15 +49,6 @@
}, },
]; ];
const descriptionRules = [
(value: string): string | boolean => {
if (!value || value.trim() === "") {
return "Description cannot be empty.";
}
return true;
},
];
// Get all the groups withing the assignment // Get all the groups withing the assignment
const groupsQueryResult = useGroupsQuery(props.classId, props.assignmentId, true); const groupsQueryResult = useGroupsQuery(props.classId, props.assignmentId, true);
groups.value = groupsQueryResult.data.value?.groups; groups.value = groupsQueryResult.data.value?.groups;