228 lines
8.2 KiB
Vue
228 lines
8.2 KiB
Vue
<script setup lang="ts">
|
|
import FeedbackSummary from "@/components/feedback/feedbackSummary.vue";
|
|
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
|
|
import ItPersonRow from "@/components/ui/ItPersonRow.vue";
|
|
import ItProgress from "@/components/ui/ItProgress.vue";
|
|
import type { LearningPath } from "@/services/learningPath";
|
|
|
|
import { useCockpitStore } from "@/stores/cockpit";
|
|
import { useCompetenceStore } from "@/stores/competence";
|
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
|
import { useLearningPathStore } from "@/stores/learningPath";
|
|
import { useUserStore } from "@/stores/user";
|
|
import groupBy from "lodash/groupBy";
|
|
import log from "loglevel";
|
|
import { computed } from "vue";
|
|
|
|
const props = defineProps<{
|
|
courseSlug: string;
|
|
}>();
|
|
|
|
log.debug("CockpitIndexPage created", props.courseSlug);
|
|
|
|
const userStore = useUserStore();
|
|
const cockpitStore = useCockpitStore();
|
|
const competenceStore = useCompetenceStore();
|
|
const learningPathStore = useLearningPathStore();
|
|
const courseSessionStore = useCourseSessionsStore();
|
|
|
|
function userCountStatusForCircle(userId: number, translationKey: string) {
|
|
const criteria = competenceStore.flatPerformanceCriteria(
|
|
userId,
|
|
cockpitStore.selectedCircles
|
|
);
|
|
const grouped = groupBy(criteria, "circle.translation_key");
|
|
|
|
return competenceStore.calcStatusCount(grouped[translationKey]);
|
|
}
|
|
|
|
const circles = computed(() => {
|
|
const learningPathCircles = learningPathStore
|
|
.learningPathForUser(props.courseSlug, userStore.id)
|
|
?.circles.map((c) => {
|
|
return {
|
|
id: c.id,
|
|
title: c.title,
|
|
slug: c.slug,
|
|
translation_key: c.translation_key,
|
|
};
|
|
});
|
|
|
|
if (cockpitStore.cockpitSessionUser?.circles?.length) {
|
|
return cockpitStore.cockpitSessionUser.circles;
|
|
} else if (learningPathCircles) {
|
|
return learningPathCircles;
|
|
} else {
|
|
return [];
|
|
}
|
|
});
|
|
|
|
const selectedCirclesTitles = computed(() => {
|
|
return circles.value
|
|
.filter((c) => cockpitStore.selectedCircles.includes(c.translation_key))
|
|
.map((c) => c.title);
|
|
});
|
|
|
|
const data = {
|
|
transferProgress: {
|
|
fail: 0,
|
|
success: 3,
|
|
unknown: 8,
|
|
},
|
|
};
|
|
|
|
function setActiveClasses(translationKey: string) {
|
|
return cockpitStore.selectedCircles.indexOf(translationKey) > -1
|
|
? ["bg-blue-900", "text-white"]
|
|
: ["text-bg-900"];
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="bg-gray-200">
|
|
<div class="container-large">
|
|
<div class="mb-9 flex items-center lg:flex-row">
|
|
<h1 class="heading-3">{{ $t("general.circles") }}:</h1>
|
|
<ul class="ml-4 flex flex-row text-base font-bold leading-7">
|
|
<li
|
|
v-for="circle in circles"
|
|
:key="circle.translation_key"
|
|
class="mr-4 last:mr-0"
|
|
>
|
|
<button
|
|
class="mr-4 rounded-full border-2 border-blue-900 px-4 last:mr-0"
|
|
:class="setActiveClasses(circle.translation_key)"
|
|
@click="cockpitStore.toggleCourseSelection(circle.translation_key)"
|
|
>
|
|
{{ circle.title }}
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<!-- Status -->
|
|
<div class="mb-4 grid grid-rows-2 gap-4 lg:grid-cols-2 lg:grid-rows-none">
|
|
<div class="bg-white px-6 py-5">
|
|
<h1
|
|
class="heading-3 mb-4 bg-assignment bg-60 bg-no-repeat pl-[68px] leading-[60px]"
|
|
>
|
|
{{ $t("general.transferTask", 2) }}
|
|
</h1>
|
|
<div class="mb-4">
|
|
<ItProgress :status-count="data.transferProgress"></ItProgress>
|
|
</div>
|
|
<p>{{ $t("cockpit.tasksDone") }}</p>
|
|
</div>
|
|
<div class="bg-white px-6 py-5">
|
|
<h1
|
|
class="heading-3 mb-4 bg-test bg-60 bg-no-repeat pl-[68px] leading-[60px]"
|
|
>
|
|
{{ $t("general.examResult", 2) }}
|
|
</h1>
|
|
<div class="mb-4">
|
|
<ItProgress :status-count="data.transferProgress"></ItProgress>
|
|
</div>
|
|
<p>{{ $t("cockpit.examsDone") }}</p>
|
|
</div>
|
|
</div>
|
|
<!-- Feedback -->
|
|
<FeedbackSummary
|
|
:selcted-circles="cockpitStore.selectedCircles || []"
|
|
:circles="
|
|
learningPathStore.learningPathForUser(props.courseSlug, userStore.id)
|
|
?.circles || []
|
|
"
|
|
:course-id="courseSessionStore.courseSessionForRoute?.course.id || 0"
|
|
:url="courseSessionStore.courseSessionForRoute?.course_url || ''"
|
|
></FeedbackSummary>
|
|
<div>
|
|
<!-- progress -->
|
|
<div v-if="cockpitStore.courseSessionUsers?.length" class="bg-white p-6">
|
|
<h1 class="heading-3 mb-5">{{ $t("cockpit.progress") }}</h1>
|
|
<ul>
|
|
<ItPersonRow
|
|
v-for="csu in cockpitStore.courseSessionUsers"
|
|
:key="csu.user_id + csu.session_title"
|
|
:name="`${csu.first_name} ${csu.last_name}`"
|
|
:avatar-url="csu.avatar_url"
|
|
>
|
|
<template #center>
|
|
<div
|
|
class="mt-2 flex w-full flex-row items-center justify-between lg:mt-0"
|
|
>
|
|
<ul class="w-full">
|
|
<li
|
|
class="flex h-12 items-center justify-between"
|
|
v-for="(circle, i) of cockpitStore.selectedCircles"
|
|
:key="i"
|
|
>
|
|
<LearningPathDiagram
|
|
v-if="
|
|
learningPathStore.learningPathForUser(
|
|
props.courseSlug,
|
|
csu.user_id
|
|
)
|
|
"
|
|
:learning-path="
|
|
learningPathStore.learningPathForUser(
|
|
props.courseSlug,
|
|
csu.user_id
|
|
) as LearningPath
|
|
"
|
|
:postfix="`cockpit-${csu.user_id}-${i}`"
|
|
:profile-user-id="`${csu.user_id}`"
|
|
:show-circle-translation-keys="[circle]"
|
|
:pull-up="false"
|
|
diagram-type="singleSmall"
|
|
class="mr-4"
|
|
></LearningPathDiagram>
|
|
<p class="lg:min-w-[150px]">
|
|
{{ selectedCirclesTitles[i] }}
|
|
</p>
|
|
<div class="ml-4 flex flex-row items-center">
|
|
<div class="mr-6 flex flex-row items-center">
|
|
<it-icon-smiley-thinking
|
|
class="mr-2 inline-block h-8 w-8"
|
|
></it-icon-smiley-thinking>
|
|
<p class="text-bold inline-block">
|
|
{{ userCountStatusForCircle(csu.user_id, circle).fail }}
|
|
</p>
|
|
</div>
|
|
<li class="mr-6 flex flex-row items-center">
|
|
<it-icon-smiley-happy
|
|
class="mr-2 inline-block h-8 w-8"
|
|
></it-icon-smiley-happy>
|
|
<p class="text-bold inline-block">
|
|
{{ userCountStatusForCircle(csu.user_id, circle).success }}
|
|
</p>
|
|
</li>
|
|
<li class="flex flex-row items-center">
|
|
<it-icon-smiley-neutral
|
|
class="mr-2 inline-block h-8 w-8"
|
|
></it-icon-smiley-neutral>
|
|
<p class="text-bold inline-block">
|
|
{{ userCountStatusForCircle(csu.user_id, circle).unknown }}
|
|
</p>
|
|
</li>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</template>
|
|
<template #link>
|
|
<router-link
|
|
:to="`/course/${props.courseSlug}/cockpit/profile/${csu.user_id}`"
|
|
class="w-full text-right underline"
|
|
>
|
|
{{ $t("general.profileLink") }}
|
|
</router-link>
|
|
</template>
|
|
</ItPersonRow>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped></style>
|