179 lines
5.8 KiB
Vue
179 lines
5.8 KiB
Vue
<script setup lang="ts">
|
|
import log from "loglevel";
|
|
import { computed, onMounted, ref } from "vue";
|
|
import {
|
|
courseIdForCourseSlug,
|
|
fetchMentorCompetenceSummary,
|
|
} from "@/services/dashboard";
|
|
import type { AssignmentStatisticsRecordType, BaseStatisticsType } from "@/gql/graphql";
|
|
import { useDashboardStore } from "@/stores/dashboard";
|
|
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
|
import _ from "lodash";
|
|
import { percentToRoundedGrade } from "@/services/assignmentService";
|
|
|
|
const dashboardStore = useDashboardStore();
|
|
|
|
const props = defineProps<{
|
|
agentRole: string;
|
|
courseSlug: string;
|
|
}>();
|
|
|
|
log.debug("AgentCompetenceGradePage created", props);
|
|
|
|
const loading = ref(true);
|
|
const courseId = ref<string | undefined>(undefined);
|
|
const agentAssignmentData = ref<BaseStatisticsType | null>(null);
|
|
|
|
const courseSessionName = (courseSessionId: string) => {
|
|
return (
|
|
agentAssignmentData.value?.course_session_properties?.sessions.find(
|
|
(session) => session.id === courseSessionId
|
|
)?.name ?? ""
|
|
);
|
|
};
|
|
|
|
onMounted(async () => {
|
|
await dashboardStore.loadDashboardDetails();
|
|
courseId.value = courseIdForCourseSlug(
|
|
dashboardStore.dashboardConfigsv2,
|
|
props.courseSlug
|
|
);
|
|
|
|
if (!courseId.value) {
|
|
log.error("CourseId not found for courseSlug", props.courseSlug);
|
|
return;
|
|
}
|
|
|
|
log.debug("courseId", courseId.value);
|
|
agentAssignmentData.value = await fetchMentorCompetenceSummary(
|
|
courseId.value,
|
|
props.agentRole
|
|
);
|
|
|
|
loading.value = false;
|
|
});
|
|
|
|
type GroupedAssignments = {
|
|
competenceCertificateId: string;
|
|
competenceCertificateTitle: string;
|
|
generation: string;
|
|
courseSessionId: string;
|
|
assignments: AssignmentStatisticsRecordType[];
|
|
sumAverageEvaluationPercent: number;
|
|
averageEvaluationPercent: number | null;
|
|
averageGrade: number | null;
|
|
};
|
|
|
|
const courseSessionCompetenceAssignments = computed(() => {
|
|
let resultArray = [] as GroupedAssignments[];
|
|
|
|
// group assignments by competence and course session
|
|
for (const assignment of agentAssignmentData.value?.assignments.records ?? []) {
|
|
const entry = resultArray.find(
|
|
(r) =>
|
|
r.competenceCertificateId === assignment.competence_certificate_id &&
|
|
r.courseSessionId === assignment.course_session_id
|
|
);
|
|
if (entry) {
|
|
if (assignment.metrics.ranking_completed) {
|
|
entry.assignments.push(assignment);
|
|
}
|
|
} else {
|
|
const newEntry = {
|
|
competenceCertificateId: assignment.competence_certificate_id ?? "",
|
|
competenceCertificateTitle: assignment.competence_certificate_title ?? "",
|
|
generation: assignment.generation ?? "",
|
|
courseSessionId: assignment.course_session_id ?? "",
|
|
assignments: [] as AssignmentStatisticsRecordType[],
|
|
sumAverageEvaluationPercent: 0,
|
|
averageEvaluationPercent: null,
|
|
averageGrade: null,
|
|
};
|
|
if (assignment && assignment.metrics.ranking_completed) {
|
|
newEntry.assignments.push(assignment);
|
|
}
|
|
resultArray.push(newEntry);
|
|
}
|
|
}
|
|
|
|
// filter out entries without assignments
|
|
resultArray = resultArray.filter((entry) => entry.assignments.length > 0);
|
|
|
|
// calculate average grade
|
|
for (const entry of resultArray) {
|
|
entry.sumAverageEvaluationPercent = _.sumBy(entry.assignments, (a) => {
|
|
return (
|
|
(a.metrics.average_evaluation_percent ?? 0) *
|
|
(a.metrics.competence_certificate_weight ?? 1)
|
|
);
|
|
});
|
|
entry.averageEvaluationPercent =
|
|
entry.sumAverageEvaluationPercent /
|
|
_.sumBy(entry.assignments, (a) => {
|
|
return a.metrics.competence_certificate_weight ?? 1;
|
|
});
|
|
entry.averageGrade = percentToRoundedGrade(entry.averageEvaluationPercent, false);
|
|
}
|
|
return resultArray;
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="bg-gray-200">
|
|
<div v-if="loading" class="m-8 flex justify-center">
|
|
<LoadingSpinner />
|
|
</div>
|
|
<div v-else class="container-large flex flex-col space-y-8">
|
|
<router-link class="btn-text inline-flex items-center pl-0" to="/">
|
|
<it-icon-arrow-left />
|
|
<span>{{ $t("general.back") }}</span>
|
|
</router-link>
|
|
<div>
|
|
<h2 class="text-2xl font-bold">{{ $t("a.Statistik") }}</h2>
|
|
<p class="text-gray-800">
|
|
{{ $t("a.Statistik für alle Lernenden") }}
|
|
</p>
|
|
|
|
<div class="bg-white px-4 py-2">
|
|
<section
|
|
class="flex flex-col space-x-0 border-b bg-white lg:flex-row lg:space-x-3"
|
|
></section>
|
|
<div
|
|
v-for="entry in courseSessionCompetenceAssignments"
|
|
:key="entry.courseSessionId"
|
|
:data-cy="`entry-${entry.courseSessionId}`"
|
|
class="flex flex-col justify-between gap-4 border-b p-2 last:border-b-0 md:flex-row md:items-center md:justify-between md:gap-16"
|
|
>
|
|
<div class="w-full flex-auto md:w-1/3">
|
|
{{ entry.competenceCertificateTitle }}
|
|
<br />
|
|
{{ $t("a.Durchführung") }} «{{
|
|
courseSessionName(entry.courseSessionId)
|
|
}}»
|
|
</div>
|
|
|
|
<div class="flex w-full flex-auto items-start md:w-1/3">
|
|
<div class="flex">
|
|
<div>{{ $t("a.Durchschnittsnote") }}:</div>
|
|
<div class="w-16 text-center">
|
|
{{ entry.averageGrade }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="w-full flex-auto items-end md:w-1/3 md:text-end">
|
|
<router-link
|
|
class="underline"
|
|
:to="`/statistic/${props.agentRole}/${props.courseSlug}/competence-grade/${entry.courseSessionId}/${entry.competenceCertificateId}`"
|
|
data-cy="basebox.detailsLink"
|
|
>
|
|
{{ $t("a.Details anschauen") }}
|
|
</router-link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|