Merged in feature/dashboard-daniel (pull request #317)
Feature/dashboard daniel
This commit is contained in:
commit
a738889b01
|
|
@ -1,31 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { useCurrentCourseSession } from "@/composables";
|
|
||||||
import DueDateSingle from "@/components/dueDates/DueDateSingle.vue";
|
|
||||||
import { computed } from "vue";
|
|
||||||
import { useExpertCockpitStore } from "@/stores/expertCockpit";
|
|
||||||
|
|
||||||
const expertCockpitStore = useExpertCockpitStore();
|
|
||||||
const courseSession = useCurrentCourseSession();
|
|
||||||
|
|
||||||
const circleDates = computed(() => {
|
|
||||||
const dueDates = courseSession.value.due_dates.filter((dueDate) => {
|
|
||||||
if (!expertCockpitStore.currentCircle) return false;
|
|
||||||
return expertCockpitStore.currentCircle.id == dueDate?.circle?.id;
|
|
||||||
});
|
|
||||||
return dueDates.slice(0, 4);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="flex flex-col space-y-2">
|
|
||||||
<h3 class="heading-3">{{ $t("Nächste Termine") }}</h3>
|
|
||||||
<div
|
|
||||||
v-for="dueDate in circleDates"
|
|
||||||
:key="dueDate.id"
|
|
||||||
class="border-t border-gray-500 pt-2"
|
|
||||||
>
|
|
||||||
<DueDateSingle :due-date="dueDate" :single-line="true"></DueDateSingle>
|
|
||||||
</div>
|
|
||||||
<div v-if="circleDates.length === 0">{{ $t("dueDates.noDueDatesAvailable") }}</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
2
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import AssignmentSubmissionProgress from "@/components/assignment/AssignmentSubmissionProgress.vue";
|
import AssignmentSubmissionProgress from "@/components/assignment/AssignmentSubmissionProgress.vue";
|
||||||
import type {
|
import type {
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,14 @@ const progress = computed(() => ({
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<i18next :translation="$t('a.NUMBER Elemente abgeschlossen')">
|
<i18next :translation="$t('a.NUMBER Elemente abgeschlossen')">
|
||||||
<template #NUMBER>
|
<template #NUMBER>
|
||||||
<span class="mr-3 text-4xl font-bold">{{ totalAssignments }}</span>
|
<span class="mr-3 text-xl font-bold">{{ totalAssignments }}</span>
|
||||||
</template>
|
</template>
|
||||||
</i18next>
|
</i18next>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<i18next :translation="$t('a.xOfY Punkten erreicht')">
|
<i18next :translation="$t('a.xOfY Punkten erreicht')">
|
||||||
<template #xOfY>
|
<template #xOfY>
|
||||||
<span class="mr-3 text-4xl font-bold">
|
<span class="mr-3 text-xl font-bold">
|
||||||
{{
|
{{
|
||||||
$t("a.VALUE von MAXIMUM", {
|
$t("a.VALUE von MAXIMUM", {
|
||||||
VALUE: props.achievedPointsCount,
|
VALUE: props.achievedPointsCount,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Ref } from "vue";
|
||||||
|
import { computed, onMounted, ref } from "vue";
|
||||||
|
import type { ProgressDashboardAssignmentType } from "@/gql/graphql";
|
||||||
|
import { fetchProgressData } from "@/services/dashboard";
|
||||||
|
import AssignmentProgressSummaryBox from "@/components/dashboard/AssignmentProgressSummaryBox.vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
courseId: string;
|
||||||
|
courseSlug: string;
|
||||||
|
sessionToContinueId: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const DEFAULT_ASSIGNMENT = {
|
||||||
|
_id: "",
|
||||||
|
points_achieved_count: 0,
|
||||||
|
points_max_count: 0,
|
||||||
|
total_count: 0,
|
||||||
|
};
|
||||||
|
const assignment: Ref<ProgressDashboardAssignmentType | null> = ref(DEFAULT_ASSIGNMENT);
|
||||||
|
|
||||||
|
const competenceCertificateUrl = computed(() => {
|
||||||
|
return `/course/${props.courseSlug}/competence/certificates?courseSessionId=${props.sessionToContinueId}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const data = await fetchProgressData(props.courseId);
|
||||||
|
assignment.value = data?.assignment ?? null;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="assignment">
|
||||||
|
<div class="w-[395px]">
|
||||||
|
<AssignmentProgressSummaryBox
|
||||||
|
:total-assignments="assignment.total_count"
|
||||||
|
:achieved-points-count="assignment.points_achieved_count"
|
||||||
|
:max-points-count="assignment.points_max_count"
|
||||||
|
:details-link="competenceCertificateUrl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -6,6 +6,7 @@ import BaseBox from "@/components/dashboard/BaseBox.vue";
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
assignmentsCompleted: number;
|
assignmentsCompleted: number;
|
||||||
avgPassed: number;
|
avgPassed: number;
|
||||||
|
courseSlug: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const progress = computed(() => {
|
const progress = computed(() => {
|
||||||
|
|
@ -19,7 +20,7 @@ const progress = computed(() => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BaseBox
|
<BaseBox
|
||||||
:details-link="'/statistic/assignment'"
|
:details-link="`/statistic/${courseSlug}/assignment`"
|
||||||
data-cy="dashboard.stats.assignments"
|
data-cy="dashboard.stats.assignments"
|
||||||
>
|
>
|
||||||
<template #title>{{ $t("a.Kompetenznachweis-Elemente") }}</template>
|
<template #title>{{ $t("a.Kompetenznachweis-Elemente") }}</template>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import BaseBox from "@/components/dashboard/BaseBox.vue";
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
daysCompleted: number;
|
daysCompleted: number;
|
||||||
avgParticipantsPresent: number;
|
avgParticipantsPresent: number;
|
||||||
|
courseSlug: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const progressRecord = computed(() => {
|
const progressRecord = computed(() => {
|
||||||
|
|
@ -18,7 +19,10 @@ const progressRecord = computed(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BaseBox :details-link="'/statistic/attendance'" data-cy="dashboard.stats.attendance">
|
<BaseBox
|
||||||
|
:details-link="`/statistic/${props.courseSlug}/attendance`"
|
||||||
|
data-cy="dashboard.stats.attendance"
|
||||||
|
>
|
||||||
<template #title>{{ $t("a.Anwesenheit") }}</template>
|
<template #title>{{ $t("a.Anwesenheit") }}</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ defineProps<{
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col space-y-4 bg-white p-6">
|
<div class="flex h-full flex-col space-y-4 bg-white">
|
||||||
<h4 class="mb-1 font-bold">
|
<h4 class="mb-1 font-bold">
|
||||||
<slot name="title"></slot>
|
<slot name="title"></slot>
|
||||||
</h4>
|
</h4>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Ref } from "vue";
|
||||||
|
import { computed, onMounted, ref } from "vue";
|
||||||
|
import type { ProgressDashboardCompetenceType } from "@/gql/graphql";
|
||||||
|
import CompetenceSummaryBox from "@/components/dashboard/CompetenceSummaryBox.vue";
|
||||||
|
import { fetchProgressData } from "@/services/dashboard";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
courseId: string;
|
||||||
|
courseSlug: string;
|
||||||
|
sessionToContinueId: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const DEFAULT_COMPETENCE = { _id: "", total_count: 0, success_count: 0, fail_count: 0 };
|
||||||
|
const competence: Ref<ProgressDashboardCompetenceType | null> = ref(DEFAULT_COMPETENCE);
|
||||||
|
|
||||||
|
const competenceCriteriaUrl = computed(() => {
|
||||||
|
return `/course/${props.courseSlug}/competence/self-evaluation-and-feedback?courseSessionId=${props.sessionToContinueId}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const data = await fetchProgressData(props.courseId);
|
||||||
|
competence.value = data?.competence ?? null;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="competence" class="w-[395px]">
|
||||||
|
<CompetenceSummaryBox
|
||||||
|
:fail-count="competence.fail_count"
|
||||||
|
:success-count="competence.success_count"
|
||||||
|
:details-link="competenceCriteriaUrl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -13,11 +13,11 @@ defineProps<{
|
||||||
<template #title>{{ $t("a.Selbsteinschätzungen") }}</template>
|
<template #title>{{ $t("a.Selbsteinschätzungen") }}</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<it-icon-smiley-happy class="mr-4 h-12 w-12"></it-icon-smiley-happy>
|
<it-icon-smiley-happy class="mr-4 h-[28px] w-[28px]"></it-icon-smiley-happy>
|
||||||
<i18next :translation="$t('a.{NUMBER} Das kann ich')">
|
<i18next :translation="$t('a.{NUMBER} Das kann ich')">
|
||||||
<template #NUMBER>
|
<template #NUMBER>
|
||||||
<span
|
<span
|
||||||
class="mr-3 text-4xl font-bold"
|
class="mr-3 text-2xl font-bold"
|
||||||
data-cy="dashboard.stats.competence.success"
|
data-cy="dashboard.stats.competence.success"
|
||||||
>
|
>
|
||||||
{{ successCount }}
|
{{ successCount }}
|
||||||
|
|
@ -26,11 +26,13 @@ defineProps<{
|
||||||
</i18next>
|
</i18next>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<it-icon-smiley-thinking class="mr-4 h-12 w-12"></it-icon-smiley-thinking>
|
<it-icon-smiley-thinking
|
||||||
|
class="mr-4 h-[28px] w-[28px]"
|
||||||
|
></it-icon-smiley-thinking>
|
||||||
<i18next :translation="$t('a.{NUMBER} Das will ich nochmals anschauen')">
|
<i18next :translation="$t('a.{NUMBER} Das will ich nochmals anschauen')">
|
||||||
<template #NUMBER>
|
<template #NUMBER>
|
||||||
<span
|
<span
|
||||||
class="mr-3 text-4xl font-bold"
|
class="mr-3 text-2xl font-bold"
|
||||||
data-cy="dashboard.stats.competence.fail"
|
data-cy="dashboard.stats.competence.fail"
|
||||||
>
|
>
|
||||||
{{ failCount }}
|
{{ failCount }}
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import DueDatesList from "@/components/dueDates/DueDatesList.vue";
|
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
|
||||||
import { useDashboardStore } from "@/stores/dashboard";
|
|
||||||
import { getCockpitUrl } from "@/utils/utils";
|
|
||||||
|
|
||||||
const dashboardStore = useDashboardStore();
|
|
||||||
const courseSessionsStore = useCourseSessionsStore();
|
|
||||||
const allDueDates = courseSessionsStore.allDueDates();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<template v-if="dashboardStore.currentDashboardConfig">
|
|
||||||
<h4 class="mb-6 text-xl font-bold">{{ $t("a.Aktueller Lehrgang") }}</h4>
|
|
||||||
|
|
||||||
<div class="mb-6 border border-gray-300 p-6">
|
|
||||||
<h3 class="mb-6">{{ dashboardStore.currentDashboardConfig.name }}</h3>
|
|
||||||
<router-link
|
|
||||||
class="btn-blue"
|
|
||||||
target="_blank"
|
|
||||||
:to="getCockpitUrl(dashboardStore.currentDashboardConfig.slug)"
|
|
||||||
>
|
|
||||||
{{ $t("a.Cockpit anschauen") }}
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<router-link
|
|
||||||
v-if="dashboardStore.dashboardConfigs.length > 1"
|
|
||||||
class="block text-sm underline"
|
|
||||||
to="/statistic/list"
|
|
||||||
>
|
|
||||||
{{ $t("a.Alle Lehrgänge anzeigen") }}
|
|
||||||
</router-link>
|
|
||||||
|
|
||||||
<h3 class="mb-6 mt-16 text-xl font-bold">{{ $t("a.AlleTermine") }}</h3>
|
|
||||||
<DueDatesList
|
|
||||||
:due-dates="allDueDates"
|
|
||||||
:max-count="13"
|
|
||||||
:show-top-border="true"
|
|
||||||
:show-all-due-dates-link="true"
|
|
||||||
:show-bottom-border="true"
|
|
||||||
:show-course-session="true"
|
|
||||||
></DueDatesList>
|
|
||||||
</template>
|
|
||||||
|
|
@ -0,0 +1,174 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from "vue";
|
||||||
|
import type { DashboardCourseConfigType, WidgetType } from "@/services/dashboard";
|
||||||
|
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
|
||||||
|
import CompetenceSummary from "@/components/dashboard/CompetenceSummary.vue";
|
||||||
|
import AssignmentSummary from "@/components/dashboard/AssignmentSummary.vue";
|
||||||
|
import MentorOpenTasksCount from "@/components/dashboard/MentorOpenTasksCount.vue";
|
||||||
|
import MentorMenteeCount from "@/components/dashboard/MentorMenteeCount.vue";
|
||||||
|
import MentorCompetenceSummary from "@/components/dashboard/MentorCompetenceSummary.vue";
|
||||||
|
import { getCockpitUrl, getLearningMentorUrl, getLearningPathUrl } from "@/utils/utils";
|
||||||
|
import UkStatistics from "@/components/dashboard/UkStatistics.vue";
|
||||||
|
|
||||||
|
const mentorWidgets = [
|
||||||
|
"MentorTasksWidget",
|
||||||
|
"MentorPersonWidget",
|
||||||
|
"MentorCompetenceWidget",
|
||||||
|
];
|
||||||
|
const progressWidgets = ["CompetenceWidget", "CompetenceCertificateWidget"];
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
courseConfig: DashboardCourseConfigType | undefined;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const courseSlug = computed(() => props.courseConfig?.course_slug ?? "");
|
||||||
|
const courseName = computed(() => props.courseConfig?.course_title ?? "");
|
||||||
|
const numberOfMentorWidgets = computed(() => {
|
||||||
|
return (
|
||||||
|
props.courseConfig?.widgets?.filter((widget) => mentorWidgets.includes(widget))
|
||||||
|
.length ?? 0
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const numberOfProgressWidgets = computed(() => {
|
||||||
|
return (
|
||||||
|
props.courseConfig?.widgets?.filter((widget) => progressWidgets.includes(widget))
|
||||||
|
.length ?? 0
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function hasWidget(widget: WidgetType) {
|
||||||
|
return props.courseConfig?.widgets?.includes(widget) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionButtonProps = computed<{ href: string; text: string; cyKey: string }>(
|
||||||
|
() => {
|
||||||
|
if (props.courseConfig?.role_key === "Member") {
|
||||||
|
return {
|
||||||
|
href: getLearningPathUrl(props.courseConfig?.course_slug),
|
||||||
|
text: "Weiter lernen",
|
||||||
|
cyKey: "progress-dashboard-continue-course-link",
|
||||||
|
};
|
||||||
|
} else if (props.courseConfig?.role_key === "Expert") {
|
||||||
|
return {
|
||||||
|
href: getCockpitUrl(props.courseConfig?.course_slug),
|
||||||
|
text: "Cockpit anschauen",
|
||||||
|
cyKey: "cockpit-dashboard-link",
|
||||||
|
};
|
||||||
|
} else if (props.courseConfig?.role_key === "Supervisor") {
|
||||||
|
return {
|
||||||
|
href: getCockpitUrl(props.courseConfig?.course_slug),
|
||||||
|
text: "Cockpit anschauen",
|
||||||
|
cyKey: "cockpit-dashboard-link",
|
||||||
|
};
|
||||||
|
} else if (props.courseConfig?.role_key === "MentorVV") {
|
||||||
|
return {
|
||||||
|
href: getLearningMentorUrl(props.courseConfig?.course_slug),
|
||||||
|
text: "a.Übersicht anschauen",
|
||||||
|
cyKey: "lm-dashboard-link",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
href: getLearningPathUrl(props.courseConfig?.course_slug),
|
||||||
|
text: "Weiter lernen",
|
||||||
|
cyKey: "progress-dashboard-continue-course-link",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function hasActionButton(): boolean {
|
||||||
|
return props.courseConfig?.role_key !== "MentorUK";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="courseConfig" class="mb-14 space-y-8">
|
||||||
|
<div class="flex flex-col space-y-8 bg-white p-6">
|
||||||
|
<div class="border-b border-gray-300 pb-8">
|
||||||
|
<div class="flex flex-row items-start justify-between">
|
||||||
|
<h3 class="mb-4 text-3xl" data-cy="db-course-title">{{ courseName }}</h3>
|
||||||
|
<a
|
||||||
|
v-if="hasActionButton()"
|
||||||
|
:href="actionButtonProps.href"
|
||||||
|
class="btn-blue"
|
||||||
|
:data-cy="actionButtonProps.cyKey"
|
||||||
|
>
|
||||||
|
{{ $t(actionButtonProps.text) }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
<span class="rounded bg-gray-300 px-2 py-1">
|
||||||
|
{{ $t(courseConfig.role_key) }}
|
||||||
|
</span>
|
||||||
|
<router-link
|
||||||
|
v-if="courseConfig.has_preview"
|
||||||
|
:to="getLearningPathUrl(courseConfig.course_slug)"
|
||||||
|
class="inline-block pl-6"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span>{{ $t("a.VorschauTeilnehmer") }}</span>
|
||||||
|
<it-icon-external-link class="ml-1 !h-4 !w-4" />
|
||||||
|
</div>
|
||||||
|
</router-link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
hasWidget('ProgressWidget') &&
|
||||||
|
courseConfig.session_to_continue_id &&
|
||||||
|
courseSlug
|
||||||
|
"
|
||||||
|
class="border-b border-gray-300 pb-8 last:border-0"
|
||||||
|
>
|
||||||
|
<LearningPathDiagram
|
||||||
|
:key="courseSlug"
|
||||||
|
:course-slug="courseSlug"
|
||||||
|
:course-session-id="courseConfig.session_to_continue_id"
|
||||||
|
diagram-type="horizontal"
|
||||||
|
></LearningPathDiagram>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="numberOfProgressWidgets"
|
||||||
|
class="flex flex-col flex-wrap gap-x-[60px] border-b border-gray-300 pb-8 last:border-0 md:flex-row"
|
||||||
|
>
|
||||||
|
<AssignmentSummary
|
||||||
|
v-if="hasWidget('CompetenceCertificateWidget')"
|
||||||
|
:course-slug="courseSlug"
|
||||||
|
:session-to-continue-id="courseConfig.session_to_continue_id"
|
||||||
|
:course-id="courseConfig.course_id"
|
||||||
|
/>
|
||||||
|
<CompetenceSummary
|
||||||
|
v-if="hasWidget('CompetenceWidget')"
|
||||||
|
:course-slug="courseSlug"
|
||||||
|
:session-to-continue-id="courseConfig.session_to_continue_id"
|
||||||
|
:course-id="courseConfig.course_id"
|
||||||
|
></CompetenceSummary>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="hasWidget('UKStatisticsWidget')"
|
||||||
|
class="flex flex-col flex-wrap gap-x-[60px] border-b border-gray-300 pb-8 last:border-0 md:flex-row"
|
||||||
|
>
|
||||||
|
<UkStatistics :course-slug="courseSlug" :course-id="courseConfig.course_id" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="numberOfMentorWidgets > 0"
|
||||||
|
class="flex flex-col flex-wrap items-stretch md:flex-row"
|
||||||
|
>
|
||||||
|
<MentorMenteeCount
|
||||||
|
v-if="hasWidget('MentorPersonWidget')"
|
||||||
|
:course-id="courseConfig.course_id"
|
||||||
|
:course-slug="courseConfig?.course_slug"
|
||||||
|
/>
|
||||||
|
<MentorOpenTasksCount
|
||||||
|
v-if="hasWidget('MentorTasksWidget')"
|
||||||
|
:course-id="courseConfig.course_id"
|
||||||
|
:course-slug="courseSlug"
|
||||||
|
/>
|
||||||
|
<MentorCompetenceSummary
|
||||||
|
v-if="hasWidget('MentorCompetenceWidget')"
|
||||||
|
:course-id="courseConfig.course_id"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -7,6 +7,7 @@ const props = defineProps<{
|
||||||
feedbackCount: number;
|
feedbackCount: number;
|
||||||
statisfactionMax: number;
|
statisfactionMax: number;
|
||||||
statisfactionAvg: number;
|
statisfactionAvg: number;
|
||||||
|
courseSlug: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const satisfactionColor = computed(() => {
|
const satisfactionColor = computed(() => {
|
||||||
|
|
@ -15,7 +16,10 @@ const satisfactionColor = computed(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BaseBox :details-link="'/statistic/feedback'" data-cy="dashboard.stats.feedback">
|
<BaseBox
|
||||||
|
:details-link="`/statistic/${courseSlug}/feedback`"
|
||||||
|
data-cy="dashboard.stats.feedback"
|
||||||
|
>
|
||||||
<template #title>{{ $t("a.Feedback Teilnehmer") }}</template>
|
<template #title>{{ $t("a.Feedback Teilnehmer") }}</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Ref } from "vue";
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
|
import { fetchMentorCompetenceSummary } from "@/services/dashboard";
|
||||||
|
import type { AssignmentsStatisticsType } from "@/gql/graphql";
|
||||||
|
import BaseBox from "@/components/dashboard/BaseBox.vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
courseId: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const summary: Ref<AssignmentsStatisticsType | null> = ref(null);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
summary.value = await fetchMentorCompetenceSummary(props.courseId);
|
||||||
|
console.log(summary.value);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="summary" class="w-[325px]">
|
||||||
|
<BaseBox
|
||||||
|
:details-link="`/dashboard/persons-competence?course=${props.courseId}`"
|
||||||
|
data-cy="dashboard.mentor.competenceSummary"
|
||||||
|
>
|
||||||
|
<template #title>{{ $t("Kompetenznachweise") }}</template>
|
||||||
|
<template #content>
|
||||||
|
<div class="flex flex-row space-x-3 bg-white">
|
||||||
|
<div
|
||||||
|
class="flex h-[47px] items-center justify-center py-1 pr-3 text-3xl font-bold"
|
||||||
|
>
|
||||||
|
<span>{{ summary.summary.total_passed }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="ml-3 mt-0 leading-[47px]">{{ $t("Bestanden") }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row space-x-3 bg-white pb-6">
|
||||||
|
<div
|
||||||
|
class="flex h-[47px] items-center justify-center py-1 pr-3 text-3xl font-bold"
|
||||||
|
>
|
||||||
|
<span>{{ summary.summary.total_failed }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="ml-3 mt-0 leading-[47px]">{{ $t("Nicht bestanden") }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</BaseBox>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Ref } from "vue";
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
|
import { fetchMenteeCount } from "@/services/dashboard";
|
||||||
|
import BaseBox from "@/components/dashboard/BaseBox.vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
courseId: string;
|
||||||
|
courseSlug: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const menteeCount: Ref<number> = ref(0);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const data = await fetchMenteeCount(props.courseId);
|
||||||
|
menteeCount.value = data?.mentee_count;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-[325px]">
|
||||||
|
<BaseBox
|
||||||
|
:details-link="`/dashboard/persons?course=${props.courseId}`"
|
||||||
|
data-cy="dashboard.mentor.competenceSummary"
|
||||||
|
>
|
||||||
|
<template #title>{{ $t("a.Personen") }}</template>
|
||||||
|
<template #content>
|
||||||
|
<div class="flex flex-row space-x-3 bg-white pb-6">
|
||||||
|
<div
|
||||||
|
class="flex h-[74px] items-center justify-center py-1 pr-3 text-3xl font-bold"
|
||||||
|
>
|
||||||
|
<span>{{ menteeCount }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="ml-3 mt-0 leading-[74px]">
|
||||||
|
{{ $t("a.Personen, die du begleitest") }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</BaseBox>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Ref } from "vue";
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
|
import { fetchOpenTasksCount } from "@/services/dashboard";
|
||||||
|
import BaseBox from "@/components/dashboard/BaseBox.vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
courseId: string;
|
||||||
|
courseSlug: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const openTaskCount: Ref<number> = ref(0);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const data = await fetchOpenTasksCount(props.courseId);
|
||||||
|
openTaskCount.value = data?.open_task_count;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-[325px]">
|
||||||
|
<BaseBox
|
||||||
|
:details-link="`/course/${props.courseSlug}/learning-mentor/tasks`"
|
||||||
|
data-cy="dashboard.mentor.competenceSummary"
|
||||||
|
>
|
||||||
|
<template #title>{{ $t("Zu erledigen") }}</template>
|
||||||
|
<template #content>
|
||||||
|
<div class="flex flex-row space-x-3 bg-white pb-6">
|
||||||
|
<div
|
||||||
|
class="flex h-[74px] w-[74px] items-center justify-center rounded-full border-2 border-green-500 px-3 py-1 text-3xl font-bold"
|
||||||
|
>
|
||||||
|
<span>{{ openTaskCount }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="ml-3 mt-0 leading-[74px]">{{ $t("Elemente zu erledigen") }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</BaseBox>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import DueDatesList from "@/components/dueDates/DueDatesList.vue";
|
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
|
||||||
|
|
||||||
const courseSessionsStore = useCourseSessionsStore();
|
|
||||||
const allDueDates = courseSessionsStore.allDueDates();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<h4 class="mb-6 text-xl font-bold">{{ $t("a.AlleTermine") }}</h4>
|
|
||||||
<DueDatesList
|
|
||||||
:due-dates="allDueDates"
|
|
||||||
:max-count="13"
|
|
||||||
:show-top-border="true"
|
|
||||||
:show-all-due-dates-link="true"
|
|
||||||
:show-bottom-border="true"
|
|
||||||
:show-course-session="true"
|
|
||||||
></DueDatesList>
|
|
||||||
</template>
|
|
||||||
|
|
@ -1,65 +1,97 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useDashboardStore } from "@/stores/dashboard";
|
import { useDashboardStore } from "@/stores/dashboard";
|
||||||
import { computed } from "vue";
|
import { computed, onMounted, ref } from "vue";
|
||||||
import CourseStatistics from "@/components/dashboard/CourseStatistics.vue";
|
|
||||||
import AttendanceSummaryBox from "@/components/dashboard/AttendanceSummaryBox.vue";
|
import AttendanceSummaryBox from "@/components/dashboard/AttendanceSummaryBox.vue";
|
||||||
import type { CourseStatisticsType } from "@/gql/graphql";
|
import type { CourseStatisticsType } from "@/gql/graphql";
|
||||||
import AssignmentSummaryBox from "@/components/dashboard/AssignmentSummaryBox.vue";
|
import AssignmentSummaryBox from "@/components/dashboard/AssignmentSummaryBox.vue";
|
||||||
import FeedbackSummaryBox from "@/components/dashboard/FeedbackSummaryBox.vue";
|
import FeedbackSummaryBox from "@/components/dashboard/FeedbackSummaryBox.vue";
|
||||||
import CompetenceSummaryBox from "@/components/dashboard/CompetenceSummaryBox.vue";
|
import CompetenceSummaryBox from "@/components/dashboard/CompetenceSummaryBox.vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
courseId: string;
|
||||||
|
courseSlug: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const statistics = ref<CourseStatisticsType | null>(null);
|
||||||
|
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
|
|
||||||
const statistics = computed(() => {
|
|
||||||
return dashboardStore.currentDashBoardData as CourseStatisticsType;
|
|
||||||
});
|
|
||||||
|
|
||||||
const courseSessionSelectionMetrics = computed(() => {
|
|
||||||
return statistics.value.course_session_selection_metrics;
|
|
||||||
});
|
|
||||||
|
|
||||||
const attendanceDayPresences = computed(() => {
|
const attendanceDayPresences = computed(() => {
|
||||||
return statistics.value.attendance_day_presences.summary;
|
return (
|
||||||
|
statistics?.value?.attendance_day_presences?.summary ?? {
|
||||||
|
days_completed: 0,
|
||||||
|
participants_present: 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const assigmentSummary = computed(() => {
|
const assigmentSummary = computed(() => {
|
||||||
return statistics.value.assignments.summary;
|
return (
|
||||||
|
statistics?.value?.assignments.summary ?? {
|
||||||
|
average_passed: 0,
|
||||||
|
completed_count: 0,
|
||||||
|
total_passed: 0,
|
||||||
|
total_failed: 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const competenceSummary = computed(() => {
|
const competenceSummary = computed(() => {
|
||||||
return statistics.value.competences.summary;
|
return (
|
||||||
|
statistics?.value?.competences.summary ?? {
|
||||||
|
fail_total: 0,
|
||||||
|
success_total: 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const feebackSummary = computed(() => {
|
const feebackSummary = computed(() => {
|
||||||
return statistics.value.feedback_responses.summary;
|
return (
|
||||||
|
statistics?.value?.feedback_responses.summary ?? {
|
||||||
|
satisfaction_average: 0,
|
||||||
|
satisfaction_max: 0,
|
||||||
|
total_responses: 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
statistics.value = await dashboardStore.loadStatisticsDatav2(props.courseId);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="statistics" class="mb-14 space-y-8">
|
<div v-if="statistics" class="space-y-8">
|
||||||
<CourseStatistics
|
<div
|
||||||
:session-count="courseSessionSelectionMetrics.session_count"
|
class="flex flex-col flex-wrap justify-between gap-x-5 border-b border-gray-300 pb-8 last:border-0 md:flex-row"
|
||||||
:participant-count="courseSessionSelectionMetrics.participant_count"
|
>
|
||||||
:expert-count="courseSessionSelectionMetrics.expert_count"
|
|
||||||
/>
|
|
||||||
<div class="grid auto-rows-fr grid-cols-1 gap-8 xl:grid-cols-2">
|
|
||||||
<AttendanceSummaryBox
|
<AttendanceSummaryBox
|
||||||
|
class="flex-grow"
|
||||||
:days-completed="attendanceDayPresences.days_completed"
|
:days-completed="attendanceDayPresences.days_completed"
|
||||||
:avg-participants-present="attendanceDayPresences.participants_present"
|
:avg-participants-present="attendanceDayPresences.participants_present"
|
||||||
|
:course-slug="props.courseSlug"
|
||||||
/>
|
/>
|
||||||
<AssignmentSummaryBox
|
<AssignmentSummaryBox
|
||||||
|
class="flex-grow"
|
||||||
:assignments-completed="assigmentSummary.completed_count"
|
:assignments-completed="assigmentSummary.completed_count"
|
||||||
:avg-passed="assigmentSummary.average_passed"
|
:avg-passed="assigmentSummary.average_passed"
|
||||||
|
:course-slug="props.courseSlug"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="flex flex-col flex-wrap gap-x-5 border-b border-gray-300 align-top last:border-0 md:flex-row"
|
||||||
|
>
|
||||||
<FeedbackSummaryBox
|
<FeedbackSummaryBox
|
||||||
:feedback-count="feebackSummary.total_responses"
|
:feedback-count="feebackSummary.total_responses"
|
||||||
:statisfaction-max="feebackSummary.satisfaction_max"
|
:statisfaction-max="feebackSummary.satisfaction_max"
|
||||||
:statisfaction-avg="feebackSummary.satisfaction_average"
|
:statisfaction-avg="feebackSummary.satisfaction_average"
|
||||||
|
:course-slug="props.courseSlug"
|
||||||
/>
|
/>
|
||||||
<CompetenceSummaryBox
|
<CompetenceSummaryBox
|
||||||
:fail-count="competenceSummary.fail_total"
|
:fail-count="competenceSummary.fail_total"
|
||||||
:success-count="competenceSummary.success_total"
|
:success-count="competenceSummary.success_total"
|
||||||
details-link="/statistic/competence"
|
:details-link="`/statistic/${courseSlug}/competence`"
|
||||||
|
:course-slug="props.courseSlug"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useDashboardPersonsDueDates } from "@/composables";
|
||||||
|
import { computed } from "vue";
|
||||||
|
import _ from "lodash";
|
||||||
|
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
||||||
|
import DueDateSingle from "@/components/dueDates/DueDateSingle.vue";
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
courseSessionId: string;
|
||||||
|
circleId?: string;
|
||||||
|
maxCount?: number;
|
||||||
|
showAllButton?: boolean;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
maxCount: 3,
|
||||||
|
circleId: undefined,
|
||||||
|
showAllButton: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const { loading, currentDueDates } = useDashboardPersonsDueDates();
|
||||||
|
|
||||||
|
const filteredDueDates = computed(() => {
|
||||||
|
let dueDates = currentDueDates.value.filter(
|
||||||
|
(dueDate) => dueDate.course_session_id === props.courseSessionId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (props.circleId) {
|
||||||
|
dueDates = dueDates.filter((dueDate) => dueDate.circle?.id === props.circleId);
|
||||||
|
}
|
||||||
|
return _.take(dueDates, props.maxCount);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="loading" class="m-8 flex justify-center">
|
||||||
|
<LoadingSpinner />
|
||||||
|
</div>
|
||||||
|
<div v-else class="flex flex-col space-y-2">
|
||||||
|
<h3 class="heading-3">{{ $t("Nächste Termine") }}</h3>
|
||||||
|
<div
|
||||||
|
v-for="dueDate in filteredDueDates"
|
||||||
|
:key="dueDate.id"
|
||||||
|
class="border-t border-gray-500 pt-2"
|
||||||
|
>
|
||||||
|
<DueDateSingle :due-date="dueDate"></DueDateSingle>
|
||||||
|
</div>
|
||||||
|
<div v-if="filteredDueDates.length === 0">
|
||||||
|
{{ $t("dueDates.noDueDatesAvailable") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<router-link
|
||||||
|
v-if="showAllButton"
|
||||||
|
class="btn-secondary mt-4"
|
||||||
|
:to="`/dashboard/due-dates?session=${courseSessionId}`"
|
||||||
|
>
|
||||||
|
{{ $t("a.Alle Termine anzeigen") }}
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
|
@ -1,41 +1,46 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { CourseSession, DueDate } from "@/types";
|
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
|
||||||
import { useTranslation } from "i18next-vue";
|
import { useTranslation } from "i18next-vue";
|
||||||
import dayjs from "dayjs";
|
import type { DashboardDueDate } from "@/services/dashboard";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
dueDate: DueDate;
|
dueDate: DashboardDueDate;
|
||||||
singleLine?: boolean;
|
singleLine?: boolean;
|
||||||
showCourseSession?: boolean;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dateType = t(props.dueDate.date_type_translation_key);
|
const dateType = t(props.dueDate.date_type_translation_key);
|
||||||
const assignmentType = t(props.dueDate.assignment_type_translation_key);
|
const assignmentType = t(props.dueDate.assignment_type_translation_key);
|
||||||
|
|
||||||
const courseSessionsStore = useCourseSessionsStore();
|
const urlText = computed(() => {
|
||||||
const courseSession = courseSessionsStore.allCourseSessions.find(
|
let result = "";
|
||||||
(cs: CourseSession) => cs.id === props.dueDate.course_session_id
|
if (dateType) {
|
||||||
);
|
result += dateType;
|
||||||
|
|
||||||
if (!courseSession) {
|
|
||||||
throw new Error("Course session not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = courseSession.actions.includes("expert-cockpit")
|
|
||||||
? props.dueDate.url_expert
|
|
||||||
: props.dueDate.url;
|
|
||||||
|
|
||||||
const courseSessionTitle = computed(() => {
|
|
||||||
if (props.dueDate.course_session_id) {
|
|
||||||
return (
|
|
||||||
courseSessionsStore.getCourseSessionById(props.dueDate.course_session_id)
|
|
||||||
?.title ?? ""
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return "";
|
|
||||||
|
if (assignmentType && !props.dueDate.title.startsWith(assignmentType)) {
|
||||||
|
result += " " + assignmentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.dueDate.title) {
|
||||||
|
result += " " + props.dueDate.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.trim();
|
||||||
|
});
|
||||||
|
|
||||||
|
const showAsUrl = computed(() => {
|
||||||
|
return ["SUPERVISOR", "EXPERT", "MEMBER"].includes(
|
||||||
|
props.dueDate.course_session.my_role
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = computed(() => {
|
||||||
|
if (["SUPERVISOR", "EXPERT"].includes(props.dueDate.course_session.my_role)) {
|
||||||
|
return props.dueDate.url_expert;
|
||||||
|
}
|
||||||
|
return props.dueDate.url;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -46,34 +51,42 @@ const courseSessionTitle = computed(() => {
|
||||||
>
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<div>
|
<div>
|
||||||
<a class="underline" :href="url">
|
<a v-if="showAsUrl" :href="url">
|
||||||
<span class="text-bold">
|
<span class="text-bold text-gray-900">
|
||||||
{{ dayjs(props.dueDate.start).format("D. MMMM YYYY") }}:
|
{{ dayjs(props.dueDate.start).format("dddd D. MMMM YYYY") }}
|
||||||
<template v-if="dateType">
|
|
||||||
{{ dateType }}
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
{{ assignmentType }}
|
|
||||||
</template>
|
|
||||||
{{ " " }}
|
|
||||||
</span>
|
</span>
|
||||||
<template v-if="assignmentType && dateType">
|
|
||||||
{{ assignmentType }}:
|
|
||||||
{{ props.dueDate.title }}
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
{{ props.dueDate.title }}
|
|
||||||
</template>
|
|
||||||
</a>
|
</a>
|
||||||
|
<span v-else class="text-bold text-gray-900">
|
||||||
|
{{ dayjs(props.dueDate.start).format("dddd D. MMMM YYYY") }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a v-if="showAsUrl" class="underline" :href="url">
|
||||||
|
<span class="text-bold">
|
||||||
|
{{ urlText }}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<span v-else class="text-bold">
|
||||||
|
{{ urlText }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-small text-gray-900">
|
<div class="text-small text-gray-900">
|
||||||
<div>
|
<div>
|
||||||
<span v-if="props.showCourseSession ?? courseSessionTitle">
|
<span v-if="props.dueDate.course_session.is_uk">
|
||||||
{{ courseSessionTitle }}:
|
{{ props.dueDate.course_session.session_title }}:
|
||||||
</span>
|
</span>
|
||||||
{{ $t("a.Circle") }} «{{ props.dueDate.circle?.title }}»
|
{{ $t("a.Circle") }} «{{ props.dueDate.circle?.title }}»
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="props.dueDate.persons?.length" class="flex gap-2">
|
||||||
|
<div v-for="person in props.dueDate.persons" :key="person.user_id">
|
||||||
|
<img
|
||||||
|
class="inline-block h-11 w-11 rounded-full"
|
||||||
|
:src="person.avatar_url_small || '/static/avatars/myvbv-default-avatar.png'"
|
||||||
|
:alt="`${person.first_name} ${person.last_name}`"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
<script lang="ts" setup>
|
|
||||||
import DueDateSingle from "@/components/dueDates/DueDateSingle.vue";
|
|
||||||
import type { DueDate } from "@/types";
|
|
||||||
import { computed } from "vue";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
maxCount: number;
|
|
||||||
dueDates: DueDate[];
|
|
||||||
showTopBorder: boolean;
|
|
||||||
showBottomBorder: boolean;
|
|
||||||
showAllDueDatesLink: boolean;
|
|
||||||
showCourseSession: boolean;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const allDueDates = computed(() => {
|
|
||||||
return props.dueDates;
|
|
||||||
});
|
|
||||||
|
|
||||||
const dueDatesDisplayed = computed(() => {
|
|
||||||
return props.dueDates.slice(0, props.maxCount);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<ul :class="showBottomBorder ? '' : 'no-border-last'">
|
|
||||||
<li
|
|
||||||
v-for="dueDate in dueDatesDisplayed"
|
|
||||||
:key="dueDate.id"
|
|
||||||
class="cy-single-due-date"
|
|
||||||
:class="{ 'first:border-t': props.showTopBorder, 'border-b': true }"
|
|
||||||
>
|
|
||||||
<DueDateSingle
|
|
||||||
:due-date="dueDate"
|
|
||||||
:show-course-session="props.showCourseSession"
|
|
||||||
></DueDateSingle>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div v-if="allDueDates.length === 0">{{ $t("dueDates.noDueDatesAvailable") }}</div>
|
|
||||||
<div
|
|
||||||
v-if="showAllDueDatesLink && allDueDates.length > 0"
|
|
||||||
class="flex items-center pt-6"
|
|
||||||
>
|
|
||||||
<a href="/appointments">{{ $t("dueDates.showAllDueDates") }}</a>
|
|
||||||
<it-icon-arrow-right />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="postcss" scoped>
|
|
||||||
.no-border-last li:last-child {
|
|
||||||
border-bottom: none !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<DueDatesList
|
|
||||||
:due-dates="allDueDates"
|
|
||||||
:max-count="props.maxCount"
|
|
||||||
:show-top-border="props.showTopBorder"
|
|
||||||
show-all-due-dates-link
|
|
||||||
show-bottom-border
|
|
||||||
:show-course-session="false"
|
|
||||||
></DueDatesList>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import DueDatesList from "@/components/dueDates/DueDatesList.vue";
|
|
||||||
import { useCurrentCourseSession } from "@/composables";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
maxCount: number;
|
|
||||||
showTopBorder: boolean;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const courseSession = useCurrentCourseSession();
|
|
||||||
const allDueDates = courseSession.value.due_dates;
|
|
||||||
</script>
|
|
||||||
|
|
@ -22,6 +22,7 @@ import {
|
||||||
getLearningPathUrl,
|
getLearningPathUrl,
|
||||||
getMediaCenterUrl,
|
getMediaCenterUrl,
|
||||||
} from "@/utils/utils";
|
} from "@/utils/utils";
|
||||||
|
import { useMentorTexts } from "@/composables";
|
||||||
|
|
||||||
log.debug("MainNavigationBar created");
|
log.debug("MainNavigationBar created");
|
||||||
|
|
||||||
|
|
@ -59,9 +60,9 @@ const selectedCourseSessionTitle = computed(() => {
|
||||||
const appointmentsUrl = computed(() => {
|
const appointmentsUrl = computed(() => {
|
||||||
const currentCourseSession = courseSessionsStore.currentCourseSession;
|
const currentCourseSession = courseSessionsStore.currentCourseSession;
|
||||||
if (currentCourseSession) {
|
if (currentCourseSession) {
|
||||||
return `/course/${currentCourseSession.course.slug}/appointments`;
|
return `/dashboard/due-dates?session=${currentCourseSession.id}`;
|
||||||
} else {
|
} else {
|
||||||
return `/appointments`;
|
return `/dashboard/due-dates`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -121,6 +122,8 @@ const hasLearningMentor = computed(() => {
|
||||||
const courseSession = courseSessionsStore.currentCourseSession;
|
const courseSession = courseSessionsStore.currentCourseSession;
|
||||||
return courseSession.actions.includes("learning-mentor");
|
return courseSession.actions.includes("learning-mentor");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mentorTabTitle = useMentorTexts().mentorTabTitle;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -265,7 +268,7 @@ const hasLearningMentor = computed(() => {
|
||||||
class="nav-item"
|
class="nav-item"
|
||||||
:class="{ 'nav-item--active': inLearningMentor() }"
|
:class="{ 'nav-item--active': inLearningMentor() }"
|
||||||
>
|
>
|
||||||
{{ t("a.Lernbegleitung") }}
|
{{ t(mentorTabTitle) }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import {
|
||||||
getLearningPathUrl,
|
getLearningPathUrl,
|
||||||
getMediaCenterUrl,
|
getMediaCenterUrl,
|
||||||
} from "@/utils/utils";
|
} from "@/utils/utils";
|
||||||
|
import { useMentorTexts } from "@/composables";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
|
@ -36,6 +37,8 @@ const clickLink = (to: string | undefined) => {
|
||||||
emit("closemodal");
|
emit("closemodal");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mentorTabTitle = useMentorTexts().mentorTabTitle;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -97,7 +100,7 @@ const clickLink = (to: string | undefined) => {
|
||||||
data-cy="navigation-mobile-mentor-link"
|
data-cy="navigation-mobile-mentor-link"
|
||||||
@click="clickLink(getLearningMentorUrl(courseSession.course.slug))"
|
@click="clickLink(getLearningMentorUrl(courseSession.course.slug))"
|
||||||
>
|
>
|
||||||
{{ $t("a.Lernbegleitung") }}
|
{{ $t(mentorTabTitle) }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useCurrentCourseSession } from "@/composables";
|
import { useCurrentCourseSession, useMentorTexts } from "@/composables";
|
||||||
import ItModal from "@/components/ui/ItModal.vue";
|
import ItModal from "@/components/ui/ItModal.vue";
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import { useCSRFFetch } from "@/fetchHelpers";
|
import { useCSRFFetch } from "@/fetchHelpers";
|
||||||
|
|
@ -68,19 +68,22 @@ const inviteMentor = async () => {
|
||||||
showInvitationModal.value = false;
|
showInvitationModal.value = false;
|
||||||
inviteeEmail.value = "";
|
inviteeEmail.value = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { myLearningMentors, inviteLearningMentor, noLearningMentors } = useMentorTexts();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="!isLoading" class="bg-gray-200">
|
<div v-if="!isLoading" class="bg-gray-200">
|
||||||
<div class="flex flex-row items-center justify-between py-6">
|
<div class="flex flex-row items-center justify-between py-6">
|
||||||
<h2 class="heading-2">{{ $t("a.Meine Lernbegleitung") }}</h2>
|
<h2 data-cy="lm-my-lms-title" class="heading-2">{{ $t(myLearningMentors) }}</h2>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
class="btn-secondary flex items-center"
|
class="btn-secondary flex items-center"
|
||||||
|
data-cy="lm-invite-mentor-button"
|
||||||
@click="showInvitationModal = true"
|
@click="showInvitationModal = true"
|
||||||
>
|
>
|
||||||
<it-icon-add class="it-icon mr-2 h-6 w-6" />
|
<it-icon-add class="it-icon mr-2 h-6 w-6" />
|
||||||
{{ $t("a.Neue Lernbegleitung einladen") }}
|
{{ $t(inviteLearningMentor) }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -143,16 +146,14 @@ const inviteMentor = async () => {
|
||||||
<div class="j mx-1 my-3 flex w-fit items-center space-x-1 bg-sky-200 p-4">
|
<div class="j mx-1 my-3 flex w-fit items-center space-x-1 bg-sky-200 p-4">
|
||||||
<it-icon-info class="it-icon mr-2 h-6 w-6 text-sky-700" />
|
<it-icon-info class="it-icon mr-2 h-6 w-6 text-sky-700" />
|
||||||
<span>
|
<span>
|
||||||
{{
|
{{ $t(noLearningMentors) }}
|
||||||
$t("a.Aktuell hast du noch keine Person als Lernbegleitung eingeladen.")
|
|
||||||
}}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
<ItModal v-model="showInvitationModal">
|
<ItModal v-model="showInvitationModal">
|
||||||
<template #title>{{ $t("a.Neue Lernbegleitung einladen") }}</template>
|
<template #title>{{ $t(inviteLearningMentor) }}</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<label for="mentor-email">{{ $t("a.E-Mail Adresse") }}</label>
|
<label for="mentor-email">{{ $t("a.E-Mail Adresse") }}</label>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useCurrentCourseSession } from "@/composables";
|
import { useCurrentCourseSession, useMentorTexts } from "@/composables";
|
||||||
|
|
||||||
const currentCourseSession = useCurrentCourseSession();
|
const currentCourseSession = useCurrentCourseSession();
|
||||||
|
const { actionNoLearningMentors, inviteLearningMentorShort } = useMentorTexts();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -9,11 +10,7 @@ const currentCourseSession = useCurrentCourseSession();
|
||||||
<it-icon-info class="it-icon h-6 w-6 text-sky-700" />
|
<it-icon-info class="it-icon h-6 w-6 text-sky-700" />
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
{{
|
{{ $t(actionNoLearningMentors) }}
|
||||||
$t(
|
|
||||||
"a.Aktuell hast du noch keine Person als Lernbegleitung eingeladen. Lade jetzt jemanden ein."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
<router-link
|
<router-link
|
||||||
:to="{
|
:to="{
|
||||||
|
|
@ -22,7 +19,7 @@ const currentCourseSession = useCurrentCourseSession();
|
||||||
}"
|
}"
|
||||||
class="btn-blue px-4 py-2 font-bold"
|
class="btn-blue px-4 py-2 font-bold"
|
||||||
>
|
>
|
||||||
{{ $t("a.Lernbegleitung einladen") }}
|
{{ $t(inviteLearningMentorShort) }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import LearningPathCircle from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
|
import LearningPathCircle from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
|
||||||
import { calculateCircleSectorData } from "@/pages/learningPath/learningPathPage/utils";
|
import { calculateCircleSectorData } from "@/pages/learningPath/learningPathPage/utils";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { useCourseDataWithCompletion } from "@/composables";
|
import { useCourseCircleProgress, useCourseDataWithCompletion } from "@/composables";
|
||||||
|
|
||||||
export type DiagramType = "horizontal" | "horizontalSmall" | "singleSmall";
|
export type DiagramType = "horizontal" | "horizontalSmall" | "singleSmall";
|
||||||
|
|
||||||
|
|
@ -46,14 +46,31 @@ const wrapperClasses = computed(() => {
|
||||||
}
|
}
|
||||||
return classes;
|
return classes;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { inProgressCirclesCount, circlesCount } = useCourseCircleProgress(
|
||||||
|
lpQueryResult.circles
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClasses">
|
<div>
|
||||||
<LearningPathCircle
|
<h4
|
||||||
v-for="circle in circles"
|
v-if="diagramType === 'horizontal' && circles.length > 0"
|
||||||
:key="circle.id"
|
class="mb-4 font-bold"
|
||||||
:sectors="calculateCircleSectorData(circle)"
|
>
|
||||||
></LearningPathCircle>
|
{{
|
||||||
|
$t("learningPathPage.progressText", {
|
||||||
|
inProgressCount: inProgressCirclesCount,
|
||||||
|
allCount: circlesCount,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</h4>
|
||||||
|
<div :class="wrapperClasses">
|
||||||
|
<LearningPathCircle
|
||||||
|
v-for="circle in circles"
|
||||||
|
:key="circle.id"
|
||||||
|
:sectors="calculateCircleSectorData(circle)"
|
||||||
|
></LearningPathCircle>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ const numFeedbacks = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const data = await itGet(
|
const data: { amount: number } = await itGet(
|
||||||
`/api/core/feedback/${props.courseSession.id}/${props.circleId}/`
|
`/api/core/feedback/${props.courseSession.id}/${props.circleId}/`
|
||||||
);
|
);
|
||||||
completeFeedbacks.value = data.amount;
|
completeFeedbacks.value = data.amount;
|
||||||
|
|
|
||||||
|
|
@ -28,104 +28,106 @@ const isLoaded = computed(() => !selfEvaluationFeedbackSummaries.loading.value);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<template v-if="isLoaded">
|
<div>
|
||||||
<!-- Self Evaluation -->
|
<template v-if="isLoaded">
|
||||||
<div class="bg-white px-8 py-4 lg:mb-8 lg:py-8">
|
<!-- Self Evaluation -->
|
||||||
<div class="mb-8">
|
<div class="bg-white px-8 py-4 lg:mb-8 lg:py-8">
|
||||||
<h3 class="mb-4 pb-4 lg:pb-0">
|
<div class="mb-8">
|
||||||
{{ $t("a.Selbsteinschätzungen") }}
|
<h3 class="mb-4 pb-4 lg:pb-0">
|
||||||
</h3>
|
{{ $t("a.Selbsteinschätzungen") }}
|
||||||
<ul
|
</h3>
|
||||||
class="mb-6 flex flex-col lg:flex-row lg:items-center lg:justify-between lg:gap-8"
|
<ul
|
||||||
|
class="mb-6 flex flex-col lg:flex-row lg:items-center lg:justify-between lg:gap-8"
|
||||||
|
>
|
||||||
|
<li class="mb-4 inline-block flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
||||||
|
<h5 class="mb-4 text-gray-700">«{{ $t("selfEvaluation.no") }}»</h5>
|
||||||
|
<div class="flex flex-row items-center">
|
||||||
|
<it-icon-smiley-thinking class="h-16 w-16"></it-icon-smiley-thinking>
|
||||||
|
<p
|
||||||
|
class="ml-4 inline-block text-7xl font-bold"
|
||||||
|
data-cy="self-evaluation-fail"
|
||||||
|
>
|
||||||
|
{{ selfAssessmentCounts?.fail }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="mb-4 inline-block flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
||||||
|
<h5 class="mb-4 text-gray-700">«{{ $t("selfEvaluation.yes") }}»</h5>
|
||||||
|
<div class="flex flex-row items-center">
|
||||||
|
<it-icon-smiley-happy class="h-16 w-16"></it-icon-smiley-happy>
|
||||||
|
<p
|
||||||
|
class="ml-4 inline-block text-7xl font-bold"
|
||||||
|
data-cy="self-evaluation-success"
|
||||||
|
>
|
||||||
|
{{ selfAssessmentCounts?.pass }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
||||||
|
<h5 class="mb-4 text-gray-700">{{ $t("competences.notAssessed") }}</h5>
|
||||||
|
<div class="flex flex-row items-center">
|
||||||
|
<it-icon-smiley-neutral class="h-16 w-16"></it-icon-smiley-neutral>
|
||||||
|
<p
|
||||||
|
class="ml-4 inline-block text-7xl font-bold"
|
||||||
|
data-cy="self-evaluation-unknown"
|
||||||
|
>
|
||||||
|
{{ selfAssessmentCounts?.unknown }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<!-- Feedback Evaluation -->
|
||||||
|
<div
|
||||||
|
v-if="courseSession.course.configuration.enable_learning_mentor"
|
||||||
|
class="border-t pt-8"
|
||||||
>
|
>
|
||||||
<li class="mb-4 inline-block flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
<h3 class="mb-4 pb-4 lg:pb-0">
|
||||||
<h5 class="mb-4 text-gray-700">«{{ $t("selfEvaluation.no") }}»</h5>
|
{{ $t("a.Fremdeinschätzungen") }}
|
||||||
<div class="flex flex-row items-center">
|
</h3>
|
||||||
<it-icon-smiley-thinking class="h-16 w-16"></it-icon-smiley-thinking>
|
<ul
|
||||||
<p
|
class="mb-6 flex flex-col lg:flex-row lg:items-center lg:justify-between lg:gap-8"
|
||||||
class="ml-4 inline-block text-7xl font-bold"
|
>
|
||||||
data-cy="self-evaluation-fail"
|
<li class="mb-4 inline-block flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
||||||
>
|
<h5 class="mb-4 text-gray-700">«{{ $t("receivedEvaluation.no") }}»</h5>
|
||||||
{{ selfAssessmentCounts?.fail }}
|
<div class="flex flex-row items-center">
|
||||||
</p>
|
<it-icon-smiley-thinking class="h-16 w-16"></it-icon-smiley-thinking>
|
||||||
</div>
|
<p class="ml-4 inline-block text-7xl font-bold">
|
||||||
</li>
|
{{ feedbackEvaluationCounts?.fail }}
|
||||||
<li class="mb-4 inline-block flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
</p>
|
||||||
<h5 class="mb-4 text-gray-700">«{{ $t("selfEvaluation.yes") }}»</h5>
|
</div>
|
||||||
<div class="flex flex-row items-center">
|
</li>
|
||||||
<it-icon-smiley-happy class="h-16 w-16"></it-icon-smiley-happy>
|
<li class="mb-4 inline-block flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
||||||
<p
|
<h5 class="mb-4 text-gray-700">«{{ $t("receivedEvaluation.yes") }}»</h5>
|
||||||
class="ml-4 inline-block text-7xl font-bold"
|
<div class="flex flex-row items-center">
|
||||||
data-cy="self-evaluation-success"
|
<it-icon-smiley-happy class="h-16 w-16"></it-icon-smiley-happy>
|
||||||
>
|
<p class="ml-4 inline-block text-7xl font-bold">
|
||||||
{{ selfAssessmentCounts?.pass }}
|
{{ feedbackEvaluationCounts?.pass }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
<li class="flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
||||||
<h5 class="mb-4 text-gray-700">{{ $t("competences.notAssessed") }}</h5>
|
<h5 class="mb-4 text-gray-700">{{ $t("competences.notAssessed") }}</h5>
|
||||||
<div class="flex flex-row items-center">
|
<div class="flex flex-row items-center">
|
||||||
<it-icon-smiley-neutral class="h-16 w-16"></it-icon-smiley-neutral>
|
<it-icon-smiley-neutral class="h-16 w-16"></it-icon-smiley-neutral>
|
||||||
<p
|
<p class="ml-4 inline-block text-7xl font-bold">
|
||||||
class="ml-4 inline-block text-7xl font-bold"
|
{{ feedbackEvaluationCounts?.unknown }}
|
||||||
data-cy="self-evaluation-unknown"
|
</p>
|
||||||
>
|
</div>
|
||||||
{{ selfAssessmentCounts?.unknown }}
|
</li>
|
||||||
</p>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
<!-- Show All (always)-->
|
||||||
</ul>
|
<button
|
||||||
</div>
|
class="btn-text inline-flex items-center py-2 pl-0 pt-8"
|
||||||
<!-- Feedback Evaluation -->
|
@click="emit('showAll')"
|
||||||
<div
|
|
||||||
v-if="courseSession.course.configuration.enable_learning_mentor"
|
|
||||||
class="border-t pt-8"
|
|
||||||
>
|
|
||||||
<h3 class="mb-4 pb-4 lg:pb-0">
|
|
||||||
{{ $t("a.Fremdeinschätzungen") }}
|
|
||||||
</h3>
|
|
||||||
<ul
|
|
||||||
class="mb-6 flex flex-col lg:flex-row lg:items-center lg:justify-between lg:gap-8"
|
|
||||||
>
|
>
|
||||||
<li class="mb-4 inline-block flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
<span>{{ $t("general.showAll") }}</span>
|
||||||
<h5 class="mb-4 text-gray-700">«{{ $t("receivedEvaluation.no") }}»</h5>
|
<it-icon-arrow-right></it-icon-arrow-right>
|
||||||
<div class="flex flex-row items-center">
|
</button>
|
||||||
<it-icon-smiley-thinking class="h-16 w-16"></it-icon-smiley-thinking>
|
|
||||||
<p class="ml-4 inline-block text-7xl font-bold">
|
|
||||||
{{ feedbackEvaluationCounts?.fail }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="mb-4 inline-block flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
|
||||||
<h5 class="mb-4 text-gray-700">«{{ $t("receivedEvaluation.yes") }}»</h5>
|
|
||||||
<div class="flex flex-row items-center">
|
|
||||||
<it-icon-smiley-happy class="h-16 w-16"></it-icon-smiley-happy>
|
|
||||||
<p class="ml-4 inline-block text-7xl font-bold">
|
|
||||||
{{ feedbackEvaluationCounts?.pass }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
|
||||||
<h5 class="mb-4 text-gray-700">{{ $t("competences.notAssessed") }}</h5>
|
|
||||||
<div class="flex flex-row items-center">
|
|
||||||
<it-icon-smiley-neutral class="h-16 w-16"></it-icon-smiley-neutral>
|
|
||||||
<p class="ml-4 inline-block text-7xl font-bold">
|
|
||||||
{{ feedbackEvaluationCounts?.unknown }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- Show All (always)-->
|
</template>
|
||||||
<button
|
</div>
|
||||||
class="btn-text inline-flex items-center py-2 pl-0 pt-8"
|
|
||||||
@click="emit('showAll')"
|
|
||||||
>
|
|
||||||
<span>{{ $t("general.showAll") }}</span>
|
|
||||||
<it-icon-arrow-right></it-icon-arrow-right>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,29 @@
|
||||||
import { useCSRFFetch } from "@/fetchHelpers";
|
import { useCSRFFetch } from "@/fetchHelpers";
|
||||||
import type { CourseStatisticsType } from "@/gql/graphql";
|
import type { CourseStatisticsType } from "@/gql/graphql";
|
||||||
import { graphqlClient } from "@/graphql/client";
|
import { graphqlClient } from "@/graphql/client";
|
||||||
import { COURSE_QUERY, COURSE_SESSION_DETAIL_QUERY } from "@/graphql/queries";
|
import {
|
||||||
|
COMPETENCE_NAVI_CERTIFICATE_FOR_USER_QUERY,
|
||||||
|
COMPETENCE_NAVI_CERTIFICATE_QUERY,
|
||||||
|
COURSE_QUERY,
|
||||||
|
COURSE_SESSION_DETAIL_QUERY,
|
||||||
|
} from "@/graphql/queries";
|
||||||
import {
|
import {
|
||||||
circleFlatChildren,
|
circleFlatChildren,
|
||||||
circleFlatLearningContents,
|
circleFlatLearningContents,
|
||||||
circleFlatLearningUnits,
|
circleFlatLearningUnits,
|
||||||
|
someFinishedInLearningSequence,
|
||||||
} from "@/services/circle";
|
} from "@/services/circle";
|
||||||
|
import type {
|
||||||
|
DashboardDueDate,
|
||||||
|
DashboardPersonRoleType,
|
||||||
|
DashboardPersonType,
|
||||||
|
} from "@/services/dashboard";
|
||||||
|
import {
|
||||||
|
courseIdForCourseSlug,
|
||||||
|
fetchDashboardDueDates,
|
||||||
|
fetchDashboardPersons,
|
||||||
|
fetchStatisticData,
|
||||||
|
} from "@/services/dashboard";
|
||||||
import { presignUpload, uploadFile } from "@/services/files";
|
import { presignUpload, uploadFile } from "@/services/files";
|
||||||
import { useCompletionStore } from "@/stores/completion";
|
import { useCompletionStore } from "@/stores/completion";
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||||
|
|
@ -14,11 +31,13 @@ import { useDashboardStore } from "@/stores/dashboard";
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
import type {
|
import type {
|
||||||
ActionCompetence,
|
ActionCompetence,
|
||||||
|
CircleType,
|
||||||
Course,
|
Course,
|
||||||
CourseCompletion,
|
CourseCompletion,
|
||||||
CourseCompletionStatus,
|
CourseCompletionStatus,
|
||||||
CourseSession,
|
CourseSession,
|
||||||
CourseSessionDetail,
|
CourseSessionDetail,
|
||||||
|
DashboardPersonsPageMode,
|
||||||
LearningContentWithCompletion,
|
LearningContentWithCompletion,
|
||||||
LearningMentor,
|
LearningMentor,
|
||||||
LearningPathType,
|
LearningPathType,
|
||||||
|
|
@ -26,9 +45,11 @@ import type {
|
||||||
PerformanceCriteria,
|
PerformanceCriteria,
|
||||||
} from "@/types";
|
} from "@/types";
|
||||||
import { useQuery } from "@urql/vue";
|
import { useQuery } from "@urql/vue";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { t } from "i18next";
|
||||||
import orderBy from "lodash/orderBy";
|
import orderBy from "lodash/orderBy";
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import type { ComputedRef } from "vue";
|
import type { ComputedRef, Ref } from "vue";
|
||||||
import { computed, onMounted, ref, watchEffect } from "vue";
|
import { computed, onMounted, ref, watchEffect } from "vue";
|
||||||
|
|
||||||
export function useCurrentCourseSession() {
|
export function useCurrentCourseSession() {
|
||||||
|
|
@ -166,7 +187,7 @@ export function useCourseData(courseSlug: string) {
|
||||||
log.error(result.error);
|
log.error(result.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
course.value = result.data?.course as Course;
|
course.value = result.data?.course as unknown as Course;
|
||||||
actionCompetences.value = result.data?.course
|
actionCompetences.value = result.data?.course
|
||||||
?.action_competences as ActionCompetence[];
|
?.action_competences as ActionCompetence[];
|
||||||
learningPath.value = result.data?.course?.learning_path as LearningPathType;
|
learningPath.value = result.data?.course?.learning_path as LearningPathType;
|
||||||
|
|
@ -487,3 +508,254 @@ export function useMyLearningMentors() {
|
||||||
loading,
|
loading,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getVvRoleDisplay(role: DashboardPersonRoleType) {
|
||||||
|
switch (role) {
|
||||||
|
case "LEARNING_MENTOR":
|
||||||
|
return t("a.Lernbegleitung");
|
||||||
|
case "LEARNING_MENTEE":
|
||||||
|
return t("a.Teilnehmer");
|
||||||
|
case "EXPERT":
|
||||||
|
return t("a.Experte");
|
||||||
|
case "MEMBER":
|
||||||
|
return t("a.Teilnehmer");
|
||||||
|
case "SUPERVISOR":
|
||||||
|
return t("a.Regionenleiter");
|
||||||
|
default:
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUkRoleDisplay(role: DashboardPersonRoleType) {
|
||||||
|
switch (role) {
|
||||||
|
case "LEARNING_MENTOR":
|
||||||
|
return t("a.Praxisbildner");
|
||||||
|
case "LEARNING_MENTEE":
|
||||||
|
return t("a.Teilnehmer");
|
||||||
|
case "EXPERT":
|
||||||
|
return t("a.Trainer");
|
||||||
|
case "MEMBER":
|
||||||
|
return t("a.Teilnehmer");
|
||||||
|
case "SUPERVISOR":
|
||||||
|
return t("a.Regionenleiter");
|
||||||
|
default:
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDashboardPersonsDueDates(
|
||||||
|
mode: DashboardPersonsPageMode = "default"
|
||||||
|
) {
|
||||||
|
const dashboardPersons = ref<DashboardPersonType[]>([]);
|
||||||
|
const dashboardDueDates = ref<DashboardDueDate[]>([]);
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
// due dates from today to future
|
||||||
|
const currentDueDates = ref<DashboardDueDate[]>([]);
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const [persons, dueDates] = await Promise.all([
|
||||||
|
fetchDashboardPersons(mode),
|
||||||
|
fetchDashboardDueDates(),
|
||||||
|
]);
|
||||||
|
dashboardPersons.value = persons;
|
||||||
|
|
||||||
|
// attach role name to persons
|
||||||
|
dashboardPersons.value.forEach((person) => {
|
||||||
|
person.course_sessions.forEach((cs) => {
|
||||||
|
if (cs.is_uk) {
|
||||||
|
cs.my_role_display = getUkRoleDisplay(cs.my_role);
|
||||||
|
cs.user_role_display = getUkRoleDisplay(cs.user_role);
|
||||||
|
} else if (cs.is_vv) {
|
||||||
|
cs.my_role_display = getVvRoleDisplay(cs.my_role);
|
||||||
|
cs.user_role_display = getVvRoleDisplay(cs.user_role);
|
||||||
|
} else {
|
||||||
|
cs.my_role_display = "";
|
||||||
|
cs.user_role_display = "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
dashboardDueDates.value = dueDates.map((dueDate) => {
|
||||||
|
const dateType = t(dueDate.date_type_translation_key);
|
||||||
|
const assignmentType = t(dueDate.assignment_type_translation_key);
|
||||||
|
dueDate.translatedType = dateType;
|
||||||
|
if (assignmentType) {
|
||||||
|
dueDate.translatedType += " " + assignmentType;
|
||||||
|
}
|
||||||
|
return dueDate;
|
||||||
|
});
|
||||||
|
|
||||||
|
currentDueDates.value = dashboardDueDates.value.filter((dueDate) => {
|
||||||
|
let refDate = dayjs(dueDate.start);
|
||||||
|
if (dueDate.end) {
|
||||||
|
refDate = dayjs(dueDate.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
return refDate >= dayjs().startOf("day");
|
||||||
|
});
|
||||||
|
|
||||||
|
// attach `LEARNING_MENTEE` to due dates for `LEARNING_MENTOR` persons
|
||||||
|
currentDueDates.value.forEach((dueDate) => {
|
||||||
|
if (dueDate.course_session.my_role === "LEARNING_MENTOR") {
|
||||||
|
dueDate.persons = dashboardPersons.value.filter((person) => {
|
||||||
|
if (
|
||||||
|
person.course_sessions
|
||||||
|
.map((cs) => cs.id)
|
||||||
|
.includes(dueDate.course_session.id)
|
||||||
|
) {
|
||||||
|
return person.course_sessions.some(
|
||||||
|
(cs) => cs.user_role === "LEARNING_MENTEE"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching data:", error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(fetchData);
|
||||||
|
|
||||||
|
return {
|
||||||
|
dashboardPersons,
|
||||||
|
dashboardDueDates,
|
||||||
|
currentDueDates,
|
||||||
|
loading,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMentorTexts() {
|
||||||
|
const texts = computed(() => {
|
||||||
|
try {
|
||||||
|
const is_uk =
|
||||||
|
useCourseSessionsStore().currentCourseSession?.course.configuration.is_uk;
|
||||||
|
const mentorTabTitle = is_uk ? "a.Praxisbildner" : "a.Lernbegleitung";
|
||||||
|
const myLearningMentors = is_uk ? "Meine Praxisbildner" : "Meine Lernbegleiter";
|
||||||
|
const inviteLearningMentor = is_uk
|
||||||
|
? "Neuen Praxisbildner einladen"
|
||||||
|
: "a.Neue Lernbegleitung einladen";
|
||||||
|
const inviteLearningMentorShort = is_uk
|
||||||
|
? "Praxisbildner einladen"
|
||||||
|
: "Lernbegleitung einladen";
|
||||||
|
const noLearningMentors = is_uk
|
||||||
|
? "a.Aktuell hast du noch keine Person als Praxisbildner eingeladen."
|
||||||
|
: "a.Aktuell hast du noch keine Person als Lernbegleitung eingeladen.";
|
||||||
|
const actionNoLearningMentors = is_uk
|
||||||
|
? "a.Aktuell hast du noch keine Person als Praxisbildner eingeladen. Lade jetzt jemanden ein."
|
||||||
|
: "a.Aktuell hast du noch keine Person als Lernbegleitung eingeladen. Lade jetzt jemanden ein.";
|
||||||
|
return {
|
||||||
|
mentorTabTitle,
|
||||||
|
myLearningMentors,
|
||||||
|
inviteLearningMentor,
|
||||||
|
inviteLearningMentorShort,
|
||||||
|
noLearningMentors,
|
||||||
|
actionNoLearningMentors,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
mentorTabTitle: "",
|
||||||
|
myLearningMentors: "",
|
||||||
|
inviteLearningMentor: "",
|
||||||
|
inviteLearningMentorShort: "",
|
||||||
|
noLearningMentors: "",
|
||||||
|
actionNoLearningMentors: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { ...texts.value };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCourseCircleProgress(circles: Ref<CircleType[] | undefined>) {
|
||||||
|
const inProgressCirclesCount = computed(() => {
|
||||||
|
if (circles.value?.length) {
|
||||||
|
return circles.value.filter(
|
||||||
|
(circle) =>
|
||||||
|
circle.learning_sequences.filter((ls) => someFinishedInLearningSequence(ls))
|
||||||
|
.length
|
||||||
|
).length;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const circlesCount = computed(() => {
|
||||||
|
return circles.value?.length ?? 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { inProgressCirclesCount, circlesCount };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCourseStatisticsv2(courseSlug: string) {
|
||||||
|
const dashboardStore = useDashboardStore();
|
||||||
|
const courseStatistics = ref<CourseStatisticsType | null>(null);
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
await dashboardStore.loadDashboardDetails();
|
||||||
|
const courseId = courseIdForCourseSlug(
|
||||||
|
dashboardStore.dashboardConfigsv2,
|
||||||
|
courseSlug
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (courseId) {
|
||||||
|
courseStatistics.value = await fetchStatisticData(courseId);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const courseSessionName = (courseSessionId: string) => {
|
||||||
|
return courseStatistics?.value?.course_session_properties?.sessions.find(
|
||||||
|
(session) => session.id === courseSessionId
|
||||||
|
)?.name;
|
||||||
|
};
|
||||||
|
|
||||||
|
const circleMeta = (circleId: string) => {
|
||||||
|
return courseStatistics?.value?.course_session_properties.circles.find(
|
||||||
|
(circle) => circle.id === circleId
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(fetchData);
|
||||||
|
|
||||||
|
return {
|
||||||
|
courseStatistics,
|
||||||
|
loading,
|
||||||
|
courseSessionName,
|
||||||
|
circleMeta,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCertificateQuery(userId: string | undefined, courseSlug: string) {
|
||||||
|
const certificatesQuery = (() => {
|
||||||
|
const courseSession = useCurrentCourseSession();
|
||||||
|
if (userId) {
|
||||||
|
return useQuery({
|
||||||
|
query: COMPETENCE_NAVI_CERTIFICATE_FOR_USER_QUERY,
|
||||||
|
variables: {
|
||||||
|
courseSlug: courseSlug,
|
||||||
|
courseSessionId: courseSession.value.id,
|
||||||
|
userId: userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return useQuery({
|
||||||
|
query: COMPETENCE_NAVI_CERTIFICATE_QUERY,
|
||||||
|
variables: {
|
||||||
|
courseSlug: courseSlug,
|
||||||
|
courseSessionId: courseSession.value.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
return { certificatesQuery };
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,11 @@ export const itFetch = (url: RequestInfo, options: RequestInit) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const itPost = (url: RequestInfo, data: unknown, options: RequestInit = {}) => {
|
export const itPost = <T>(
|
||||||
|
url: RequestInfo,
|
||||||
|
data: unknown,
|
||||||
|
options: RequestInit = {}
|
||||||
|
) => {
|
||||||
options = Object.assign({}, options);
|
options = Object.assign({}, options);
|
||||||
|
|
||||||
const headers = Object.assign(
|
const headers = Object.assign(
|
||||||
|
|
@ -56,11 +60,11 @@ export const itPost = (url: RequestInfo, data: unknown, options: RequestInit = {
|
||||||
return response.json().catch(() => {
|
return response.json().catch(() => {
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
});
|
});
|
||||||
});
|
}) as Promise<T>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const itGet = (url: RequestInfo) => {
|
export const itGet = <T>(url: RequestInfo) => {
|
||||||
return itPost(url, {}, { method: "GET" });
|
return itPost<T>(url, {}, { method: "GET" });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const itDelete = (url: RequestInfo) => {
|
export const itDelete = (url: RequestInfo) => {
|
||||||
|
|
@ -81,17 +85,17 @@ export function bustItGetCache(key?: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const itGetCached = (
|
export const itGetCached = <T>(
|
||||||
url: RequestInfo,
|
url: RequestInfo,
|
||||||
options = {
|
options = {
|
||||||
reload: false,
|
reload: false,
|
||||||
}
|
}
|
||||||
): Promise<any> => {
|
): Promise<T> => {
|
||||||
if (!itGetPromiseCache.has(url.toString()) || options.reload) {
|
if (!itGetPromiseCache.has(url.toString()) || options.reload) {
|
||||||
itGetPromiseCache.set(url.toString(), itGet(url));
|
itGetPromiseCache.set(url.toString(), itGet<T>(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
return itGetPromiseCache.get(url.toString()) as Promise<any>;
|
return itGetPromiseCache.get(url.toString()) as Promise<T>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useCSRFFetch = createFetch({
|
export const useCSRFFetch = createFetch({
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,14 @@ const documents = {
|
||||||
"\n query attendanceCheckQuery($courseSessionId: ID!) {\n course_session_attendance_course(id: $courseSessionId) {\n id\n attendance_user_list {\n user_id\n status\n }\n }\n }\n": types.AttendanceCheckQueryDocument,
|
"\n query attendanceCheckQuery($courseSessionId: ID!) {\n course_session_attendance_course(id: $courseSessionId) {\n id\n attendance_user_list {\n user_id\n status\n }\n }\n }\n": types.AttendanceCheckQueryDocument,
|
||||||
"\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n needs_expert_evaluation\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n solution_sample {\n id\n url\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n first_name\n last_name\n }\n assignment_user {\n avatar_url\n first_name\n last_name\n id\n }\n evaluation_points\n evaluation_max_points\n evaluation_passed\n edoniq_extended_time_flag\n completion_data\n task_completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument,
|
"\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n needs_expert_evaluation\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n solution_sample {\n id\n url\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n first_name\n last_name\n }\n assignment_user {\n avatar_url\n first_name\n last_name\n id\n }\n evaluation_points\n evaluation_max_points\n evaluation_passed\n edoniq_extended_time_flag\n completion_data\n task_completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument,
|
||||||
"\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateQueryDocument,
|
"\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateQueryDocument,
|
||||||
|
"\n query competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userId: UUID!\n ) {\n competence_certificate_list_for_user(course_slug: $courseSlug, user_id: $userId) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateForUserQueryDocument,
|
||||||
"\n query courseSessionDetail($courseSessionId: ID!) {\n course_session(id: $courseSessionId) {\n id\n title\n course {\n id\n title\n slug\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n }\n users {\n id\n user_id\n first_name\n last_name\n email\n avatar_url\n role\n circles {\n id\n title\n slug\n }\n }\n attendance_courses {\n id\n location\n trainer\n due_date {\n id\n start\n end\n }\n learning_content_id\n learning_content {\n id\n title\n circle {\n id\n title\n slug\n }\n }\n }\n assignments {\n id\n submission_deadline {\n id\n start\n }\n evaluation_deadline {\n id\n start\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n edoniq_tests {\n id\n deadline {\n id\n start\n end\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n }\n }\n": types.CourseSessionDetailDocument,
|
"\n query courseSessionDetail($courseSessionId: ID!) {\n course_session(id: $courseSessionId) {\n id\n title\n course {\n id\n title\n slug\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n }\n users {\n id\n user_id\n first_name\n last_name\n email\n avatar_url\n role\n circles {\n id\n title\n slug\n }\n }\n attendance_courses {\n id\n location\n trainer\n due_date {\n id\n start\n end\n }\n learning_content_id\n learning_content {\n id\n title\n circle {\n id\n title\n slug\n }\n }\n }\n assignments {\n id\n submission_deadline {\n id\n start\n }\n evaluation_deadline {\n id\n start\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n edoniq_tests {\n id\n deadline {\n id\n start\n end\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n }\n }\n": types.CourseSessionDetailDocument,
|
||||||
"\n query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n title\n slug\n category_name\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n action_competences {\n competence_id\n ...CoursePageFields\n performance_criteria {\n competence_id\n learning_unit {\n id\n slug\n evaluate_url\n }\n ...CoursePageFields\n }\n }\n learning_path {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n content_url\n minutes\n description\n ...CoursePageFields\n ... on LearningContentAssignmentObjectType {\n assignment_type\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n has_extended_time_test\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentRichTextObjectType {\n text\n }\n }\n }\n }\n }\n }\n }\n }\n }\n": types.CourseQueryDocument,
|
"\n query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n title\n slug\n category_name\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n is_uk\n }\n action_competences {\n competence_id\n ...CoursePageFields\n performance_criteria {\n competence_id\n learning_unit {\n id\n slug\n evaluate_url\n }\n ...CoursePageFields\n }\n }\n learning_path {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n content_url\n minutes\n description\n ...CoursePageFields\n ... on LearningContentAssignmentObjectType {\n assignment_type\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n has_extended_time_test\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentRichTextObjectType {\n text\n }\n }\n }\n }\n }\n }\n }\n }\n }\n": types.CourseQueryDocument,
|
||||||
"\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n course_configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n }\n }\n": types.DashboardConfigDocument,
|
"\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n course_configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n is_uk\n }\n }\n }\n": types.DashboardConfigDocument,
|
||||||
"\n query dashboardProgress($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n competence {\n _id\n total_count\n success_count\n fail_count\n }\n assignment {\n _id\n total_count\n points_max_count\n points_achieved_count\n }\n }\n }\n": types.DashboardProgressDocument,
|
"\n query dashboardProgress($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n competence {\n _id\n total_count\n success_count\n fail_count\n }\n assignment {\n _id\n total_count\n points_max_count\n points_achieved_count\n }\n }\n }\n": types.DashboardProgressDocument,
|
||||||
"\n query courseStatistics($courseId: ID!) {\n course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n course_session_selection_ids\n course_session_selection_metrics {\n _id\n session_count\n participant_count\n expert_count\n }\n attendance_day_presences {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n due_date\n participants_present\n participants_total\n details_url\n }\n summary {\n _id\n days_completed\n participants_present\n }\n }\n feedback_responses {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n experts\n satisfaction_average\n satisfaction_max\n details_url\n }\n summary {\n _id\n satisfaction_average\n satisfaction_max\n total_responses\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n competences {\n _id\n summary {\n _id\n success_total\n fail_total\n }\n records {\n _id\n course_session_id\n generation\n circle_id\n title\n success_count\n fail_count\n details_url\n }\n }\n }\n }\n": types.CourseStatisticsDocument,
|
"\n query dashboardCourseData($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n }\n }\n": types.DashboardCourseDataDocument,
|
||||||
|
"\n query courseStatistics($courseId: ID!) {\n course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n course_session_selection_ids\n course_session_selection_metrics {\n _id\n session_count\n participant_count\n expert_count\n }\n attendance_day_presences {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n due_date\n participants_present\n participants_total\n details_url\n }\n summary {\n _id\n days_completed\n participants_present\n }\n }\n feedback_responses {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n experts\n satisfaction_average\n satisfaction_max\n details_url\n }\n summary {\n _id\n satisfaction_average\n satisfaction_max\n total_responses\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n total_passed\n total_failed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n competences {\n _id\n summary {\n _id\n success_total\n fail_total\n }\n records {\n _id\n course_session_id\n generation\n circle_id\n title\n success_count\n fail_count\n details_url\n }\n }\n }\n }\n": types.CourseStatisticsDocument,
|
||||||
|
"\n query mentorCourseStatistics($courseId: ID!) {\n mentor_course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_selection_ids\n user_selection_ids\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n total_passed\n total_failed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n }\n }\n": types.MentorCourseStatisticsDocument,
|
||||||
"\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $learningContentType: String!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n learning_content_type: $learningContentType\n data: $data\n submitted: $submitted\n ) {\n feedback_response {\n id\n data\n submitted\n }\n errors {\n field\n messages\n }\n }\n }\n": types.SendFeedbackMutationDocument,
|
"\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $learningContentType: String!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n learning_content_type: $learningContentType\n data: $data\n submitted: $submitted\n ) {\n feedback_response {\n id\n data\n submitted\n }\n errors {\n field\n messages\n }\n }\n }\n": types.SendFeedbackMutationDocument,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -65,6 +68,10 @@ export function graphql(source: "\n query assignmentCompletionQuery(\n $assi
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"];
|
export function graphql(source: "\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n query competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userId: UUID!\n ) {\n competence_certificate_list_for_user(course_slug: $courseSlug, user_id: $userId) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userId: UUID!\n ) {\n competence_certificate_list_for_user(course_slug: $courseSlug, user_id: $userId) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
|
|
@ -72,11 +79,11 @@ export function graphql(source: "\n query courseSessionDetail($courseSessionId:
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n title\n slug\n category_name\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n action_competences {\n competence_id\n ...CoursePageFields\n performance_criteria {\n competence_id\n learning_unit {\n id\n slug\n evaluate_url\n }\n ...CoursePageFields\n }\n }\n learning_path {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n content_url\n minutes\n description\n ...CoursePageFields\n ... on LearningContentAssignmentObjectType {\n assignment_type\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n has_extended_time_test\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentRichTextObjectType {\n text\n }\n }\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n title\n slug\n category_name\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n action_competences {\n competence_id\n ...CoursePageFields\n performance_criteria {\n competence_id\n learning_unit {\n id\n slug\n evaluate_url\n }\n ...CoursePageFields\n }\n }\n learning_path {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n content_url\n minutes\n description\n ...CoursePageFields\n ... on LearningContentAssignmentObjectType {\n assignment_type\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n has_extended_time_test\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentRichTextObjectType {\n text\n }\n }\n }\n }\n }\n }\n }\n }\n }\n"];
|
export function graphql(source: "\n query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n title\n slug\n category_name\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n is_uk\n }\n action_competences {\n competence_id\n ...CoursePageFields\n performance_criteria {\n competence_id\n learning_unit {\n id\n slug\n evaluate_url\n }\n ...CoursePageFields\n }\n }\n learning_path {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n content_url\n minutes\n description\n ...CoursePageFields\n ... on LearningContentAssignmentObjectType {\n assignment_type\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n has_extended_time_test\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentRichTextObjectType {\n text\n }\n }\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n title\n slug\n category_name\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n is_uk\n }\n action_competences {\n competence_id\n ...CoursePageFields\n performance_criteria {\n competence_id\n learning_unit {\n id\n slug\n evaluate_url\n }\n ...CoursePageFields\n }\n }\n learning_path {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n content_url\n minutes\n description\n ...CoursePageFields\n ... on LearningContentAssignmentObjectType {\n assignment_type\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n has_extended_time_test\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentRichTextObjectType {\n text\n }\n }\n }\n }\n }\n }\n }\n }\n }\n"];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n course_configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n }\n }\n"): (typeof documents)["\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n course_configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n }\n }\n"];
|
export function graphql(source: "\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n course_configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n is_uk\n }\n }\n }\n"): (typeof documents)["\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n course_configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n is_uk\n }\n }\n }\n"];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
|
|
@ -84,7 +91,15 @@ export function graphql(source: "\n query dashboardProgress($courseId: ID!) {\n
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query courseStatistics($courseId: ID!) {\n course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n course_session_selection_ids\n course_session_selection_metrics {\n _id\n session_count\n participant_count\n expert_count\n }\n attendance_day_presences {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n due_date\n participants_present\n participants_total\n details_url\n }\n summary {\n _id\n days_completed\n participants_present\n }\n }\n feedback_responses {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n experts\n satisfaction_average\n satisfaction_max\n details_url\n }\n summary {\n _id\n satisfaction_average\n satisfaction_max\n total_responses\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n competences {\n _id\n summary {\n _id\n success_total\n fail_total\n }\n records {\n _id\n course_session_id\n generation\n circle_id\n title\n success_count\n fail_count\n details_url\n }\n }\n }\n }\n"): (typeof documents)["\n query courseStatistics($courseId: ID!) {\n course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n course_session_selection_ids\n course_session_selection_metrics {\n _id\n session_count\n participant_count\n expert_count\n }\n attendance_day_presences {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n due_date\n participants_present\n participants_total\n details_url\n }\n summary {\n _id\n days_completed\n participants_present\n }\n }\n feedback_responses {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n experts\n satisfaction_average\n satisfaction_max\n details_url\n }\n summary {\n _id\n satisfaction_average\n satisfaction_max\n total_responses\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n competences {\n _id\n summary {\n _id\n success_total\n fail_total\n }\n records {\n _id\n course_session_id\n generation\n circle_id\n title\n success_count\n fail_count\n details_url\n }\n }\n }\n }\n"];
|
export function graphql(source: "\n query dashboardCourseData($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n }\n }\n"): (typeof documents)["\n query dashboardCourseData($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n }\n }\n"];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n query courseStatistics($courseId: ID!) {\n course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n course_session_selection_ids\n course_session_selection_metrics {\n _id\n session_count\n participant_count\n expert_count\n }\n attendance_day_presences {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n due_date\n participants_present\n participants_total\n details_url\n }\n summary {\n _id\n days_completed\n participants_present\n }\n }\n feedback_responses {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n experts\n satisfaction_average\n satisfaction_max\n details_url\n }\n summary {\n _id\n satisfaction_average\n satisfaction_max\n total_responses\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n total_passed\n total_failed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n competences {\n _id\n summary {\n _id\n success_total\n fail_total\n }\n records {\n _id\n course_session_id\n generation\n circle_id\n title\n success_count\n fail_count\n details_url\n }\n }\n }\n }\n"): (typeof documents)["\n query courseStatistics($courseId: ID!) {\n course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n course_session_selection_ids\n course_session_selection_metrics {\n _id\n session_count\n participant_count\n expert_count\n }\n attendance_day_presences {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n due_date\n participants_present\n participants_total\n details_url\n }\n summary {\n _id\n days_completed\n participants_present\n }\n }\n feedback_responses {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n experts\n satisfaction_average\n satisfaction_max\n details_url\n }\n summary {\n _id\n satisfaction_average\n satisfaction_max\n total_responses\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n total_passed\n total_failed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n competences {\n _id\n summary {\n _id\n success_total\n fail_total\n }\n records {\n _id\n course_session_id\n generation\n circle_id\n title\n success_count\n fail_count\n details_url\n }\n }\n }\n }\n"];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n query mentorCourseStatistics($courseId: ID!) {\n mentor_course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_selection_ids\n user_selection_ids\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n total_passed\n total_failed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query mentorCourseStatistics($courseId: ID!) {\n mentor_course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_selection_ids\n user_selection_ids\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n total_passed\n total_failed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n }\n }\n"];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +1,6 @@
|
||||||
type Query {
|
type Query {
|
||||||
course_statistics(course_id: ID!): CourseStatisticsType
|
course_statistics(course_id: ID!): CourseStatisticsType
|
||||||
|
mentor_course_statistics(course_id: ID!): BaseStatisticsType
|
||||||
course_progress(course_id: ID!): CourseProgressType
|
course_progress(course_id: ID!): CourseProgressType
|
||||||
dashboard_config: [DashboardConfigType!]!
|
dashboard_config: [DashboardConfigType!]!
|
||||||
learning_path(id: ID, slug: String, course_id: ID, course_slug: String): LearningPathObjectType
|
learning_path(id: ID, slug: String, course_id: ID, course_slug: String): LearningPathObjectType
|
||||||
|
|
@ -20,6 +21,7 @@ type Query {
|
||||||
learning_content_document_list: LearningContentDocumentListObjectType
|
learning_content_document_list: LearningContentDocumentListObjectType
|
||||||
competence_certificate(id: ID, slug: String): CompetenceCertificateObjectType
|
competence_certificate(id: ID, slug: String): CompetenceCertificateObjectType
|
||||||
competence_certificate_list(id: ID, slug: String, course_id: ID, course_slug: String): CompetenceCertificateListObjectType
|
competence_certificate_list(id: ID, slug: String, course_id: ID, course_slug: String): CompetenceCertificateListObjectType
|
||||||
|
competence_certificate_list_for_user(id: ID, slug: String, course_id: ID, course_slug: String, user_id: UUID): CompetenceCertificateListObjectType
|
||||||
assignment(id: ID, slug: String): AssignmentObjectType
|
assignment(id: ID, slug: String): AssignmentObjectType
|
||||||
assignment_completion(assignment_id: ID!, course_session_id: ID!, learning_content_page_id: ID, assignment_user_id: UUID): AssignmentCompletionObjectType
|
assignment_completion(assignment_id: ID!, course_session_id: ID!, learning_content_page_id: ID, assignment_user_id: UUID): AssignmentCompletionObjectType
|
||||||
}
|
}
|
||||||
|
|
@ -29,15 +31,59 @@ type CourseStatisticsType {
|
||||||
course_id: ID!
|
course_id: ID!
|
||||||
course_title: String!
|
course_title: String!
|
||||||
course_slug: String!
|
course_slug: String!
|
||||||
course_session_properties: StatisticsCourseSessionPropertiesType!
|
|
||||||
course_session_selection_ids: [ID]!
|
course_session_selection_ids: [ID]!
|
||||||
|
user_selection_ids: [ID]
|
||||||
|
assignments: AssignmentsStatisticsType!
|
||||||
|
course_session_properties: StatisticsCourseSessionPropertiesType!
|
||||||
course_session_selection_metrics: StatisticsCourseSessionsSelectionMetricType!
|
course_session_selection_metrics: StatisticsCourseSessionsSelectionMetricType!
|
||||||
attendance_day_presences: AttendanceDayPresencesStatisticsType!
|
attendance_day_presences: AttendanceDayPresencesStatisticsType!
|
||||||
feedback_responses: FeedbackStatisticsResponsesType!
|
feedback_responses: FeedbackStatisticsResponsesType!
|
||||||
assignments: AssignmentsStatisticsType!
|
|
||||||
competences: CompetencesStatisticsType!
|
competences: CompetencesStatisticsType!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AssignmentsStatisticsType {
|
||||||
|
_id: ID!
|
||||||
|
records: [AssignmentStatisticsRecordType!]!
|
||||||
|
summary: AssignmentStatisticsSummaryType!
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssignmentStatisticsRecordType {
|
||||||
|
_id: ID!
|
||||||
|
course_session_id: ID!
|
||||||
|
course_session_assignment_id: ID!
|
||||||
|
circle_id: ID!
|
||||||
|
generation: String!
|
||||||
|
assignment_type_translation_key: String!
|
||||||
|
assignment_title: String!
|
||||||
|
deadline: DateTime!
|
||||||
|
metrics: AssignmentCompletionMetricsType!
|
||||||
|
details_url: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The `DateTime` scalar type represents a DateTime
|
||||||
|
value as specified by
|
||||||
|
[iso8601](https://en.wikipedia.org/wiki/ISO_8601).
|
||||||
|
"""
|
||||||
|
scalar DateTime
|
||||||
|
|
||||||
|
type AssignmentCompletionMetricsType {
|
||||||
|
_id: ID!
|
||||||
|
passed_count: Int!
|
||||||
|
failed_count: Int!
|
||||||
|
unranked_count: Int!
|
||||||
|
ranking_completed: Boolean!
|
||||||
|
average_passed: Float!
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssignmentStatisticsSummaryType {
|
||||||
|
_id: ID!
|
||||||
|
completed_count: Int!
|
||||||
|
average_passed: Float!
|
||||||
|
total_passed: Int!
|
||||||
|
total_failed: Int!
|
||||||
|
}
|
||||||
|
|
||||||
type StatisticsCourseSessionPropertiesType {
|
type StatisticsCourseSessionPropertiesType {
|
||||||
_id: ID!
|
_id: ID!
|
||||||
sessions: [StatisticsCourseSessionDataType!]!
|
sessions: [StatisticsCourseSessionDataType!]!
|
||||||
|
|
@ -79,13 +125,6 @@ type PresenceRecordStatisticsType {
|
||||||
details_url: String!
|
details_url: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
|
||||||
The `DateTime` scalar type represents a DateTime
|
|
||||||
value as specified by
|
|
||||||
[iso8601](https://en.wikipedia.org/wiki/ISO_8601).
|
|
||||||
"""
|
|
||||||
scalar DateTime
|
|
||||||
|
|
||||||
type AttendanceSummaryStatisticsType {
|
type AttendanceSummaryStatisticsType {
|
||||||
_id: ID!
|
_id: ID!
|
||||||
days_completed: Int!
|
days_completed: Int!
|
||||||
|
|
@ -116,40 +155,6 @@ type FeedbackStatisticsSummaryType {
|
||||||
total_responses: Int!
|
total_responses: Int!
|
||||||
}
|
}
|
||||||
|
|
||||||
type AssignmentsStatisticsType {
|
|
||||||
_id: ID!
|
|
||||||
records: [AssignmentStatisticsRecordType!]!
|
|
||||||
summary: AssignmentStatisticsSummaryType!
|
|
||||||
}
|
|
||||||
|
|
||||||
type AssignmentStatisticsRecordType {
|
|
||||||
_id: ID!
|
|
||||||
course_session_id: ID!
|
|
||||||
course_session_assignment_id: ID!
|
|
||||||
circle_id: ID!
|
|
||||||
generation: String!
|
|
||||||
assignment_type_translation_key: String!
|
|
||||||
assignment_title: String!
|
|
||||||
deadline: DateTime!
|
|
||||||
metrics: AssignmentCompletionMetricsType!
|
|
||||||
details_url: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type AssignmentCompletionMetricsType {
|
|
||||||
_id: ID!
|
|
||||||
passed_count: Int!
|
|
||||||
failed_count: Int!
|
|
||||||
unranked_count: Int!
|
|
||||||
ranking_completed: Boolean!
|
|
||||||
average_passed: Float!
|
|
||||||
}
|
|
||||||
|
|
||||||
type AssignmentStatisticsSummaryType {
|
|
||||||
_id: ID!
|
|
||||||
completed_count: Int!
|
|
||||||
average_passed: Float!
|
|
||||||
}
|
|
||||||
|
|
||||||
type CompetencesStatisticsType {
|
type CompetencesStatisticsType {
|
||||||
_id: ID!
|
_id: ID!
|
||||||
summary: CompetencePerformanceStatisticsSummaryType!
|
summary: CompetencePerformanceStatisticsSummaryType!
|
||||||
|
|
@ -173,12 +178,22 @@ type CompetenceRecordStatisticsType {
|
||||||
details_url: String!
|
details_url: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BaseStatisticsType {
|
||||||
|
_id: ID!
|
||||||
|
course_id: ID!
|
||||||
|
course_title: String!
|
||||||
|
course_slug: String!
|
||||||
|
course_session_selection_ids: [ID]!
|
||||||
|
user_selection_ids: [ID]
|
||||||
|
assignments: AssignmentsStatisticsType!
|
||||||
|
}
|
||||||
|
|
||||||
type CourseProgressType {
|
type CourseProgressType {
|
||||||
_id: ID!
|
_id: ID!
|
||||||
course_id: ID!
|
course_id: ID!
|
||||||
session_to_continue_id: ID
|
session_to_continue_id: ID
|
||||||
competence: ProgressDashboardCompetenceType!
|
competence: ProgressDashboardCompetenceType
|
||||||
assignment: ProgressDashboardAssignmentType!
|
assignment: ProgressDashboardAssignmentType
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProgressDashboardCompetenceType {
|
type ProgressDashboardCompetenceType {
|
||||||
|
|
@ -208,6 +223,7 @@ enum DashboardType {
|
||||||
PROGRESS_DASHBOARD
|
PROGRESS_DASHBOARD
|
||||||
SIMPLE_DASHBOARD
|
SIMPLE_DASHBOARD
|
||||||
MENTOR_DASHBOARD
|
MENTOR_DASHBOARD
|
||||||
|
PRAXISBILDNER_DASHBOARD
|
||||||
}
|
}
|
||||||
|
|
||||||
type CourseConfigurationObjectType {
|
type CourseConfigurationObjectType {
|
||||||
|
|
@ -215,6 +231,8 @@ type CourseConfigurationObjectType {
|
||||||
enable_circle_documents: Boolean!
|
enable_circle_documents: Boolean!
|
||||||
enable_learning_mentor: Boolean!
|
enable_learning_mentor: Boolean!
|
||||||
enable_competence_certificates: Boolean!
|
enable_competence_certificates: Boolean!
|
||||||
|
is_vv: Boolean!
|
||||||
|
is_uk: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type LearningPathObjectType implements CoursePageInterface {
|
type LearningPathObjectType implements CoursePageInterface {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ export const AttendanceSummaryStatisticsType = "AttendanceSummaryStatisticsType"
|
||||||
export const AttendanceUserInputType = "AttendanceUserInputType";
|
export const AttendanceUserInputType = "AttendanceUserInputType";
|
||||||
export const AttendanceUserObjectType = "AttendanceUserObjectType";
|
export const AttendanceUserObjectType = "AttendanceUserObjectType";
|
||||||
export const AttendanceUserStatus = "AttendanceUserStatus";
|
export const AttendanceUserStatus = "AttendanceUserStatus";
|
||||||
|
export const BaseStatisticsType = "BaseStatisticsType";
|
||||||
export const Boolean = "Boolean";
|
export const Boolean = "Boolean";
|
||||||
export const CircleLightObjectType = "CircleLightObjectType";
|
export const CircleLightObjectType = "CircleLightObjectType";
|
||||||
export const CircleObjectType = "CircleObjectType";
|
export const CircleObjectType = "CircleObjectType";
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,42 @@ export const COMPETENCE_NAVI_CERTIFICATE_QUERY = graphql(`
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
export const COMPETENCE_NAVI_CERTIFICATE_FOR_USER_QUERY = graphql(`
|
||||||
|
query competenceCertificateForUserQuery(
|
||||||
|
$courseSlug: String!
|
||||||
|
$courseSessionId: ID!
|
||||||
|
$userId: UUID!
|
||||||
|
) {
|
||||||
|
competence_certificate_list_for_user(course_slug: $courseSlug, user_id: $userId) {
|
||||||
|
...CoursePageFields
|
||||||
|
competence_certificates {
|
||||||
|
...CoursePageFields
|
||||||
|
assignments {
|
||||||
|
...CoursePageFields
|
||||||
|
assignment_type
|
||||||
|
max_points
|
||||||
|
completion(course_session_id: $courseSessionId) {
|
||||||
|
id
|
||||||
|
completion_status
|
||||||
|
submitted_at
|
||||||
|
evaluation_points
|
||||||
|
evaluation_max_points
|
||||||
|
evaluation_passed
|
||||||
|
}
|
||||||
|
learning_content {
|
||||||
|
...CoursePageFields
|
||||||
|
circle {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
export const COURSE_SESSION_DETAIL_QUERY = graphql(`
|
export const COURSE_SESSION_DETAIL_QUERY = graphql(`
|
||||||
query courseSessionDetail($courseSessionId: ID!) {
|
query courseSessionDetail($courseSessionId: ID!) {
|
||||||
course_session(id: $courseSessionId) {
|
course_session(id: $courseSessionId) {
|
||||||
|
|
@ -220,6 +256,7 @@ export const COURSE_QUERY = graphql(`
|
||||||
enable_circle_documents
|
enable_circle_documents
|
||||||
enable_learning_mentor
|
enable_learning_mentor
|
||||||
enable_competence_certificates
|
enable_competence_certificates
|
||||||
|
is_uk
|
||||||
}
|
}
|
||||||
action_competences {
|
action_competences {
|
||||||
competence_id
|
competence_id
|
||||||
|
|
@ -304,6 +341,7 @@ export const DASHBOARD_CONFIG = graphql(`
|
||||||
enable_circle_documents
|
enable_circle_documents
|
||||||
enable_learning_mentor
|
enable_learning_mentor
|
||||||
enable_competence_certificates
|
enable_competence_certificates
|
||||||
|
is_uk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -331,6 +369,16 @@ export const DASHBOARD_COURSE_SESSION_PROGRESS = graphql(`
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
export const DASHBOARD_COURSE_DATA = graphql(`
|
||||||
|
query dashboardCourseData($courseId: ID!) {
|
||||||
|
course_progress(course_id: $courseId) {
|
||||||
|
_id
|
||||||
|
course_id
|
||||||
|
session_to_continue_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
export const DASHBOARD_COURSE_STATISTICS = graphql(`
|
export const DASHBOARD_COURSE_STATISTICS = graphql(`
|
||||||
query courseStatistics($courseId: ID!) {
|
query courseStatistics($courseId: ID!) {
|
||||||
course_statistics(course_id: $courseId) {
|
course_statistics(course_id: $courseId) {
|
||||||
|
|
@ -400,6 +448,8 @@ export const DASHBOARD_COURSE_STATISTICS = graphql(`
|
||||||
_id
|
_id
|
||||||
completed_count
|
completed_count
|
||||||
average_passed
|
average_passed
|
||||||
|
total_passed
|
||||||
|
total_failed
|
||||||
}
|
}
|
||||||
records {
|
records {
|
||||||
_id
|
_id
|
||||||
|
|
@ -442,3 +492,45 @@ export const DASHBOARD_COURSE_STATISTICS = graphql(`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
export const DASHBOARD_MENTOR_COMPETENCE_SUMMARY = graphql(`
|
||||||
|
query mentorCourseStatistics($courseId: ID!) {
|
||||||
|
mentor_course_statistics(course_id: $courseId) {
|
||||||
|
_id
|
||||||
|
course_id
|
||||||
|
course_title
|
||||||
|
course_slug
|
||||||
|
course_session_selection_ids
|
||||||
|
user_selection_ids
|
||||||
|
assignments {
|
||||||
|
_id
|
||||||
|
summary {
|
||||||
|
_id
|
||||||
|
completed_count
|
||||||
|
average_passed
|
||||||
|
total_passed
|
||||||
|
total_failed
|
||||||
|
}
|
||||||
|
records {
|
||||||
|
_id
|
||||||
|
course_session_id
|
||||||
|
course_session_assignment_id
|
||||||
|
circle_id
|
||||||
|
generation
|
||||||
|
assignment_title
|
||||||
|
assignment_type_translation_key
|
||||||
|
details_url
|
||||||
|
deadline
|
||||||
|
metrics {
|
||||||
|
_id
|
||||||
|
passed_count
|
||||||
|
failed_count
|
||||||
|
unranked_count
|
||||||
|
ranking_completed
|
||||||
|
average_passed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
|
||||||
|
|
@ -1,179 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed, onMounted, ref, watch } from "vue";
|
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
|
||||||
import { useTranslation } from "i18next-vue";
|
|
||||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
|
||||||
import type { DueDate } from "@/types";
|
|
||||||
import DueDatesList from "@/components/dueDates/DueDatesList.vue";
|
|
||||||
import { useCourseData } from "@/composables";
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const UNFILTERED = Number.MAX_SAFE_INTEGER.toString();
|
|
||||||
const courseSessionsStore = useCourseSessionsStore();
|
|
||||||
|
|
||||||
type Item = {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CourseItem = Item & {
|
|
||||||
slug: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const courses: CourseItem[] = courseSessionsStore.uniqueCourseSessionsByCourse.map(
|
|
||||||
(cs) => ({
|
|
||||||
id: cs.course.id,
|
|
||||||
name: cs.course.title,
|
|
||||||
slug: cs.course.slug,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
const selectedCourse = ref<CourseItem>(courses[0]);
|
|
||||||
|
|
||||||
const courseSessions = computed(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
id: UNFILTERED,
|
|
||||||
name: t("a.AlleDurchführungen"),
|
|
||||||
},
|
|
||||||
...courseSessionsStore.allCourseSessions
|
|
||||||
.filter((cs) => cs.course.id === selectedCourse.value.id)
|
|
||||||
.map((cs) => ({ id: cs.id, name: cs.title })),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
const selectedSession = ref<Item>(courseSessions.value[0]);
|
|
||||||
|
|
||||||
// pre-select course and session if we are in a course session
|
|
||||||
if (courseSessionsStore.currentCourseSession) {
|
|
||||||
const session = courseSessionsStore.currentCourseSession;
|
|
||||||
const { id: courseId, title: courseName, slug: courseSlug } = session.course;
|
|
||||||
selectedCourse.value = { id: courseId, name: courseName, slug: courseSlug };
|
|
||||||
const { id: sessionId, title: sessionName } = session;
|
|
||||||
selectedSession.value = { id: sessionId, name: sessionName };
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialItemCircle: Item = {
|
|
||||||
id: UNFILTERED,
|
|
||||||
name: t("a.AlleCircle"),
|
|
||||||
};
|
|
||||||
const circles = ref<Item[]>([initialItemCircle]);
|
|
||||||
const selectedCircle = ref<Item>(circles.value[0]);
|
|
||||||
|
|
||||||
async function loadCircleValues() {
|
|
||||||
if (selectedCourse.value) {
|
|
||||||
const learningPathQuery = useCourseData(selectedCourse.value.slug);
|
|
||||||
await learningPathQuery.resultPromise;
|
|
||||||
circles.value = [
|
|
||||||
initialItemCircle,
|
|
||||||
...(learningPathQuery.circles.value ?? []).map((circle) => ({
|
|
||||||
id: circle.id,
|
|
||||||
name: circle.title,
|
|
||||||
})),
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
circles.value = [initialItemCircle];
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedCircle.value = circles.value[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(selectedCourse, async () => {
|
|
||||||
selectedSession.value = courseSessions.value[0];
|
|
||||||
await loadCircleValues();
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
await loadCircleValues();
|
|
||||||
});
|
|
||||||
|
|
||||||
const appointments = computed(() => {
|
|
||||||
return courseSessionsStore
|
|
||||||
.allDueDates()
|
|
||||||
.filter(
|
|
||||||
(dueDate) =>
|
|
||||||
isMatchingCourse(dueDate) &&
|
|
||||||
isMatchingSession(dueDate) &&
|
|
||||||
isMatchingCircle(dueDate)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const isMatchingSession = (dueDate: DueDate) =>
|
|
||||||
selectedSession.value.id === UNFILTERED ||
|
|
||||||
dueDate.course_session_id === selectedSession.value.id;
|
|
||||||
|
|
||||||
const isMatchingCircle = (dueDate: DueDate) =>
|
|
||||||
selectedCircle.value.id === UNFILTERED ||
|
|
||||||
dueDate.circle?.id === selectedCircle.value.id;
|
|
||||||
|
|
||||||
const isMatchingCourse = (dueDate: DueDate) =>
|
|
||||||
courseSessions.value.map((cs) => cs.id).includes(dueDate.course_session_id);
|
|
||||||
|
|
||||||
const numAppointmentsToShow = ref(7);
|
|
||||||
const canLoadMore = computed(() => {
|
|
||||||
return numAppointmentsToShow.value < appointments.value.length;
|
|
||||||
});
|
|
||||||
|
|
||||||
async function loadAdditionalAppointments() {
|
|
||||||
numAppointmentsToShow.value *= 2;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="bg-gray-200">
|
|
||||||
<div class="container-large px-8 py-8">
|
|
||||||
<header class="mb-6 flex flex-col lg:flex-row lg:items-center lg:justify-between">
|
|
||||||
<h1>{{ $t("a.AlleTermine") }}</h1>
|
|
||||||
<div>
|
|
||||||
<ItDropdownSelect
|
|
||||||
v-model="selectedCourse"
|
|
||||||
data-cy="appointments-course-select"
|
|
||||||
:items="courses"
|
|
||||||
></ItDropdownSelect>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<div class="flex flex-col space-y-2">
|
|
||||||
<div class="flex flex-col space-x-0 bg-white lg:flex-row lg:space-x-3">
|
|
||||||
<ItDropdownSelect
|
|
||||||
v-model="selectedSession"
|
|
||||||
data-cy="appointments-session-select"
|
|
||||||
:items="courseSessions"
|
|
||||||
borderless
|
|
||||||
></ItDropdownSelect>
|
|
||||||
<ItDropdownSelect
|
|
||||||
v-model="selectedCircle"
|
|
||||||
data-cy="appointments-circle-select"
|
|
||||||
:items="circles"
|
|
||||||
borderless
|
|
||||||
></ItDropdownSelect>
|
|
||||||
</div>
|
|
||||||
<div class="bg-white px-5">
|
|
||||||
<DueDatesList
|
|
||||||
:show-top-border="false"
|
|
||||||
:show-bottom-border="canLoadMore"
|
|
||||||
:due-dates="appointments"
|
|
||||||
:show-all-due-dates-link="false"
|
|
||||||
:max-count="numAppointmentsToShow"
|
|
||||||
data-cy="appointments-list"
|
|
||||||
:show-course-session="true"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
v-if="canLoadMore"
|
|
||||||
class="py-4 underline"
|
|
||||||
data-cy="load-more-notifications"
|
|
||||||
@click="loadAdditionalAppointments()"
|
|
||||||
>
|
|
||||||
{{ $t("notifications.load_more") }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="postcss" scoped>
|
|
||||||
.no-border-last li:last-child {
|
|
||||||
border-bottom: none !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -41,8 +41,8 @@ const userStore = useUserStore();
|
||||||
class="bg-white p-4 lg:p-8"
|
class="bg-white p-4 lg:p-8"
|
||||||
@submit.prevent="
|
@submit.prevent="
|
||||||
userStore.handleLogin(
|
userStore.handleLogin(
|
||||||
state.username,
|
state.username.trim(),
|
||||||
state.password,
|
state.password.trim(),
|
||||||
route.query.next as string
|
route.query.next as string
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ const updateItems = async (_items: []) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const response = await itGet("/api/notify/email_notification_settings/");
|
const response: any = await itGet("/api/notify/email_notification_settings/");
|
||||||
items.value = items.value.map((item) => {
|
items.value = items.value.map((item) => {
|
||||||
item.checked = response.includes(item.value);
|
item.checked = response.includes(item.value);
|
||||||
return item;
|
return item;
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ function findUserPointsHtml(userId: string) {
|
||||||
"%)";
|
"%)";
|
||||||
|
|
||||||
if (!gradedUser.passed) {
|
if (!gradedUser.passed) {
|
||||||
result += ` <span class="my-2 rounded-md bg-error-red-200 px-2.5 py-0.5">${t(
|
result += ` <span class="my-2 rounded-md bg-error-red-200 px-2.5 py-0.5 inline-block leading-5">${t(
|
||||||
"a.Nicht bestanden"
|
"a.Nicht bestanden"
|
||||||
)}</span>`;
|
)}</span>`;
|
||||||
}
|
}
|
||||||
|
|
@ -157,10 +157,11 @@ function findUserPointsHtml(userId: string) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<div
|
<p
|
||||||
v-if="findGradedUser(csu.user_id) && !isPraxisAssignment"
|
v-if="findGradedUser(csu.user_id) && !isPraxisAssignment"
|
||||||
|
class="text-left md:text-right"
|
||||||
v-html="findUserPointsHtml(csu.user_id)"
|
v-html="findUserPointsHtml(csu.user_id)"
|
||||||
></div>
|
></p>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
<template #link>
|
<template #link>
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@ import { useCourseSessionDetailQuery, useCurrentCourseSession } from "@/composab
|
||||||
import SubmissionsOverview from "@/components/cockpit/SubmissionsOverview.vue";
|
import SubmissionsOverview from "@/components/cockpit/SubmissionsOverview.vue";
|
||||||
import { useExpertCockpitStore } from "@/stores/expertCockpit";
|
import { useExpertCockpitStore } from "@/stores/expertCockpit";
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import CockpitDates from "@/components/cockpit/CockpitDates.vue";
|
|
||||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||||
import UserStatusCount from "@/components/cockpit/UserStatusCount.vue";
|
import UserStatusCount from "@/components/cockpit/UserStatusCount.vue";
|
||||||
import { useExpertCockpitPageData } from "@/pages/cockpit/cockpitPage/composables";
|
import { useExpertCockpitPageData } from "@/pages/cockpit/cockpitPage/composables";
|
||||||
|
import CourseSessionDueDatesList from "@/components/dueDates/CourseSessionDueDatesList.vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
courseSlug: string;
|
courseSlug: string;
|
||||||
|
|
@ -105,7 +105,11 @@ const courseSessionDetailResult = useCourseSessionDetailQuery();
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-4 bg-white p-6">
|
<div class="mb-4 bg-white p-6">
|
||||||
<CockpitDates></CockpitDates>
|
<CourseSessionDueDatesList
|
||||||
|
:course-session-id="courseSession.id"
|
||||||
|
:circle-id="expertCockpitStore.currentCircle.id"
|
||||||
|
:max-count="4"
|
||||||
|
></CourseSessionDueDatesList>
|
||||||
</div>
|
</div>
|
||||||
<SubmissionsOverview
|
<SubmissionsOverview
|
||||||
:course-session="courseSession"
|
:course-session="courseSession"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ const circleDocumentsResultData = ref<CircleDocument[]>([]);
|
||||||
let courseSessionDocumentsUrl = "";
|
let courseSessionDocumentsUrl = "";
|
||||||
|
|
||||||
async function fetchDocuments() {
|
async function fetchDocuments() {
|
||||||
const result = await fetchCourseSessionDocuments(courseSession.value?.id);
|
const result: any = await fetchCourseSessionDocuments(courseSession.value?.id);
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
circleDocumentsResultData.value = result;
|
circleDocumentsResultData.value = result;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ log.debug("CompetenceCertificateComponent setup");
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
competenceCertificate: CompetenceCertificate;
|
competenceCertificate: CompetenceCertificate;
|
||||||
detailView: boolean;
|
detailView: boolean;
|
||||||
|
frontendUrl?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const totalPointsEvaluatedAssignments = computed(() => {
|
const totalPointsEvaluatedAssignments = computed(() => {
|
||||||
|
|
@ -40,6 +41,12 @@ const progressStatusCount = computed(() => {
|
||||||
props.competenceCertificate.assignments
|
props.competenceCertificate.assignments
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const frontendUrl = computed(() => {
|
||||||
|
return props.frontendUrl
|
||||||
|
? props.frontendUrl
|
||||||
|
: props.competenceCertificate.frontend_url;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -90,7 +97,7 @@ const progressStatusCount = computed(() => {
|
||||||
|
|
||||||
<div v-if="!props.detailView">
|
<div v-if="!props.detailView">
|
||||||
<router-link
|
<router-link
|
||||||
:to="competenceCertificate.frontend_url"
|
:to="frontendUrl"
|
||||||
class="btn-text mt-4 inline-flex items-center py-2 pl-0"
|
class="btn-text mt-4 inline-flex items-center py-2 pl-0"
|
||||||
:data-cy="`certificate-${competenceCertificate.slug}-detail-link`"
|
:data-cy="`certificate-${competenceCertificate.slug}-detail-link`"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,37 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import { COMPETENCE_NAVI_CERTIFICATE_QUERY } from "@/graphql/queries";
|
|
||||||
import { useQuery } from "@urql/vue";
|
|
||||||
import { computed, onMounted } from "vue";
|
import { computed, onMounted } from "vue";
|
||||||
import type { CompetenceCertificate } from "@/types";
|
import type { CompetenceCertificate } from "@/types";
|
||||||
import { useCurrentCourseSession } from "@/composables";
|
import { useCertificateQuery } from "@/composables";
|
||||||
import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue";
|
import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue";
|
||||||
|
import { getCertificates } from "@/services/competence";
|
||||||
|
import { getPreviousRoute } from "@/router/history";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
courseSlug: string;
|
courseSlug: string;
|
||||||
certificateSlug: string;
|
certificateSlug: string;
|
||||||
|
userId?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
log.debug("CompetenceCertificateDetailPage setup", props);
|
log.debug("CompetenceCertificateDetailPage setup", props);
|
||||||
|
|
||||||
const courseSession = useCurrentCourseSession();
|
const certificatesQuery = useCertificateQuery(
|
||||||
|
props.userId,
|
||||||
const certificatesQuery = useQuery({
|
props.courseSlug
|
||||||
query: COMPETENCE_NAVI_CERTIFICATE_QUERY,
|
).certificatesQuery;
|
||||||
variables: {
|
|
||||||
courseSlug: props.courseSlug,
|
|
||||||
courseSessionId: courseSession.value.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const certificate = computed(() => {
|
const certificate = computed(() => {
|
||||||
|
const certificates = getCertificates(
|
||||||
|
certificatesQuery.data.value,
|
||||||
|
props.userId ?? null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!certificates) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(certificatesQuery.data.value?.competence_certificate_list
|
(certificates.competence_certificates as unknown as CompetenceCertificate[]) ?? []
|
||||||
?.competence_certificates as unknown as CompetenceCertificate[]) ?? []
|
|
||||||
).find((cc) => cc.slug.endsWith(props.certificateSlug));
|
).find((cc) => cc.slug.endsWith(props.certificateSlug));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -40,11 +44,14 @@ onMounted(async () => {
|
||||||
<div class="container-large">
|
<div class="container-large">
|
||||||
<nav class="py-4">
|
<nav class="py-4">
|
||||||
<router-link
|
<router-link
|
||||||
class="btn-text inline-flex items-center pl-0"
|
:to="
|
||||||
:to="`/course/${props.courseSlug}/competence/certificates`"
|
getPreviousRoute() || `/course/${props.courseSlug}/competence/certificates`
|
||||||
|
"
|
||||||
|
class="btn-text inline-flex items-center p-0"
|
||||||
|
data-cy="back-button"
|
||||||
>
|
>
|
||||||
<it-icon-arrow-left />
|
<it-icon-arrow-left class="-ml-1 mr-1 h-5 w-5"></it-icon-arrow-left>
|
||||||
<span>{{ $t("general.back") }}</span>
|
<span class="inline">{{ $t("general.back") }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</nav>
|
</nav>
|
||||||
<div v-if="certificate">
|
<div v-if="certificate">
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,81 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import { COMPETENCE_NAVI_CERTIFICATE_QUERY } from "@/graphql/queries";
|
|
||||||
import { useQuery } from "@urql/vue";
|
|
||||||
import { computed, onMounted } from "vue";
|
import { computed, onMounted } from "vue";
|
||||||
import type { CompetenceCertificate } from "@/types";
|
import type { CompetenceCertificate } from "@/types";
|
||||||
import { useCurrentCourseSession } from "@/composables";
|
import { useCertificateQuery } from "@/composables";
|
||||||
import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue";
|
import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue";
|
||||||
import {
|
import {
|
||||||
assignmentsMaxEvaluationPoints,
|
assignmentsMaxEvaluationPoints,
|
||||||
assignmentsUserPoints,
|
assignmentsUserPoints,
|
||||||
} from "@/pages/competence/utils";
|
} from "@/pages/competence/utils";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import { getCertificates } from "@/services/competence";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
courseSlug: string;
|
courseSlug: string;
|
||||||
|
userId?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
log.debug("CompetenceCertificateListPage setup", props);
|
log.debug("CompetenceCertificateListPage setup", props);
|
||||||
|
|
||||||
const courseSession = useCurrentCourseSession();
|
const route = useRoute();
|
||||||
|
|
||||||
const certificatesQuery = useQuery({
|
const certificatesQuery = useCertificateQuery(
|
||||||
query: COMPETENCE_NAVI_CERTIFICATE_QUERY,
|
props.userId,
|
||||||
variables: {
|
props.courseSlug
|
||||||
courseSlug: props.courseSlug,
|
).certificatesQuery;
|
||||||
courseSessionId: courseSession.value.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const competenceCertificates = computed(() => {
|
const competenceCertificates = computed(() => {
|
||||||
|
const certificates = getCertificates(
|
||||||
|
certificatesQuery.data.value,
|
||||||
|
props.userId ?? null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!certificates) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(certificatesQuery.data.value?.competence_certificate_list
|
(certificates?.competence_certificates as unknown as CompetenceCertificate[]) ?? []
|
||||||
?.competence_certificates as unknown as CompetenceCertificate[]) ?? []
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const assignments = computed(() => {
|
const assignments = computed(() => {
|
||||||
return competenceCertificates.value.flatMap((cc) => cc.assignments);
|
return competenceCertificates?.value?.flatMap((cc) => cc.assignments);
|
||||||
});
|
});
|
||||||
|
|
||||||
const totalPointsEvaluatedAssignments = computed(() => {
|
const totalPointsEvaluatedAssignments = computed(() => {
|
||||||
return assignmentsMaxEvaluationPoints(assignments.value);
|
return assignmentsMaxEvaluationPoints(assignments.value ?? []);
|
||||||
});
|
});
|
||||||
|
|
||||||
const userPointsEvaluatedAssignments = computed(() => {
|
const userPointsEvaluatedAssignments = computed(() => {
|
||||||
return assignmentsUserPoints(assignments.value);
|
return assignmentsUserPoints(assignments.value ?? []);
|
||||||
});
|
});
|
||||||
|
|
||||||
const numAssignmentsEvaluated = computed(() => {
|
const numAssignmentsEvaluated = computed(() => {
|
||||||
return assignments.value.filter((a) => {
|
return (assignments.value ?? []).filter((a) => {
|
||||||
return a.completion?.completion_status === "EVALUATION_SUBMITTED";
|
return a.completion?.completion_status === "EVALUATION_SUBMITTED";
|
||||||
}).length;
|
}).length;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const certificateFrontendUrl = function (frontendUrl: string) {
|
||||||
|
if (props.userId) {
|
||||||
|
const pathSegments = frontendUrl.split("/");
|
||||||
|
const lastSegment = pathSegments[pathSegments.length - 1];
|
||||||
|
|
||||||
|
// Assuming you want to navigate to the current path + last segment
|
||||||
|
return `${route.path}/${lastSegment}`;
|
||||||
|
}
|
||||||
|
return frontendUrl;
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// log.debug("AssignmentView mounted", props.assignmentId, props.userId);
|
// log.debug("AssignmentView mounted", props.assignmentId, props.userId);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="container-large">
|
<div v-if="assignments" class="container-large">
|
||||||
<h2 class="mb-4 lg:py-4">{{ $t("a.Kompetenznachweise") }}</h2>
|
<h2 class="mb-4 lg:py-4">{{ $t("a.Kompetenznachweise") }}</h2>
|
||||||
|
|
||||||
<div class="mb-4 bg-white p-8" data-cy="certificate-total-points-text">
|
<div class="mb-4 bg-white p-8" data-cy="certificate-total-points-text">
|
||||||
|
|
@ -92,6 +109,7 @@ onMounted(async () => {
|
||||||
<CompetenceCertificateComponent
|
<CompetenceCertificateComponent
|
||||||
:competence-certificate="competenceCertificate"
|
:competence-certificate="competenceCertificate"
|
||||||
:detail-view="false"
|
:detail-view="false"
|
||||||
|
:frontend-url="certificateFrontendUrl(competenceCertificate.frontend_url)"
|
||||||
></CompetenceCertificateComponent>
|
></CompetenceCertificateComponent>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
||||||
|
import log from "loglevel";
|
||||||
|
import { useDashboardPersonsDueDates } from "@/composables";
|
||||||
|
import { computed } from "vue";
|
||||||
|
import _ from "lodash";
|
||||||
|
import DueDateSingle from "@/components/dueDates/DueDateSingle.vue";
|
||||||
|
|
||||||
|
log.debug("DashboardAsideWidget created");
|
||||||
|
|
||||||
|
const { loading, dashboardPersons, dashboardDueDates } = useDashboardPersonsDueDates();
|
||||||
|
|
||||||
|
const displayDueDates = computed(() => {
|
||||||
|
return _.take(dashboardDueDates.value, 6);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="loading" class="m-8 flex justify-center">
|
||||||
|
<LoadingSpinner />
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<section class="border-b p-8">
|
||||||
|
<h3 class="mb-4">{{ dashboardPersons.length }} {{ $t("a.Personen") }}</h3>
|
||||||
|
<div>
|
||||||
|
<router-link class="btn-secondary" to="/dashboard/persons">
|
||||||
|
{{ $t("a.Alle Personen anzeigen") }}
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section v-if="dashboardDueDates.length > 0" class="p-8">
|
||||||
|
<h3>{{ $t("a.Termine") }}</h3>
|
||||||
|
|
||||||
|
<div v-for="dueDate in displayDueDates" :key="dueDate.id">
|
||||||
|
<div class="border-b">
|
||||||
|
<DueDateSingle :due-date="dueDate" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<router-link class="btn-secondary mt-4" to="/dashboard/due-dates">
|
||||||
|
{{ $t("a.Alle Termine anzeigen") }}
|
||||||
|
</router-link>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,268 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
||||||
|
import log from "loglevel";
|
||||||
|
import { useDashboardPersonsDueDates } from "@/composables";
|
||||||
|
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||||
|
import { computed, ref, watch } from "vue";
|
||||||
|
import { useTranslation } from "i18next-vue";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
|
import DueDateSingle from "@/components/dueDates/DueDateSingle.vue";
|
||||||
|
import { getPreviousRoute } from "@/router/history";
|
||||||
|
|
||||||
|
log.debug("DashboardPersonsPage created");
|
||||||
|
|
||||||
|
const UNFILTERED = "0";
|
||||||
|
|
||||||
|
type DropboxItem = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CourseItem = DropboxItem & {
|
||||||
|
slug: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const { loading, currentDueDates: dashboardDueDates } = useDashboardPersonsDueDates();
|
||||||
|
|
||||||
|
const courses = computed(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: UNFILTERED,
|
||||||
|
name: `${t("Lehrgang")}: ${t("a.Alle")}`,
|
||||||
|
slug: "",
|
||||||
|
},
|
||||||
|
..._(dashboardDueDates.value)
|
||||||
|
.map((dueDate) => {
|
||||||
|
return {
|
||||||
|
name: dueDate.course_session.course_title,
|
||||||
|
id: dueDate.course_session.course_id,
|
||||||
|
slug: dueDate.course_session.course_slug,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.uniqBy("id")
|
||||||
|
.orderBy("name")
|
||||||
|
.value(),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedCourse = ref<CourseItem>(courses.value[0]);
|
||||||
|
|
||||||
|
const courseSessions = computed(() => {
|
||||||
|
let sessions = _(dashboardDueDates.value)
|
||||||
|
.map((dueDate) => {
|
||||||
|
return Object.assign({}, dueDate.course_session, {
|
||||||
|
name: dueDate.course_session.session_title,
|
||||||
|
id: dueDate.course_session.id,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.uniqBy("id")
|
||||||
|
.orderBy("name")
|
||||||
|
.value();
|
||||||
|
|
||||||
|
// filter by selected course
|
||||||
|
if (selectedCourse.value.id !== UNFILTERED) {
|
||||||
|
sessions = sessions.filter((cs) => cs.course_id === selectedCourse.value.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: UNFILTERED,
|
||||||
|
name: `${t("Durchführung")}: ${t("a.Alle")}`,
|
||||||
|
},
|
||||||
|
...sessions,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedSessionRouteQuery = useRouteQuery("session", UNFILTERED, {
|
||||||
|
mode: "replace",
|
||||||
|
});
|
||||||
|
const selectedSession = ref<DropboxItem>(courseSessions.value[0]);
|
||||||
|
|
||||||
|
watch(selectedSession, () => {
|
||||||
|
// @ts-ignore
|
||||||
|
selectedSessionRouteQuery.value = selectedSession.value.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(courseSessions, () => {
|
||||||
|
if (courseSessions.value.length > 0 && selectedSessionRouteQuery.value) {
|
||||||
|
// preselect session from route query
|
||||||
|
const selectedSessionFromRoute = courseSessions.value.find(
|
||||||
|
(cs) => cs.id === selectedSessionRouteQuery.value
|
||||||
|
);
|
||||||
|
if (selectedSessionFromRoute) {
|
||||||
|
selectedSession.value = selectedSessionFromRoute;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedSession.value = courseSessions.value[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
const filteredDueDates = computed(() => {
|
||||||
|
return _.orderBy(
|
||||||
|
dashboardDueDates.value
|
||||||
|
.filter((dueDate) => {
|
||||||
|
if (selectedCourse.value.id === UNFILTERED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return dueDate.course_session.course_id === selectedCourse.value.id;
|
||||||
|
})
|
||||||
|
.filter((dueDate) => {
|
||||||
|
if (selectedSession.value.id === UNFILTERED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return dueDate.course_session.id === selectedSession.value.id;
|
||||||
|
})
|
||||||
|
.filter((dueDate) => {
|
||||||
|
if (selectedCircle.value.id === UNFILTERED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return dueDate.circle?.id === selectedCircle.value.id;
|
||||||
|
})
|
||||||
|
.filter((dueDate) => {
|
||||||
|
if (selectedType.value.id === UNFILTERED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return dueDate.translatedType === selectedType.value.id;
|
||||||
|
}),
|
||||||
|
|
||||||
|
["start"]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const filtersVisible = computed(() => {
|
||||||
|
return (
|
||||||
|
courses.value.length > 2 ||
|
||||||
|
courseSessions.value.length > 2 ||
|
||||||
|
circles.value.length > 2 ||
|
||||||
|
dueDateTypes.value.length > 2
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const circles = computed(() => {
|
||||||
|
const dueDatesBySelectedCourse = dashboardDueDates.value.filter((dueDate) => {
|
||||||
|
if (selectedCourse.value.id === UNFILTERED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return dueDate.course_session.course_id === selectedCourse.value.id;
|
||||||
|
});
|
||||||
|
const circleList = _(dueDatesBySelectedCourse)
|
||||||
|
.filter((dueDate) => {
|
||||||
|
return !!dueDate.circle;
|
||||||
|
})
|
||||||
|
.map((dueDate) => {
|
||||||
|
return {
|
||||||
|
name: dueDate.circle?.title ?? "",
|
||||||
|
id: dueDate.circle?.id ?? "",
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.uniqBy("id")
|
||||||
|
.orderBy("name")
|
||||||
|
.value();
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: UNFILTERED,
|
||||||
|
name: `${t("Circle")}: ${t("a.Alle")}`,
|
||||||
|
},
|
||||||
|
...circleList,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
const selectedCircle = ref<DropboxItem>(circles.value[0]);
|
||||||
|
|
||||||
|
const dueDateTypes = computed(() => {
|
||||||
|
const types = _(dashboardDueDates.value)
|
||||||
|
.map((dueDate) => {
|
||||||
|
return {
|
||||||
|
name: dueDate.translatedType,
|
||||||
|
id: dueDate.translatedType,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.uniqBy("id")
|
||||||
|
.orderBy("name")
|
||||||
|
.value();
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: UNFILTERED,
|
||||||
|
name: t("a.AlleTypen"),
|
||||||
|
},
|
||||||
|
...types,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedType = ref<DropboxItem>(dueDateTypes.value[0]);
|
||||||
|
|
||||||
|
watch(selectedCourse, async () => {
|
||||||
|
selectedSession.value = courseSessions.value[0];
|
||||||
|
selectedCircle.value = circles.value[0];
|
||||||
|
selectedType.value = dueDateTypes.value[0];
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="loading" class="m-8 flex justify-center">
|
||||||
|
<LoadingSpinner />
|
||||||
|
</div>
|
||||||
|
<div v-else class="bg-gray-200">
|
||||||
|
<div class="container-large">
|
||||||
|
<router-link
|
||||||
|
:to="getPreviousRoute() || '/'"
|
||||||
|
class="btn-text inline-flex items-center p-0"
|
||||||
|
data-cy="back-button"
|
||||||
|
>
|
||||||
|
<it-icon-arrow-left class="-ml-1 mr-1 h-5 w-5"></it-icon-arrow-left>
|
||||||
|
<span class="inline">{{ $t("general.back") }}</span>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<h2 class="my-4">{{ $t("a.Termine") }}</h2>
|
||||||
|
<div class="bg-white px-4 py-2">
|
||||||
|
<section
|
||||||
|
v-if="filtersVisible"
|
||||||
|
class="flex flex-col space-x-0 border-b bg-white lg:flex-row lg:space-x-3"
|
||||||
|
>
|
||||||
|
<ItDropdownSelect
|
||||||
|
v-model="selectedCourse"
|
||||||
|
data-cy="select-course"
|
||||||
|
:items="courses"
|
||||||
|
borderless
|
||||||
|
></ItDropdownSelect>
|
||||||
|
|
||||||
|
<ItDropdownSelect
|
||||||
|
v-model="selectedSession"
|
||||||
|
data-cy="select-session"
|
||||||
|
:items="courseSessions"
|
||||||
|
borderless
|
||||||
|
></ItDropdownSelect>
|
||||||
|
|
||||||
|
<ItDropdownSelect
|
||||||
|
v-model="selectedCircle"
|
||||||
|
data-cy="select-circle"
|
||||||
|
:items="circles"
|
||||||
|
borderless
|
||||||
|
></ItDropdownSelect>
|
||||||
|
|
||||||
|
<ItDropdownSelect
|
||||||
|
v-model="selectedType"
|
||||||
|
data-cy="select-type"
|
||||||
|
:items="dueDateTypes"
|
||||||
|
borderless
|
||||||
|
></ItDropdownSelect>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section data-cy="due-date-list">
|
||||||
|
<div v-for="dueDate in filteredDueDates" :key="dueDate.id" class="border-b">
|
||||||
|
<DueDateSingle :single-line="true" :due-date="dueDate"></DueDateSingle>
|
||||||
|
</div>
|
||||||
|
<div v-if="!filteredDueDates.length" class="mt-4">
|
||||||
|
<p>{{ $t("dueDates.noDueDatesAvailable") }}</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -1,33 +1,21 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Component } from "vue";
|
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
import StatisticPage from "@/pages/dashboard/StatisticPage.vue";
|
|
||||||
import ProgressPage from "@/pages/dashboard/ProgressPage.vue";
|
|
||||||
import SimpleDates from "@/components/dashboard/SimpleDates.vue";
|
|
||||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
|
||||||
import { useDashboardStore } from "@/stores/dashboard";
|
import { useDashboardStore } from "@/stores/dashboard";
|
||||||
import type { DashboardType } from "@/gql/graphql";
|
import type { DashboardCourseConfigType } from "@/services/dashboard";
|
||||||
import SimpleCoursePage from "@/pages/dashboard/SimpleCoursePage.vue";
|
|
||||||
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
||||||
import CourseDetailDates from "@/components/dashboard/CourseDetailDates.vue";
|
|
||||||
import NoCourseSession from "@/components/dashboard/NoCourseSession.vue";
|
import NoCourseSession from "@/components/dashboard/NoCourseSession.vue";
|
||||||
import MentorPage from "@/pages/dashboard/MentorPage.vue";
|
import CoursePanel from "@/components/dashboard/CoursePanel.vue";
|
||||||
|
import DashboardAsideWidget from "@/pages/dashboard/DashboardAsideWidget.vue";
|
||||||
|
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
|
|
||||||
interface DashboardPage {
|
onMounted(async () => {
|
||||||
main: Component;
|
await dashboardStore.loadDashboardDetails();
|
||||||
aside: Component;
|
});
|
||||||
|
|
||||||
|
function newDashboardConfigForId(id: string): DashboardCourseConfigType | undefined {
|
||||||
|
return dashboardStore.dashboardConfigsv2.find((config) => config.course_id == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const boards: Record<DashboardType, DashboardPage> = {
|
|
||||||
PROGRESS_DASHBOARD: { main: ProgressPage, aside: SimpleDates },
|
|
||||||
SIMPLE_DASHBOARD: { main: SimpleCoursePage, aside: SimpleDates },
|
|
||||||
STATISTICS_DASHBOARD: { main: StatisticPage, aside: CourseDetailDates },
|
|
||||||
MENTOR_DASHBOARD: { main: MentorPage, aside: SimpleDates },
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(dashboardStore.loadDashboardDetails);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -35,30 +23,24 @@ onMounted(dashboardStore.loadDashboardDetails);
|
||||||
<LoadingSpinner />
|
<LoadingSpinner />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else-if="dashboardStore.currentDashboardConfig"
|
v-else-if="dashboardStore.dashboardConfigsv2.length"
|
||||||
class="flex flex-col lg:flex-row"
|
class="flex flex-col lg:flex-row"
|
||||||
>
|
>
|
||||||
<main class="grow bg-gray-200 lg:order-2">
|
<main class="grow bg-gray-200 lg:order-2">
|
||||||
<div class="m-8">
|
<div class="m-8">
|
||||||
<div class="mb-10 flex items-center justify-between">
|
<!-- new way of dashboard -->
|
||||||
<h1 data-cy="dashboard-title">Dashboard</h1>
|
<ul>
|
||||||
<ItDropdownSelect
|
<li
|
||||||
:model-value="dashboardStore.currentDashboardConfig"
|
v-for="config in dashboardStore.dashboardConfigsv2"
|
||||||
class="mt-4 w-full lg:mt-0 lg:w-96"
|
:key="config.course_id"
|
||||||
:items="dashboardStore.dashboardConfigs"
|
>
|
||||||
@update:model-value="dashboardStore.switchAndLoadDashboardConfig"
|
<CoursePanel :course-config="newDashboardConfigForId(config.course_id)" />
|
||||||
></ItDropdownSelect>
|
</li>
|
||||||
</div>
|
</ul>
|
||||||
|
|
||||||
<component
|
|
||||||
:is="boards[dashboardStore.currentDashboardConfig.dashboard_type].main"
|
|
||||||
></component>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<aside class="m-8 lg:order-1 lg:w-[343px]">
|
<aside class="lg:order-2 lg:w-[384px] xl:w-[512px]">
|
||||||
<component
|
<DashboardAsideWidget />
|
||||||
:is="boards[dashboardStore.currentDashboardConfig.dashboard_type].aside"
|
|
||||||
></component>
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
<NoCourseSession v-else class="container-medium mt-14" />
|
<NoCourseSession v-else class="container-medium mt-14" />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,394 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
||||||
|
import log from "loglevel";
|
||||||
|
import { useDashboardPersonsDueDates } from "@/composables";
|
||||||
|
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||||
|
import { computed, ref, watch } from "vue";
|
||||||
|
import { useTranslation } from "i18next-vue";
|
||||||
|
import _ from "lodash";
|
||||||
|
import type { DashboardPersonCourseSessionType } from "@/services/dashboard";
|
||||||
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
|
import type { DashboardPersonsPageMode } from "@/types";
|
||||||
|
|
||||||
|
log.debug("DashboardPersonsPage created");
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
mode?: DashboardPersonsPageMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
mode: "default",
|
||||||
|
});
|
||||||
|
|
||||||
|
const UNFILTERED = Number.MAX_SAFE_INTEGER.toString();
|
||||||
|
|
||||||
|
type MenuItem = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const { loading, dashboardPersons } = useDashboardPersonsDueDates(props.mode);
|
||||||
|
|
||||||
|
const courses = computed(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: UNFILTERED,
|
||||||
|
name: `${t("Lehrgang")}: ${t("a.Alle")}`,
|
||||||
|
},
|
||||||
|
..._(dashboardPersons.value)
|
||||||
|
.flatMap((person) => person.course_sessions)
|
||||||
|
.map((cs) => {
|
||||||
|
return { name: cs.course_title, id: cs.course_id };
|
||||||
|
})
|
||||||
|
.uniqBy("id")
|
||||||
|
.orderBy("name")
|
||||||
|
.value(),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
const selectedCourse = ref<MenuItem>(courses.value[0]);
|
||||||
|
const selectedCourseRouteQuery = useRouteQuery("course", UNFILTERED, {
|
||||||
|
mode: "replace",
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(selectedCourse, () => {
|
||||||
|
selectedCourseRouteQuery.value = selectedCourse.value.id;
|
||||||
|
});
|
||||||
|
watch(courses, () => {
|
||||||
|
if (selectedCourseRouteQuery.value !== UNFILTERED) {
|
||||||
|
selectedCourse.value =
|
||||||
|
courses.value.find((course) => course.id === selectedCourseRouteQuery.value) ||
|
||||||
|
courses.value[0];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const regions = computed(() => {
|
||||||
|
let values = _(dashboardPersons.value)
|
||||||
|
.flatMap((person) => person.course_sessions)
|
||||||
|
.map((cs) => {
|
||||||
|
return Object.assign({}, cs, { name: cs.region, id: cs.region });
|
||||||
|
})
|
||||||
|
.filter((cs) => !!cs.region)
|
||||||
|
.uniqBy("id")
|
||||||
|
.orderBy("name")
|
||||||
|
.value();
|
||||||
|
|
||||||
|
// filter by selected course
|
||||||
|
if (selectedCourse.value.id !== UNFILTERED) {
|
||||||
|
values = values.filter((cs) => cs.course_id === selectedCourse.value.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: UNFILTERED,
|
||||||
|
name: `${t("Region")}: ${t("a.Alle")}`,
|
||||||
|
},
|
||||||
|
...values,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
const selectedRegion = ref<MenuItem>(regions.value[0]);
|
||||||
|
|
||||||
|
const courseSessions = computed(() => {
|
||||||
|
let values = _(dashboardPersons.value)
|
||||||
|
.flatMap((person) => person.course_sessions)
|
||||||
|
.map((cs) => {
|
||||||
|
return Object.assign({}, cs, { name: cs.session_title, id: cs.id });
|
||||||
|
})
|
||||||
|
.uniqBy("id")
|
||||||
|
.orderBy("name")
|
||||||
|
.value();
|
||||||
|
|
||||||
|
// filter by selected course
|
||||||
|
if (selectedCourse.value.id !== UNFILTERED) {
|
||||||
|
values = values.filter((cs) => cs.course_id === selectedCourse.value.id);
|
||||||
|
}
|
||||||
|
// filter by selected region
|
||||||
|
if (selectedRegion.value.id !== UNFILTERED) {
|
||||||
|
values = values.filter((cs) => cs.region === selectedRegion.value.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: UNFILTERED,
|
||||||
|
name: `${t("Durchführung")}: ${t("a.Alle")}`,
|
||||||
|
},
|
||||||
|
...values,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
const selectedSession = ref<MenuItem>(courseSessions.value[0]);
|
||||||
|
|
||||||
|
const generations = computed(() => {
|
||||||
|
const values = _(dashboardPersons.value)
|
||||||
|
.flatMap((person) => person.course_sessions)
|
||||||
|
.map((cs) => {
|
||||||
|
return Object.assign({}, cs, { name: cs.generation, id: cs.generation });
|
||||||
|
})
|
||||||
|
.filter((cs) => !!cs.generation)
|
||||||
|
.uniqBy("id")
|
||||||
|
.orderBy("name")
|
||||||
|
.value();
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: UNFILTERED,
|
||||||
|
name: `${t("Generation")}: ${t("a.Alle")}`,
|
||||||
|
},
|
||||||
|
...values,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
const selectedGeneration = ref<MenuItem>(generations.value[0]);
|
||||||
|
|
||||||
|
const roles = computed(() => {
|
||||||
|
const values = _(dashboardPersons.value)
|
||||||
|
.flatMap((person) => person.course_sessions)
|
||||||
|
.map((cs) => {
|
||||||
|
return Object.assign({}, cs, {
|
||||||
|
name: cs.user_role_display,
|
||||||
|
id: cs.user_role_display,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.uniqBy("id")
|
||||||
|
.orderBy("name")
|
||||||
|
.value();
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: "",
|
||||||
|
name: `${t("Rolle")}: ${t("a.Alle")}`,
|
||||||
|
},
|
||||||
|
...values,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
const selectedRole = ref<MenuItem>(roles.value[0]);
|
||||||
|
|
||||||
|
const filteredPersons = computed(() => {
|
||||||
|
return _.orderBy(
|
||||||
|
dashboardPersons.value
|
||||||
|
.filter((person) => {
|
||||||
|
if (selectedCourse.value.id === UNFILTERED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return person.course_sessions.some(
|
||||||
|
(cs) => cs.course_id === selectedCourse.value.id
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.filter((person) => {
|
||||||
|
if (selectedSession.value.id === UNFILTERED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return person.course_sessions.some((cs) => cs.id === selectedSession.value.id);
|
||||||
|
})
|
||||||
|
.filter((person) => {
|
||||||
|
if (selectedRegion.value.id === UNFILTERED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return person.course_sessions.some(
|
||||||
|
(cs) => cs.region === selectedRegion.value.id
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.filter((person) => {
|
||||||
|
if (selectedGeneration.value.id === UNFILTERED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return person.course_sessions.some(
|
||||||
|
(cs) => cs.generation === selectedGeneration.value.id
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.filter((person) => {
|
||||||
|
if (selectedRole.value.id === "") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return person.course_sessions.some(
|
||||||
|
(cs) => cs.user_role_display === selectedRole.value.id
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
["last_name", "first_name"]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const filtersVisible = computed(() => {
|
||||||
|
return (
|
||||||
|
courses.value.length > 2 ||
|
||||||
|
courseSessions.value.length > 2 ||
|
||||||
|
regions.value.length > 2 ||
|
||||||
|
generations.value.length > 2 ||
|
||||||
|
roles.value.length > 2
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function personRoleDisplayValue(personCourseSession: DashboardPersonCourseSessionType) {
|
||||||
|
if (
|
||||||
|
["SUPERVISOR", "EXPERT", "LEARNING_MENTOR"].includes(personCourseSession.user_role)
|
||||||
|
) {
|
||||||
|
return personCourseSession.user_role_display;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(selectedCourse, () => {
|
||||||
|
selectedRegion.value = regions.value[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(selectedRegion, () => {
|
||||||
|
selectedSession.value = courseSessions.value[0];
|
||||||
|
selectedGeneration.value = generations.value[0];
|
||||||
|
selectedRole.value = roles.value[0];
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="loading" class="m-8 flex justify-center">
|
||||||
|
<LoadingSpinner />
|
||||||
|
</div>
|
||||||
|
<div v-else class="bg-gray-200">
|
||||||
|
<div class="container-large">
|
||||||
|
<router-link
|
||||||
|
:to="`/`"
|
||||||
|
class="btn-text inline-flex items-center p-0"
|
||||||
|
data-cy="back-to-learning-path-button"
|
||||||
|
>
|
||||||
|
<it-icon-arrow-left class="-ml-1 mr-1 h-5 w-5"></it-icon-arrow-left>
|
||||||
|
<span class="inline">{{ $t("general.back") }}</span>
|
||||||
|
</router-link>
|
||||||
|
<h2 class="my-4">{{ $t("a.Personen") }}</h2>
|
||||||
|
<div class="bg-white px-4 py-2">
|
||||||
|
<section
|
||||||
|
v-if="filtersVisible"
|
||||||
|
class="flex flex-col space-x-0 border-b bg-white lg:flex-row lg:space-x-3"
|
||||||
|
>
|
||||||
|
<ItDropdownSelect
|
||||||
|
v-if="courses.length > 2"
|
||||||
|
v-model="selectedCourse"
|
||||||
|
data-cy="select-course"
|
||||||
|
:items="courses"
|
||||||
|
borderless
|
||||||
|
></ItDropdownSelect>
|
||||||
|
|
||||||
|
<ItDropdownSelect
|
||||||
|
v-if="regions.length > 2"
|
||||||
|
v-model="selectedRegion"
|
||||||
|
data-cy="select-region"
|
||||||
|
:items="regions"
|
||||||
|
borderless
|
||||||
|
></ItDropdownSelect>
|
||||||
|
|
||||||
|
<ItDropdownSelect
|
||||||
|
v-if="courseSessions.length > 2"
|
||||||
|
v-model="selectedSession"
|
||||||
|
data-cy="select-course"
|
||||||
|
:items="courseSessions"
|
||||||
|
borderless
|
||||||
|
></ItDropdownSelect>
|
||||||
|
|
||||||
|
<ItDropdownSelect
|
||||||
|
v-if="generations.length > 2"
|
||||||
|
v-model="selectedGeneration"
|
||||||
|
data-cy="select-generation"
|
||||||
|
:items="generations"
|
||||||
|
borderless
|
||||||
|
></ItDropdownSelect>
|
||||||
|
|
||||||
|
<ItDropdownSelect
|
||||||
|
v-if="roles.length > 2"
|
||||||
|
v-model="selectedRole"
|
||||||
|
data-cy="select-role"
|
||||||
|
:items="roles"
|
||||||
|
borderless
|
||||||
|
></ItDropdownSelect>
|
||||||
|
</section>
|
||||||
|
<div
|
||||||
|
v-for="person in filteredPersons"
|
||||||
|
:key="person.user_id"
|
||||||
|
data-cy="person"
|
||||||
|
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">
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<img
|
||||||
|
class="inline-block h-11 w-11 rounded-full"
|
||||||
|
:src="
|
||||||
|
person.avatar_url_small ||
|
||||||
|
'/static/avatars/myvbv-default-avatar.png'
|
||||||
|
"
|
||||||
|
:alt="`${person.first_name} ${person.last_name}`"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<div class="text-bold">
|
||||||
|
{{ person.first_name }}
|
||||||
|
{{ person.last_name }}
|
||||||
|
</div>
|
||||||
|
<div class="text-gray-900">{{ person.email }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full flex-auto items-start md:w-2/3">
|
||||||
|
<div
|
||||||
|
v-for="cs in person.course_sessions"
|
||||||
|
:key="cs.id"
|
||||||
|
class="w-full border-b pb-2 pt-2 first:pt-0 last:border-b-0 last:pb-0"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col md:flex-row md:items-center">
|
||||||
|
<div v-if="props.mode === 'default'" class="md:w-1/2">
|
||||||
|
<div class="text-gray-900">{{ cs.course_title }}</div>
|
||||||
|
<div v-if="cs.is_uk">{{ cs.session_title }}</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="props.mode === 'competenceMetrics'" class="md:w-1/3">
|
||||||
|
<div
|
||||||
|
v-if="cs.competence_metrics?.passed_count || 0 > 0"
|
||||||
|
class="my-2 w-fit rounded-md bg-green-200 px-2.5 py-0.5"
|
||||||
|
>
|
||||||
|
{{ $t("a.Bestanden") }}:
|
||||||
|
{{ cs.competence_metrics?.passed_count }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="props.mode === 'default'" class="md:w-1/4">
|
||||||
|
{{ personRoleDisplayValue(cs) }}
|
||||||
|
</div>
|
||||||
|
<div v-else-if="props.mode === 'competenceMetrics'" class="md:w-1/3">
|
||||||
|
<div
|
||||||
|
v-if="cs.competence_metrics?.failed_count || 0 > 0"
|
||||||
|
class="my-2 w-fit rounded-md bg-error-red-200 px-2.5 py-0.5"
|
||||||
|
>
|
||||||
|
{{ $t("a.Nicht Bestanden") }}:
|
||||||
|
{{ cs.competence_metrics?.failed_count }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="md:w-1/4 md:text-right">
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
(['SUPERVISOR', 'EXPERT'].includes(cs.my_role) &&
|
||||||
|
cs.user_role === 'MEMBER') ||
|
||||||
|
(cs.my_role === 'LEARNING_MENTOR' &&
|
||||||
|
cs.user_role === 'LEARNING_MENTEE')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<router-link
|
||||||
|
:to="{
|
||||||
|
name: 'profileLearningPath',
|
||||||
|
params: {
|
||||||
|
userId: person.user_id,
|
||||||
|
courseSlug: cs.course_slug,
|
||||||
|
},
|
||||||
|
query: { courseSessionId: cs.id },
|
||||||
|
}"
|
||||||
|
class="link w-full lg:text-right"
|
||||||
|
>
|
||||||
|
{{ $t("a.Profil anzeigen") }}
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -1,25 +1,21 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useDashboardStore } from "@/stores/dashboard";
|
|
||||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
|
||||||
import { computed } from "vue";
|
|
||||||
import type {
|
import type {
|
||||||
AssignmentCompletionMetricsType,
|
AssignmentCompletionMetricsType,
|
||||||
AssignmentStatisticsRecordType,
|
AssignmentStatisticsRecordType,
|
||||||
CourseStatisticsType,
|
CourseStatisticsType,
|
||||||
|
StatisticsCircleDataType,
|
||||||
} from "@/gql/graphql";
|
} from "@/gql/graphql";
|
||||||
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
|
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
|
||||||
import { useCourseStatistics } from "@/composables";
|
|
||||||
import ItProgress from "@/components/ui/ItProgress.vue";
|
|
||||||
import { getDateString } from "@/components/dueDates/dueDatesUtils";
|
import { getDateString } from "@/components/dueDates/dueDatesUtils";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import ItProgress from "@/components/ui/ItProgress.vue";
|
||||||
|
|
||||||
const dashboardStore = useDashboardStore();
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const props = defineProps<{
|
||||||
const statistics = computed(() => {
|
courseStatistics: CourseStatisticsType;
|
||||||
return dashboardStore.currentDashBoardData as CourseStatisticsType;
|
courseSessionName: (sessionId: string) => string;
|
||||||
});
|
circleMeta: (circleId: string) => StatisticsCircleDataType;
|
||||||
|
}>();
|
||||||
const { courseSessionName, circleMeta } = useCourseStatistics();
|
|
||||||
|
|
||||||
const assignmentStats = (metrics: AssignmentCompletionMetricsType) => {
|
const assignmentStats = (metrics: AssignmentCompletionMetricsType) => {
|
||||||
if (!metrics.ranking_completed) {
|
if (!metrics.ranking_completed) {
|
||||||
|
|
@ -43,20 +39,14 @@ const total = (metrics: AssignmentCompletionMetricsType) => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main v-if="statistics">
|
<main>
|
||||||
<div class="mb-10 flex items-center justify-between">
|
<div class="mb-10 flex items-center justify-between">
|
||||||
<h3>{{ $t("a.Kompetenznachweis-Elemente") }}</h3>
|
<h3>{{ $t("a.Kompetenznachweis-Elemente") }}</h3>
|
||||||
<ItDropdownSelect
|
|
||||||
:model-value="dashboardStore.currentDashboardConfig"
|
|
||||||
class="mt-4 w-full lg:mt-0 lg:w-96"
|
|
||||||
:items="dashboardStore.dashboardConfigs"
|
|
||||||
@update:model-value="dashboardStore.switchAndLoadDashboardConfig"
|
|
||||||
></ItDropdownSelect>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="statistics.assignments.records" class="mt-8 bg-white">
|
<div v-if="courseStatistics?.assignments.records" class="mt-8 bg-white">
|
||||||
<StatisticFilterList
|
<StatisticFilterList
|
||||||
:course-session-properties="statistics.course_session_properties"
|
:course-session-properties="courseStatistics?.course_session_properties"
|
||||||
:items="statistics.assignments.records"
|
:items="courseStatistics.assignments.records"
|
||||||
>
|
>
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
|
|
@ -89,8 +79,8 @@ const total = (metrics: AssignmentCompletionMetricsType) => {
|
||||||
<div v-else>Noch nicht bestätigt</div>
|
<div v-else>Noch nicht bestätigt</div>
|
||||||
<ItProgress
|
<ItProgress
|
||||||
:status-count="
|
:status-count="
|
||||||
assignmentStats((item as AssignmentStatisticsRecordType).metrics)
|
assignmentStats((item as AssignmentStatisticsRecordType).metrics)
|
||||||
"
|
"
|
||||||
></ItProgress>
|
></ItProgress>
|
||||||
<router-link
|
<router-link
|
||||||
class="underline"
|
class="underline"
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,20 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useDashboardStore } from "@/stores/dashboard";
|
import type {
|
||||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
CourseStatisticsType,
|
||||||
import { computed } from "vue";
|
PresenceRecordStatisticsType,
|
||||||
import type { CourseStatisticsType, PresenceRecordStatisticsType } from "@/gql/graphql";
|
StatisticsCircleDataType,
|
||||||
|
} from "@/gql/graphql";
|
||||||
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
|
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
|
||||||
import ItProgress from "@/components/ui/ItProgress.vue";
|
import ItProgress from "@/components/ui/ItProgress.vue";
|
||||||
import { useCourseStatistics } from "@/composables";
|
|
||||||
import { getDateString } from "@/components/dueDates/dueDatesUtils";
|
import { getDateString } from "@/components/dueDates/dueDatesUtils";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
const dashboardStore = useDashboardStore();
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const props = defineProps<{
|
||||||
const statistics = computed(() => {
|
courseStatistics: CourseStatisticsType;
|
||||||
return dashboardStore.currentDashBoardData as CourseStatisticsType;
|
courseSessionName: (sessionId: string) => string;
|
||||||
});
|
circleMeta: (circleId: string) => StatisticsCircleDataType;
|
||||||
|
}>();
|
||||||
const { courseSessionName, circleMeta } = useCourseStatistics();
|
|
||||||
|
|
||||||
const attendanceStats = (present: number, total: number) => {
|
const attendanceStats = (present: number, total: number) => {
|
||||||
return {
|
return {
|
||||||
|
|
@ -27,20 +26,17 @@ const attendanceStats = (present: number, total: number) => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main v-if="statistics">
|
<main>
|
||||||
<div class="mb-10 flex items-center justify-between">
|
<div class="mb-10 flex items-center justify-between">
|
||||||
<h3>{{ $t("Anwesenheit") }}</h3>
|
<h3>{{ $t("Anwesenheit") }}</h3>
|
||||||
<ItDropdownSelect
|
|
||||||
:model-value="dashboardStore.currentDashboardConfig"
|
|
||||||
class="mt-4 w-full lg:mt-0 lg:w-96"
|
|
||||||
:items="dashboardStore.dashboardConfigs"
|
|
||||||
@update:model-value="dashboardStore.switchAndLoadDashboardConfig"
|
|
||||||
></ItDropdownSelect>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="statistics.attendance_day_presences.records" class="mt-8 bg-white">
|
<div
|
||||||
|
v-if="courseStatistics?.attendance_day_presences.records"
|
||||||
|
class="mt-8 bg-white"
|
||||||
|
>
|
||||||
<StatisticFilterList
|
<StatisticFilterList
|
||||||
:course-session-properties="statistics.course_session_properties"
|
:course-session-properties="courseStatistics.course_session_properties"
|
||||||
:items="statistics.attendance_day_presences.records"
|
:items="courseStatistics.attendance_day_presences.records"
|
||||||
>
|
>
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,28 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useDashboardStore } from "@/stores/dashboard";
|
|
||||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
|
||||||
import { computed } from "vue";
|
|
||||||
import type {
|
import type {
|
||||||
CompetenceRecordStatisticsType,
|
CompetenceRecordStatisticsType,
|
||||||
CourseStatisticsType,
|
CourseStatisticsType,
|
||||||
|
StatisticsCircleDataType,
|
||||||
} from "@/gql/graphql";
|
} from "@/gql/graphql";
|
||||||
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
|
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
|
||||||
import { useCourseStatistics } from "@/composables";
|
|
||||||
|
|
||||||
const dashboardStore = useDashboardStore();
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const props = defineProps<{
|
||||||
const statistics = computed(() => {
|
courseStatistics: CourseStatisticsType;
|
||||||
return dashboardStore.currentDashBoardData as CourseStatisticsType;
|
courseSessionName: (sessionId: string) => string;
|
||||||
});
|
circleMeta: (circleId: string) => StatisticsCircleDataType;
|
||||||
|
}>();
|
||||||
const { courseSessionName, circleMeta } = useCourseStatistics();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main v-if="statistics">
|
<main>
|
||||||
<div class="mb-10 flex items-center justify-between">
|
<div class="mb-10 flex items-center justify-between">
|
||||||
<h3>{{ $t("a.Selbsteinschätzung") }}</h3>
|
<h3>{{ $t("a.Selbsteinschätzung") }}</h3>
|
||||||
<ItDropdownSelect
|
|
||||||
:model-value="dashboardStore.currentDashboardConfig"
|
|
||||||
class="mt-4 w-full lg:mt-0 lg:w-96"
|
|
||||||
:items="dashboardStore.dashboardConfigs"
|
|
||||||
@update:model-value="dashboardStore.switchAndLoadDashboardConfig"
|
|
||||||
></ItDropdownSelect>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="statistics.competences.records" class="mt-8 bg-white">
|
<div v-if="courseStatistics?.competences.records" class="mt-8 bg-white">
|
||||||
<StatisticFilterList
|
<StatisticFilterList
|
||||||
:course-session-properties="statistics.course_session_properties"
|
:course-session-properties="courseStatistics.course_session_properties"
|
||||||
:items="statistics.competences.records"
|
:items="courseStatistics.competences.records"
|
||||||
>
|
>
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,30 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useDashboardStore } from "@/stores/dashboard";
|
|
||||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
|
||||||
import { computed } from "vue";
|
|
||||||
import type {
|
import type {
|
||||||
CourseStatisticsType,
|
CourseStatisticsType,
|
||||||
FeedbackStatisticsRecordType,
|
FeedbackStatisticsRecordType,
|
||||||
PresenceRecordStatisticsType,
|
PresenceRecordStatisticsType,
|
||||||
|
StatisticsCircleDataType,
|
||||||
} from "@/gql/graphql";
|
} from "@/gql/graphql";
|
||||||
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
|
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
|
||||||
import { useCourseStatistics } from "@/composables";
|
|
||||||
import { getBlendedColorForRating } from "@/utils/ratingToColor";
|
import { getBlendedColorForRating } from "@/utils/ratingToColor";
|
||||||
|
|
||||||
const dashboardStore = useDashboardStore();
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const props = defineProps<{
|
||||||
const statistics = computed(() => {
|
courseStatistics: CourseStatisticsType;
|
||||||
return dashboardStore.currentDashBoardData as CourseStatisticsType;
|
courseSessionName: (sessionId: string) => string;
|
||||||
});
|
circleMeta: (circleId: string) => StatisticsCircleDataType;
|
||||||
|
}>();
|
||||||
const { courseSessionName, circleMeta } = useCourseStatistics();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main v-if="statistics">
|
<main>
|
||||||
<div class="mb-10 flex items-center justify-between">
|
<div class="mb-10 flex items-center justify-between">
|
||||||
<h3>{{ $t("a.Feedback Teilnehmer") }}</h3>
|
<h3>{{ $t("a.Feedback Teilnehmer") }}</h3>
|
||||||
<ItDropdownSelect
|
|
||||||
:model-value="dashboardStore.currentDashboardConfig"
|
|
||||||
class="mt-4 w-full lg:mt-0 lg:w-96"
|
|
||||||
:items="dashboardStore.dashboardConfigs"
|
|
||||||
@update:model-value="dashboardStore.switchAndLoadDashboardConfig"
|
|
||||||
></ItDropdownSelect>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="statistics.feedback_responses.records" class="mt-8 bg-white">
|
<div v-if="courseStatistics?.feedback_responses.records" class="mt-8 bg-white">
|
||||||
<StatisticFilterList
|
<StatisticFilterList
|
||||||
:course-session-properties="statistics.course_session_properties"
|
:course-session-properties="courseStatistics.course_session_properties"
|
||||||
:items="statistics.feedback_responses.records"
|
:items="courseStatistics.feedback_responses.records"
|
||||||
>
|
>
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,18 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useDashboardStore } from "@/stores/dashboard";
|
|
||||||
import { onMounted } from "vue";
|
|
||||||
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
||||||
|
import { useCourseStatisticsv2 } from "@/composables";
|
||||||
|
|
||||||
const dashboardStore = useDashboardStore();
|
const props = defineProps<{
|
||||||
onMounted(dashboardStore.loadDashboardDetails);
|
courseSlug: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { courseStatistics, loading, courseSessionName, circleMeta } =
|
||||||
|
useCourseStatisticsv2(props.courseSlug);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bg-gray-200">
|
<div class="bg-gray-200">
|
||||||
<div v-if="dashboardStore.loading" class="m-8 flex justify-center">
|
<div v-if="loading" class="m-8 flex justify-center">
|
||||||
<LoadingSpinner />
|
<LoadingSpinner />
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="container-large flex flex-col space-y-8">
|
<div v-else class="container-large flex flex-col space-y-8">
|
||||||
|
|
@ -17,7 +20,11 @@ onMounted(dashboardStore.loadDashboardDetails);
|
||||||
<it-icon-arrow-left />
|
<it-icon-arrow-left />
|
||||||
<span>{{ $t("general.back") }}</span>
|
<span>{{ $t("general.back") }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-view></router-view>
|
<router-view
|
||||||
|
:course-statistics="courseStatistics"
|
||||||
|
:course-session-name="courseSessionName"
|
||||||
|
:circle-meta="circleMeta"
|
||||||
|
></router-view>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,19 @@ onMounted(() => {
|
||||||
>
|
>
|
||||||
<ul class="flex flex-col lg:flex-row">
|
<ul class="flex flex-col lg:flex-row">
|
||||||
<li
|
<li
|
||||||
|
data-cy="lm-mentees-navigation-link"
|
||||||
class="border-t-2 border-t-transparent"
|
class="border-t-2 border-t-transparent"
|
||||||
|
:class="{
|
||||||
|
'border-b-2 border-b-blue-900': route.name === 'mentorsAndParticipants',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<router-link :to="{ name: 'mentorsAndParticipants' }" class="block py-3">
|
||||||
|
{{ $t("a.Personen") }}
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
v-if="!courseSession.course.configuration.is_uk"
|
||||||
|
class="border-t-2 border-t-transparent lg:ml-12"
|
||||||
:class="{
|
:class="{
|
||||||
'border-b-2 border-b-blue-900': route.name
|
'border-b-2 border-b-blue-900': route.name
|
||||||
?.toString()
|
?.toString()
|
||||||
|
|
@ -41,19 +53,7 @@ onMounted(() => {
|
||||||
:to="{ name: 'learningMentorOverview' }"
|
:to="{ name: 'learningMentorOverview' }"
|
||||||
class="block py-3"
|
class="block py-3"
|
||||||
>
|
>
|
||||||
{{ $t("a.Übersicht") }}
|
{{ $t("a.Aufgaben") }}
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li
|
|
||||||
data-cy="lm-mentees-navigation-link"
|
|
||||||
class="border-t-2 border-t-transparent lg:ml-12"
|
|
||||||
:class="{
|
|
||||||
'border-b-2 border-b-blue-900': route.name === 'mentorsAndParticipants',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<router-link :to="{ name: 'mentorsAndParticipants' }" class="block py-3">
|
|
||||||
{{ $t("a.Personen") }}
|
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ const courseSession = useCurrentCourseSession();
|
||||||
const circleDocumentsResultData = ref<CircleDocument[]>([]);
|
const circleDocumentsResultData = ref<CircleDocument[]>([]);
|
||||||
|
|
||||||
async function fetchDocuments() {
|
async function fetchDocuments() {
|
||||||
const result = await fetchCourseSessionDocuments(courseSession.value?.id);
|
const result: any = await fetchCourseSessionDocuments(courseSession.value?.id);
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
circleDocumentsResultData.value = result;
|
circleDocumentsResultData.value = result;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ const documents = ref<BlockDocument[]>([]);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
log.debug("DocumentListBlock mounted");
|
log.debug("DocumentListBlock mounted");
|
||||||
const response = await itGetCached(`/api/course/page/${props.content.slug}/`);
|
const response: any = await itGetCached(`/api/course/page/${props.content.slug}/`);
|
||||||
documents.value = response.documents;
|
documents.value = response.documents;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ async function startTest() {
|
||||||
extendedTimeTest.value = true;
|
extendedTimeTest.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await itPost("/api/core/edoniq-test/redirect/", {
|
const response: any = await itPost("/api/core/edoniq-test/redirect/", {
|
||||||
learning_content_id: props.content.id,
|
learning_content_id: props.content.id,
|
||||||
extended_time_test: extendedTimeTest.value,
|
extended_time_test: extendedTimeTest.value,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import DueDatesShortList from "@/components/dueDates/DueDatesShortList.vue";
|
|
||||||
import LearningPathListView from "@/pages/learningPath/learningPathPage/LearningPathListView.vue";
|
import LearningPathListView from "@/pages/learningPath/learningPathPage/LearningPathListView.vue";
|
||||||
import LearningPathPathView from "@/pages/learningPath/learningPathPage/LearningPathPathView.vue";
|
import LearningPathPathView from "@/pages/learningPath/learningPathPage/LearningPathPathView.vue";
|
||||||
import CircleProgress from "@/pages/learningPath/learningPathPage/LearningPathProgress.vue";
|
import CircleProgress from "@/pages/learningPath/learningPathPage/LearningPathProgress.vue";
|
||||||
|
|
@ -8,8 +7,12 @@ import type { ViewType } from "@/pages/learningPath/learningPathPage/LearningPat
|
||||||
import LearningPathViewSwitch from "@/pages/learningPath/learningPathPage/LearningPathViewSwitch.vue";
|
import LearningPathViewSwitch from "@/pages/learningPath/learningPathPage/LearningPathViewSwitch.vue";
|
||||||
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core";
|
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core";
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import { useCourseDataWithCompletion } from "@/composables";
|
import {
|
||||||
import { someFinishedInLearningSequence } from "@/services/circle";
|
useCourseCircleProgress,
|
||||||
|
useCourseDataWithCompletion,
|
||||||
|
useCurrentCourseSession,
|
||||||
|
} from "@/composables";
|
||||||
|
import CourseSessionDueDatesList from "@/components/dueDates/CourseSessionDueDatesList.vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
courseSlug: string;
|
courseSlug: string;
|
||||||
|
|
@ -28,20 +31,11 @@ const lpQueryResult = useCourseDataWithCompletion(props.courseSlug);
|
||||||
const learningPath = computed(() => lpQueryResult.learningPath.value);
|
const learningPath = computed(() => lpQueryResult.learningPath.value);
|
||||||
const course = computed(() => lpQueryResult.course.value);
|
const course = computed(() => lpQueryResult.course.value);
|
||||||
|
|
||||||
const circlesCount = computed(() => {
|
const courseSession = useCurrentCourseSession();
|
||||||
return lpQueryResult.circles.value?.length ?? 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
const inProgressCirclesCount = computed(() => {
|
const { inProgressCirclesCount, circlesCount } = useCourseCircleProgress(
|
||||||
if (lpQueryResult.circles.value?.length) {
|
lpQueryResult.circles
|
||||||
return lpQueryResult.circles.value.filter(
|
);
|
||||||
(circle) =>
|
|
||||||
circle.learning_sequences.filter((ls) => someFinishedInLearningSequence(ls))
|
|
||||||
.length
|
|
||||||
).length;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
const changeViewType = (viewType: ViewType) => {
|
const changeViewType = (viewType: ViewType) => {
|
||||||
selectedView.value = viewType;
|
selectedView.value = viewType;
|
||||||
|
|
@ -72,10 +66,10 @@ const changeViewType = (viewType: ViewType) => {
|
||||||
|
|
||||||
<!-- Right -->
|
<!-- Right -->
|
||||||
<div v-if="!useMobileLayout" class="flex-grow">
|
<div v-if="!useMobileLayout" class="flex-grow">
|
||||||
<div class="text-bold pb-3">
|
<CourseSessionDueDatesList
|
||||||
{{ $t("learningPathPage.nextDueDates") }}
|
:course-session-id="courseSession.id"
|
||||||
</div>
|
:max-count="2"
|
||||||
<DueDatesShortList :max-count="2" :show-top-border="true"></DueDatesShortList>
|
></CourseSessionDueDatesList>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ const executePayment = () => {
|
||||||
redirect_url: fullHost,
|
redirect_url: fullHost,
|
||||||
address: address.value,
|
address: address.value,
|
||||||
product: props.courseType,
|
product: props.courseType,
|
||||||
}).then((res) => {
|
}).then((res: any) => {
|
||||||
console.log("Going to next page", res.next_step_url);
|
console.log("Going to next page", res.next_step_url);
|
||||||
window.location.href = res.next_step_url;
|
window.location.href = res.next_step_url;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,102 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import CockpitProfileContent from "@/components/userProfile/UserProfileContent.vue";
|
import CockpitProfileContent from "@/components/userProfile/UserProfileContent.vue";
|
||||||
import { ref } from "vue";
|
|
||||||
import SelfEvaluationAndFeedbackList from "@/components/selfEvaluationFeedback/SelfEvaluationAndFeedbackList.vue";
|
|
||||||
import SelfEvaluationAndFeedbackOverview from "@/components/selfEvaluationFeedback/SelfEvaluationAndFeedbackOverview.vue";
|
|
||||||
import { useCurrentCourseSession } from "@/composables";
|
import { useCurrentCourseSession } from "@/composables";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
type SubMenuType = "OVERVIEW" | "DETAILS";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
userId: string;
|
userId: string;
|
||||||
courseSlug: string;
|
courseSlug: string;
|
||||||
|
certificateSlug?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
interface SubMenuItem {
|
interface SubMenuItem {
|
||||||
type: SubMenuType;
|
|
||||||
label: string;
|
label: string;
|
||||||
|
url: string;
|
||||||
|
inMenu: boolean;
|
||||||
|
routeMatch: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const MENU_ENTRIES: SubMenuItem[] = [
|
const SUBPAGES: SubMenuItem[] = [
|
||||||
{ type: "OVERVIEW", label: "a.Übersicht" },
|
|
||||||
{
|
{
|
||||||
type: "DETAILS",
|
label: "a.Übersicht",
|
||||||
label: useCurrentCourseSession().value.course.configuration.enable_learning_mentor
|
url: `/course/${props.courseSlug}/profile/${props.userId}/competence`,
|
||||||
|
inMenu: true,
|
||||||
|
routeMatch: ["competenceMain"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: useCurrentCourseSession().value.course.configuration.is_vv
|
||||||
? "a.Selbst- und Fremdeinschätzungen"
|
? "a.Selbst- und Fremdeinschätzungen"
|
||||||
: "a.Selbsteinschätzungen",
|
: "a.Selbsteinschätzungen",
|
||||||
|
url: `/course/${props.courseSlug}/profile/${props.userId}/competence/evaluations`,
|
||||||
|
inMenu: true,
|
||||||
|
routeMatch: ["competenceEvaluations"],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const active = ref<SubMenuItem>(MENU_ENTRIES[0]);
|
if (useCurrentCourseSession().value.course.configuration.is_uk) {
|
||||||
const selectDetails = () => {
|
SUBPAGES.push(
|
||||||
active.value = MENU_ENTRIES[1];
|
{
|
||||||
};
|
label: "Kompetenznachweise",
|
||||||
|
url: `/course/${props.courseSlug}/profile/${props.userId}/competence/certificates`,
|
||||||
|
inMenu: true,
|
||||||
|
routeMatch: ["competenceCertificates", "competenceCertificateDetail"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "",
|
||||||
|
url: "",
|
||||||
|
inMenu: false,
|
||||||
|
routeMatch: [],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertRouteRecordNameToString(
|
||||||
|
routeRecordName: string | symbol | undefined
|
||||||
|
): string {
|
||||||
|
if (!routeRecordName) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (typeof routeRecordName === "symbol") {
|
||||||
|
// Convert symbol to string explicitly
|
||||||
|
return routeRecordName.toString();
|
||||||
|
} else {
|
||||||
|
// It's already a string, return as is
|
||||||
|
return routeRecordName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<CockpitProfileContent>
|
<CockpitProfileContent>
|
||||||
<template #side>
|
<template #side>
|
||||||
<div v-for="(entry, index) in MENU_ENTRIES" :key="index" class="mb-2">
|
<div
|
||||||
<button
|
v-for="(entry, index) in SUBPAGES.filter((p) => p.inMenu)"
|
||||||
|
:key="index"
|
||||||
|
class="mb-2"
|
||||||
|
>
|
||||||
|
<router-link
|
||||||
|
:to="entry.url"
|
||||||
class="flex w-full items-center space-x-2 p-2 pr-4 text-blue-900 hover:bg-gray-200 lg:pr-8"
|
class="flex w-full items-center space-x-2 p-2 pr-4 text-blue-900 hover:bg-gray-200 lg:pr-8"
|
||||||
:class="{ 'text-bold bg-gray-200': active.type === entry.type }"
|
:class="{
|
||||||
@click="active = entry"
|
'text-bold bg-gray-200': route.matched.some((record) =>
|
||||||
|
entry.routeMatch.includes(convertRouteRecordNameToString(record?.name))
|
||||||
|
),
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<span>{{ $t(entry.label) }}</span>
|
<span>{{ $t(entry.label) }}</span>
|
||||||
</button>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #main>
|
<template #main>
|
||||||
<div class="container-large">
|
<div class="container-large">
|
||||||
<SelfEvaluationAndFeedbackOverview
|
<router-view
|
||||||
v-if="active.type === 'OVERVIEW'"
|
|
||||||
:profile-user-id="props.userId"
|
:profile-user-id="props.userId"
|
||||||
@show-all="selectDetails"
|
:user-id="props.userId"
|
||||||
/>
|
:course-slug="useCurrentCourseSession().value.course.slug"
|
||||||
<SelfEvaluationAndFeedbackList
|
:certificate-slug="certificateSlug ? certificateSlug : ''"
|
||||||
v-else-if="active.type === 'DETAILS'"
|
></router-view>
|
||||||
class="w-full"
|
|
||||||
:profile-user-id="props.userId"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</CockpitProfileContent>
|
</CockpitProfileContent>
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,16 @@ const props = defineProps<{
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const pages = ref([
|
const pages = ref([
|
||||||
{ label: t("general.learningPath"), route: "profileLearningPath" },
|
{
|
||||||
{ label: t("a.KompetenzNavi"), route: "profileCompetence" },
|
label: t("general.learningPath"),
|
||||||
|
route: "profileLearningPath",
|
||||||
|
routeMatch: "profileLearningPath",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("a.KompetenzNavi"),
|
||||||
|
route: "competenceMain",
|
||||||
|
routeMatch: "profileCompetence",
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const courseSession = useCurrentCourseSession();
|
const courseSession = useCurrentCourseSession();
|
||||||
|
|
@ -54,7 +62,11 @@ onMounted(() => {
|
||||||
v-for="page in pages"
|
v-for="page in pages"
|
||||||
:key="page.route"
|
:key="page.route"
|
||||||
class="relative top-px mr-12 pb-3"
|
class="relative top-px mr-12 pb-3"
|
||||||
:class="[route.name === page.route ? 'border-b-2 border-blue-900 pb-3' : '']"
|
:class="[
|
||||||
|
route.matched.some((record) => record.name === page.routeMatch)
|
||||||
|
? 'border-b-2 border-blue-900 pb-3'
|
||||||
|
: '',
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<router-link :to="{ name: page.route }">
|
<router-link :to="{ name: page.route }">
|
||||||
{{ page.label }}
|
{{ page.label }}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,10 @@ const loginRequired = (to: RouteLocationNormalized) => {
|
||||||
return !to.meta?.public;
|
return !to.meta?.public;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function handleCurrentCourseSession(to: RouteLocationNormalized) {
|
export async function handleCurrentCourseSession(
|
||||||
|
to: RouteLocationNormalized,
|
||||||
|
options?: { unset?: boolean }
|
||||||
|
) {
|
||||||
// register after login hooks
|
// register after login hooks
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
if (userStore.loggedIn) {
|
if (userStore.loggedIn) {
|
||||||
|
|
@ -59,9 +62,12 @@ export async function handleCurrentCourseSession(to: RouteLocationNormalized) {
|
||||||
if (to.params.courseSlug) {
|
if (to.params.courseSlug) {
|
||||||
courseSessionsStore._currentCourseSlug = to.params.courseSlug as string;
|
courseSessionsStore._currentCourseSlug = to.params.courseSlug as string;
|
||||||
} else {
|
} else {
|
||||||
courseSessionsStore._currentCourseSlug = "";
|
if (options?.unset) {
|
||||||
|
courseSessionsStore._currentCourseSlug = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!courseSessionsStore.loaded) {
|
if (!courseSessionsStore.loaded) {
|
||||||
|
console.log("handleCurrentCourseSession: loadCourseSessionsData");
|
||||||
await courseSessionsStore.loadCourseSessionsData();
|
await courseSessionsStore.loadCourseSessionsData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -77,6 +83,7 @@ export async function handleCourseSessionAsQueryParam(to: RouteLocationNormalize
|
||||||
if (userStore.loggedIn) {
|
if (userStore.loggedIn) {
|
||||||
const courseSessionsStore = useCourseSessionsStore();
|
const courseSessionsStore = useCourseSessionsStore();
|
||||||
if (!courseSessionsStore.loaded) {
|
if (!courseSessionsStore.loaded) {
|
||||||
|
console.log("handleCourseSessionAsQueryParam: loadCourseSessionsData");
|
||||||
await courseSessionsStore.loadCourseSessionsData();
|
await courseSessionsStore.loadCourseSessionsData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,7 +96,7 @@ export async function handleCourseSessionAsQueryParam(to: RouteLocationNormalize
|
||||||
return {
|
return {
|
||||||
path: to.path,
|
path: to.path,
|
||||||
query: restOfQuery,
|
query: restOfQuery,
|
||||||
replace: true,
|
// replace: true,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// courseSessionId is invalid for current user -> redirect to home
|
// courseSessionId is invalid for current user -> redirect to home
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,32 @@ import type {
|
||||||
} from "vue-router";
|
} from "vue-router";
|
||||||
|
|
||||||
const routeHistory: RouteLocationNormalized[] = [];
|
const routeHistory: RouteLocationNormalized[] = [];
|
||||||
const MAX_HISTORY = 10; // for example, store the last 10 visited routes
|
const MAX_HISTORY = 10;
|
||||||
let isFirstNavigation = true;
|
let isFirstNavigation = true;
|
||||||
|
|
||||||
|
let lastNavigationWasPush = false;
|
||||||
|
|
||||||
|
export function setLastNavigationWasPush(value: boolean) {
|
||||||
|
lastNavigationWasPush = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLastNavigationWasPush() {
|
||||||
|
return lastNavigationWasPush;
|
||||||
|
}
|
||||||
|
|
||||||
export const addToHistory: NavigationGuard = (to, from, next) => {
|
export const addToHistory: NavigationGuard = (to, from, next) => {
|
||||||
// Add the current route to the history, and ensure it doesn't exceed the maximum length
|
// Add the current route to the history, and ensure it doesn't exceed the maximum length
|
||||||
if (isFirstNavigation) {
|
if (isFirstNavigation) {
|
||||||
isFirstNavigation = false;
|
isFirstNavigation = false;
|
||||||
} else {
|
} else if (lastNavigationWasPush) {
|
||||||
routeHistory.push(from);
|
routeHistory.push(from);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (routeHistory.length > MAX_HISTORY) {
|
if (routeHistory.length > MAX_HISTORY) {
|
||||||
routeHistory.shift();
|
routeHistory.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastNavigationWasPush = false;
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import {
|
||||||
redirectToLoginIfRequired,
|
redirectToLoginIfRequired,
|
||||||
updateLoggedIn,
|
updateLoggedIn,
|
||||||
} from "@/router/guards";
|
} from "@/router/guards";
|
||||||
import { addToHistory } from "@/router/history";
|
import { addToHistory, setLastNavigationWasPush } from "@/router/history";
|
||||||
import { onboardingRedirect } from "@/router/onboarding";
|
import { onboardingRedirect } from "@/router/onboarding";
|
||||||
import { createRouter, createWebHistory } from "vue-router";
|
import { createRouter, createWebHistory } from "vue-router";
|
||||||
|
|
||||||
|
|
@ -60,6 +60,19 @@ const router = createRouter({
|
||||||
name: "home",
|
name: "home",
|
||||||
component: DashboardPage,
|
component: DashboardPage,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/dashboard/persons",
|
||||||
|
component: () => import("@/pages/dashboard/DashboardPersonsPage.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/dashboard/persons-competence",
|
||||||
|
component: () => import("@/pages/dashboard/DashboardPersonsPage.vue"),
|
||||||
|
props: { mode: "competenceMetrics" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/dashboard/due-dates",
|
||||||
|
component: () => import("@/pages/dashboard/DashboardDueDatesPage.vue"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/course/:courseSlug/media",
|
path: "/course/:courseSlug/media",
|
||||||
props: true,
|
props: true,
|
||||||
|
|
@ -166,6 +179,37 @@ const router = createRouter({
|
||||||
component: () => import("@/pages/userProfile/CompetenceProfilePage.vue"),
|
component: () => import("@/pages/userProfile/CompetenceProfilePage.vue"),
|
||||||
props: true,
|
props: true,
|
||||||
name: "profileCompetence",
|
name: "profileCompetence",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "competenceMain",
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
"@/components/selfEvaluationFeedback/SelfEvaluationAndFeedbackOverview.vue"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "evaluations",
|
||||||
|
name: "competenceEvaluations",
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
"@/components/selfEvaluationFeedback/SelfEvaluationAndFeedbackList.vue"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "certificates/:certificateSlug",
|
||||||
|
name: "competenceCertificateDetail",
|
||||||
|
props: true,
|
||||||
|
component: () =>
|
||||||
|
import("@/pages/competence/CompetenceCertificateDetailPage.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "certificates",
|
||||||
|
name: "competenceCertificates",
|
||||||
|
component: () =>
|
||||||
|
import("@/pages/competence/CompetenceCertificateListPage.vue"),
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
@ -177,16 +221,16 @@ const router = createRouter({
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
component: () =>
|
|
||||||
import("@/pages/learningMentor/mentor/MentorOverviewPage.vue"),
|
|
||||||
name: "learningMentorOverview",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "participants",
|
|
||||||
component: () =>
|
component: () =>
|
||||||
import("@/pages/learningMentor/mentor/MentorParticipantsPage.vue"),
|
import("@/pages/learningMentor/mentor/MentorParticipantsPage.vue"),
|
||||||
name: "mentorsAndParticipants",
|
name: "mentorsAndParticipants",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "tasks",
|
||||||
|
component: () =>
|
||||||
|
import("@/pages/learningMentor/mentor/MentorOverviewPage.vue"),
|
||||||
|
name: "learningMentorOverview",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "self-evaluation-feedback/:learningUnitId",
|
path: "self-evaluation-feedback/:learningUnitId",
|
||||||
component: () =>
|
component: () =>
|
||||||
|
|
@ -264,7 +308,7 @@ const router = createRouter({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/statistic",
|
path: "/statistic/:courseSlug",
|
||||||
props: true,
|
props: true,
|
||||||
component: () => import("@/pages/dashboard/statistic/StatisticParentPage.vue"),
|
component: () => import("@/pages/dashboard/statistic/StatisticParentPage.vue"),
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -316,14 +360,6 @@ const router = createRouter({
|
||||||
path: "/notifications",
|
path: "/notifications",
|
||||||
component: () => import("@/pages/NotificationsPage.vue"),
|
component: () => import("@/pages/NotificationsPage.vue"),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "/appointments",
|
|
||||||
component: () => import("@/pages/AppointmentsPage.vue"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/course/:courseSlug/appointments",
|
|
||||||
component: () => import("@/pages/AppointmentsPage.vue"),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "/onboarding/:courseType",
|
path: "/onboarding/:courseType",
|
||||||
props: true,
|
props: true,
|
||||||
|
|
@ -387,9 +423,26 @@ router.beforeEach(updateLoggedIn);
|
||||||
router.beforeEach(redirectToLoginIfRequired);
|
router.beforeEach(redirectToLoginIfRequired);
|
||||||
|
|
||||||
// register after login hooks
|
// register after login hooks
|
||||||
router.beforeEach(handleCurrentCourseSession);
|
router.beforeEach(async (to) => await handleCurrentCourseSession(to));
|
||||||
router.beforeEach(handleCourseSessionAsQueryParam);
|
router.beforeEach(async (to) => await handleCourseSessionAsQueryParam(to));
|
||||||
|
|
||||||
|
// only unset the current course session in the after hook
|
||||||
|
router.afterEach(async (to) => await handleCurrentCourseSession(to, { unset: true }));
|
||||||
|
|
||||||
router.beforeEach(addToHistory);
|
router.beforeEach(addToHistory);
|
||||||
|
|
||||||
|
// Wrap router.replace to track when it's called
|
||||||
|
const originalReplace = router.replace;
|
||||||
|
router.replace = function (to) {
|
||||||
|
setLastNavigationWasPush(false);
|
||||||
|
return originalReplace.call(this, to);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrap router.push to track when it's called
|
||||||
|
const originalPush = router.push;
|
||||||
|
router.push = function (to) {
|
||||||
|
setLastNavigationWasPush(true);
|
||||||
|
return originalPush.call(this, to);
|
||||||
|
};
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
import type {
|
||||||
|
CompetenceCertificateForUserQueryQuery,
|
||||||
|
CompetenceCertificateListObjectType,
|
||||||
|
CompetenceCertificateQueryQuery,
|
||||||
|
} from "@/gql/graphql";
|
||||||
import type { PerformanceCriteria } from "@/types";
|
import type { PerformanceCriteria } from "@/types";
|
||||||
import groupBy from "lodash/groupBy";
|
import groupBy from "lodash/groupBy";
|
||||||
|
|
||||||
|
|
@ -17,3 +22,42 @@ export function calcPerformanceCriteriaStatusCount(criteria: PerformanceCriteria
|
||||||
FAIL: 0,
|
FAIL: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type guards
|
||||||
|
export function isCompetenceCertificateForUserQueryQuery(
|
||||||
|
data: any
|
||||||
|
): data is CompetenceCertificateForUserQueryQuery {
|
||||||
|
return (
|
||||||
|
(data as CompetenceCertificateForUserQueryQuery)
|
||||||
|
.competence_certificate_list_for_user !== undefined
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isCompetenceCertificateQueryQuery(
|
||||||
|
data: any
|
||||||
|
): data is CompetenceCertificateQueryQuery {
|
||||||
|
return (
|
||||||
|
(data as CompetenceCertificateQueryQuery).competence_certificate_list !== undefined
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCertificates(
|
||||||
|
data: any,
|
||||||
|
userId: string | null
|
||||||
|
): CompetenceCertificateListObjectType | null {
|
||||||
|
if (!data) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let certificates = null;
|
||||||
|
|
||||||
|
if (userId && isCompetenceCertificateForUserQueryQuery(data)) {
|
||||||
|
certificates = data.competence_certificate_list_for_user;
|
||||||
|
} else if (isCompetenceCertificateQueryQuery(data)) {
|
||||||
|
certificates = data.competence_certificate_list;
|
||||||
|
} else {
|
||||||
|
// Handle case where data does not match expected types
|
||||||
|
console.error("Data structure is not recognized!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (certificates as unknown as CompetenceCertificateListObjectType) ?? null;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,93 @@ import {
|
||||||
DASHBOARD_CONFIG,
|
DASHBOARD_CONFIG,
|
||||||
DASHBOARD_COURSE_SESSION_PROGRESS,
|
DASHBOARD_COURSE_SESSION_PROGRESS,
|
||||||
DASHBOARD_COURSE_STATISTICS,
|
DASHBOARD_COURSE_STATISTICS,
|
||||||
|
DASHBOARD_MENTOR_COMPETENCE_SUMMARY,
|
||||||
} from "@/graphql/queries";
|
} from "@/graphql/queries";
|
||||||
|
|
||||||
|
import { itGetCached } from "@/fetchHelpers";
|
||||||
import type {
|
import type {
|
||||||
|
AssignmentsStatisticsType,
|
||||||
CourseProgressType,
|
CourseProgressType,
|
||||||
CourseStatisticsType,
|
CourseStatisticsType,
|
||||||
DashboardConfigType,
|
DashboardConfigType,
|
||||||
} from "@/gql/graphql";
|
} from "@/gql/graphql";
|
||||||
|
import type { DashboardPersonsPageMode, DueDate } from "@/types";
|
||||||
|
|
||||||
|
export type DashboardPersonRoleType =
|
||||||
|
| "SUPERVISOR"
|
||||||
|
| "EXPERT"
|
||||||
|
| "MEMBER"
|
||||||
|
| "LEARNING_MENTOR"
|
||||||
|
| "LEARNING_MENTEE";
|
||||||
|
|
||||||
|
export type DashboardRoleKeyType =
|
||||||
|
| "Supervisor"
|
||||||
|
| "Expert"
|
||||||
|
| "Member"
|
||||||
|
| "MentorUK"
|
||||||
|
| "MentorVV";
|
||||||
|
|
||||||
|
export type WidgetType =
|
||||||
|
| "ProgressWidget"
|
||||||
|
| "CompetenceWidget"
|
||||||
|
| "MentorTasksWidget"
|
||||||
|
| "MentorPersonWidget"
|
||||||
|
| "MentorCompetenceWidget"
|
||||||
|
| "CompetenceCertificateWidget"
|
||||||
|
| "UKStatisticsWidget";
|
||||||
|
|
||||||
|
export type DashboardPersonCourseSessionType = {
|
||||||
|
id: string;
|
||||||
|
session_title: string;
|
||||||
|
course_id: string;
|
||||||
|
course_title: string;
|
||||||
|
course_slug: string;
|
||||||
|
region: string;
|
||||||
|
generation: string;
|
||||||
|
user_role: DashboardPersonRoleType;
|
||||||
|
user_role_display: string;
|
||||||
|
my_role: DashboardPersonRoleType;
|
||||||
|
my_role_display: string;
|
||||||
|
is_uk: boolean;
|
||||||
|
is_vv: boolean;
|
||||||
|
competence_metrics?: {
|
||||||
|
passed_count: number;
|
||||||
|
failed_count: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DashboardPersonType = {
|
||||||
|
user_id: string;
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
email: string;
|
||||||
|
course_sessions: DashboardPersonCourseSessionType[];
|
||||||
|
avatar_url: string;
|
||||||
|
avatar_url_small: string;
|
||||||
|
competence_metrics?: {
|
||||||
|
passed_count: number;
|
||||||
|
failed_count: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DashboardCourseConfigType = {
|
||||||
|
course_id: string;
|
||||||
|
course_slug: string;
|
||||||
|
course_title: string;
|
||||||
|
role_key: DashboardRoleKeyType;
|
||||||
|
is_uk: boolean;
|
||||||
|
is_vv: boolean;
|
||||||
|
is_mentor: boolean;
|
||||||
|
widgets: WidgetType[];
|
||||||
|
has_preview: boolean;
|
||||||
|
session_to_continue_id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DashboardDueDate = DueDate & {
|
||||||
|
course_session: DashboardPersonCourseSessionType;
|
||||||
|
translatedType: string;
|
||||||
|
persons?: DashboardPersonType[];
|
||||||
|
};
|
||||||
|
|
||||||
export const fetchStatisticData = async (
|
export const fetchStatisticData = async (
|
||||||
courseId: string
|
courseId: string
|
||||||
|
|
@ -47,6 +127,7 @@ export const fetchProgressData = async (
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchDashboardConfig = async (): Promise<DashboardConfigType[] | null> => {
|
export const fetchDashboardConfig = async (): Promise<DashboardConfigType[] | null> => {
|
||||||
try {
|
try {
|
||||||
const res = await graphqlClient.query(DASHBOARD_CONFIG, {});
|
const res = await graphqlClient.query(DASHBOARD_CONFIG, {});
|
||||||
|
|
@ -55,9 +136,63 @@ export const fetchDashboardConfig = async (): Promise<DashboardConfigType[] | nu
|
||||||
console.error("Error fetching dashboard config:", res.error);
|
console.error("Error fetching dashboard config:", res.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.data?.dashboard_config || null;
|
return (res.data?.dashboard_config as unknown as DashboardConfigType[]) || null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching dashboard config:", error);
|
console.error("Error fetching dashboard config:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fetchMentorCompetenceSummary = async (
|
||||||
|
courseId: string
|
||||||
|
): Promise<AssignmentsStatisticsType | null> => {
|
||||||
|
try {
|
||||||
|
const res = await graphqlClient.query(DASHBOARD_MENTOR_COMPETENCE_SUMMARY, {
|
||||||
|
courseId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.error) {
|
||||||
|
console.error("Error fetching data for course ID:", courseId, res.error);
|
||||||
|
}
|
||||||
|
return res.data?.mentor_course_statistics?.assignments || null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error fetching data for course ID: ${courseId}`, error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function fetchDashboardPersons(mode: DashboardPersonsPageMode) {
|
||||||
|
let url = "/api/dashboard/persons/";
|
||||||
|
if (mode === "competenceMetrics") {
|
||||||
|
url += "?with_competence_metrics=true";
|
||||||
|
}
|
||||||
|
return await itGetCached<DashboardPersonType[]>(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchDashboardDueDates() {
|
||||||
|
return await itGetCached<DashboardDueDate[]>("/api/dashboard/duedates/");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchDashboardConfigv2() {
|
||||||
|
return await itGetCached<DashboardCourseConfigType[]>("/api/dashboard/config/");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchMenteeCount(courseId: string) {
|
||||||
|
return await itGetCached<{ mentee_count: number }>(
|
||||||
|
`/api/dashboard/course/${courseId}/mentees/`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchOpenTasksCount(courseId: string) {
|
||||||
|
return await itGetCached<{ open_task_count: number }>(
|
||||||
|
`/api/dashboard/course/${courseId}/open_tasks/`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function courseIdForCourseSlug(
|
||||||
|
dashboardConfigs: DashboardCourseConfigType[],
|
||||||
|
courseSlug: string
|
||||||
|
) {
|
||||||
|
const config = dashboardConfigs.find((config) => config.course_slug === courseSlug);
|
||||||
|
return config?.course_id;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export function useEntities() {
|
||||||
const countries: Ref<Country[]> = ref([]);
|
const countries: Ref<Country[]> = ref([]);
|
||||||
const organisations: Ref<Organisation[]> = ref([]);
|
const organisations: Ref<Organisation[]> = ref([]);
|
||||||
|
|
||||||
itGetCached("/api/core/entities/").then((res) => {
|
itGetCached("/api/core/entities/").then((res: any) => {
|
||||||
countries.value = res.countries;
|
countries.value = res.countries;
|
||||||
organisations.value = res.organisations;
|
organisations.value = res.organisations;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ export async function uploadCircleDocument(
|
||||||
throw new Error("No file selected");
|
throw new Error("No file selected");
|
||||||
}
|
}
|
||||||
|
|
||||||
const startData = await startFileUpload(data, courseSessionId);
|
const startData: any = await startFileUpload(data, courseSessionId);
|
||||||
|
|
||||||
await uploadFile(startData, data.file);
|
await uploadFile(startData, data.file);
|
||||||
const response = itPost(`/api/core/file/finish/`, {
|
const response = itPost(`/api/core/file/finish/`, {
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ export const useLearningMentees = (
|
||||||
error.value = null;
|
error.value = null;
|
||||||
|
|
||||||
itGet(`/api/mentor/${courseSessionId}/summary`)
|
itGet(`/api/mentor/${courseSessionId}/summary`)
|
||||||
.then((response) => {
|
.then((response: any) => {
|
||||||
summary.value = response;
|
summary.value = response;
|
||||||
})
|
})
|
||||||
.catch((err) => (error.value = err))
|
.catch((err) => (error.value = err))
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ export const useCompletionStore = defineStore({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (courseSessionId) {
|
if (courseSessionId) {
|
||||||
const completionData = await itPost("/api/course/completion/mark/", {
|
const completionData: any = await itPost("/api/course/completion/mark/", {
|
||||||
page_id: page.id,
|
page_id: page.id,
|
||||||
completion_status: page.completion_status,
|
completion_status: page.completion_status,
|
||||||
course_session_id: courseSessionId,
|
course_session_id: courseSessionId,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { itGetCached } from "@/fetchHelpers";
|
import { itGetCached } from "@/fetchHelpers";
|
||||||
import type { CourseSession, DueDate } from "@/types";
|
import type { CourseSession } from "@/types";
|
||||||
import eventBus from "@/utils/eventBus";
|
import eventBus from "@/utils/eventBus";
|
||||||
import { useRouteLookups } from "@/utils/route";
|
import { useRouteLookups } from "@/utils/route";
|
||||||
import { useLocalStorage } from "@vueuse/core";
|
import { useLocalStorage } from "@vueuse/core";
|
||||||
import dayjs from "dayjs";
|
|
||||||
import uniqBy from "lodash/uniqBy";
|
import uniqBy from "lodash/uniqBy";
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
|
|
@ -25,13 +24,6 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
if (userStore.loggedIn) {
|
if (userStore.loggedIn) {
|
||||||
// TODO: refactor after implementing of Klassenkonzept
|
|
||||||
await Promise.all(
|
|
||||||
allCourseSessions.value.map(async (cs) => {
|
|
||||||
sortDueDates(cs.due_dates);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!allCourseSessions.value) {
|
if (!allCourseSessions.value) {
|
||||||
throw `No courseSessionData found for user`;
|
throw `No courseSessionData found for user`;
|
||||||
}
|
}
|
||||||
|
|
@ -137,37 +129,12 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
||||||
return Boolean(hasPreview && (inLearningPath() || inCompetenceProfile()));
|
return Boolean(hasPreview && (inLearningPath() || inCompetenceProfile()));
|
||||||
});
|
});
|
||||||
|
|
||||||
function allDueDates() {
|
|
||||||
const allDueDatesReturn: DueDate[] = [];
|
|
||||||
|
|
||||||
allCourseSessions.value?.forEach((cs) => {
|
|
||||||
allDueDatesReturn.push(...cs.due_dates);
|
|
||||||
});
|
|
||||||
|
|
||||||
sortDueDates(allDueDatesReturn);
|
|
||||||
return allDueDatesReturn;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortDueDates(dueDates: DueDate[]) {
|
|
||||||
dueDates.sort((a, b) => {
|
|
||||||
const dateA = dayjs(a.start);
|
|
||||||
const dateB = dayjs(b.start);
|
|
||||||
|
|
||||||
if (!dateA.isValid() && !dateB.isValid()) return 0; // If both are invalid, they are equal
|
|
||||||
if (!dateA.isValid()) return 1; // If dateA is invalid, it goes after dateB
|
|
||||||
if (!dateB.isValid()) return -1; // If dateB is invalid, it goes after dateA
|
|
||||||
|
|
||||||
return dateA.diff(dateB); // sort by `start`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uniqueCourseSessionsByCourse,
|
uniqueCourseSessionsByCourse,
|
||||||
allCurrentCourseSessions,
|
allCurrentCourseSessions,
|
||||||
getCourseSessionById,
|
getCourseSessionById,
|
||||||
switchCourseSessionById,
|
switchCourseSessionById,
|
||||||
isCourseSessionPreviewActive,
|
isCourseSessionPreviewActive,
|
||||||
allDueDates,
|
|
||||||
|
|
||||||
// use `useCurrentCourseSession` whenever possible
|
// use `useCurrentCourseSession` whenever possible
|
||||||
currentCourseSession,
|
currentCourseSession,
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ import type {
|
||||||
CourseProgressType,
|
CourseProgressType,
|
||||||
CourseStatisticsType,
|
CourseStatisticsType,
|
||||||
DashboardConfigType,
|
DashboardConfigType,
|
||||||
DashboardType,
|
|
||||||
} from "@/gql/graphql";
|
} from "@/gql/graphql";
|
||||||
|
import type { DashboardCourseConfigType } from "@/services/dashboard";
|
||||||
import {
|
import {
|
||||||
fetchDashboardConfig,
|
fetchDashboardConfig,
|
||||||
fetchProgressData,
|
fetchDashboardConfigv2,
|
||||||
fetchStatisticData,
|
fetchStatisticData,
|
||||||
} from "@/services/dashboard";
|
} from "@/services/dashboard";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
|
|
@ -15,6 +15,7 @@ import { ref } from "vue";
|
||||||
|
|
||||||
export const useDashboardStore = defineStore("dashboard", () => {
|
export const useDashboardStore = defineStore("dashboard", () => {
|
||||||
const dashboardConfigs: Ref<DashboardConfigType[]> = ref([]);
|
const dashboardConfigs: Ref<DashboardConfigType[]> = ref([]);
|
||||||
|
const dashboardConfigsv2: Ref<DashboardCourseConfigType[]> = ref([]);
|
||||||
const currentDashboardConfig: Ref<DashboardConfigType | undefined> = ref();
|
const currentDashboardConfig: Ref<DashboardConfigType | undefined> = ref();
|
||||||
const dashBoardDataCache: Record<
|
const dashBoardDataCache: Record<
|
||||||
string,
|
string,
|
||||||
|
|
@ -24,21 +25,21 @@ export const useDashboardStore = defineStore("dashboard", () => {
|
||||||
ref(null);
|
ref(null);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
const loadDashboardData = async (type: DashboardType, id: string) => {
|
// const loadDashboardData = async (type: DashboardType, id: string) => {
|
||||||
let data;
|
// let data;
|
||||||
switch (type) {
|
// switch (type) {
|
||||||
case "STATISTICS_DASHBOARD":
|
// case "STATISTICS_DASHBOARD":
|
||||||
data = await fetchStatisticData(id);
|
// data = await fetchStatisticData(id);
|
||||||
break;
|
// break;
|
||||||
case "PROGRESS_DASHBOARD":
|
// case "PROGRESS_DASHBOARD":
|
||||||
data = await fetchProgressData(id);
|
// data = await fetchProgressData(id);
|
||||||
break;
|
// break;
|
||||||
default:
|
// default:
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
dashBoardDataCache[id] = data;
|
// dashBoardDataCache[id] = data;
|
||||||
currentDashBoardData.value = data;
|
// currentDashBoardData.value = data;
|
||||||
};
|
// };
|
||||||
|
|
||||||
const switchAndLoadDashboardConfig = async (config: DashboardConfigType) => {
|
const switchAndLoadDashboardConfig = async (config: DashboardConfigType) => {
|
||||||
currentDashboardConfig.value = config;
|
currentDashboardConfig.value = config;
|
||||||
|
|
@ -56,29 +57,47 @@ export const useDashboardStore = defineStore("dashboard", () => {
|
||||||
|
|
||||||
const loadDashboardDetails = async () => {
|
const loadDashboardDetails = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
dashboardConfigsv2.value = await fetchDashboardConfigv2();
|
||||||
|
console.log("got dashboard config v2: ", dashboardConfigsv2.value);
|
||||||
try {
|
try {
|
||||||
if (!currentDashboardConfig.value) {
|
// if (!currentDashboardConfig.value) {
|
||||||
await loadDashboardConfig();
|
// await loadDashboardConfig();
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
const { id, dashboard_type } = currentDashboardConfig.value;
|
// const { id, dashboard_type } = currentDashboardConfig.value;
|
||||||
if (dashBoardDataCache[id]) {
|
// if (dashBoardDataCache[id]) {
|
||||||
currentDashBoardData.value = dashBoardDataCache[id];
|
// currentDashBoardData.value = dashBoardDataCache[id];
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
await loadDashboardData(dashboard_type, id);
|
// // await loadDashboardData(dashboard_type, id);
|
||||||
} finally {
|
} finally {
|
||||||
|
console.log("done loading dashboard details");
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loadStatisticsData = async (id: string) => {
|
||||||
|
const data = await fetchStatisticData(id);
|
||||||
|
dashBoardDataCache[id] = data;
|
||||||
|
currentDashBoardData.value = data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadStatisticsDatav2 = async (id: string) => {
|
||||||
|
const data = await fetchStatisticData(id);
|
||||||
|
dashBoardDataCache[id] = data;
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dashboardConfigs,
|
dashboardConfigs,
|
||||||
|
dashboardConfigsv2,
|
||||||
currentDashboardConfig,
|
currentDashboardConfig,
|
||||||
switchAndLoadDashboardConfig,
|
switchAndLoadDashboardConfig,
|
||||||
loadDashboardConfig,
|
loadDashboardConfig,
|
||||||
loadDashboardDetails,
|
loadDashboardDetails,
|
||||||
currentDashBoardData,
|
currentDashBoardData,
|
||||||
loading,
|
loading,
|
||||||
|
loadStatisticsData,
|
||||||
|
loadStatisticsDatav2,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ export const useMediaLibraryStore = defineStore({
|
||||||
return this.mediaLibraryPage;
|
return this.mediaLibraryPage;
|
||||||
}
|
}
|
||||||
log.debug("load mediaLibraryPageData");
|
log.debug("load mediaLibraryPageData");
|
||||||
const mediaLibraryPageData = await itGet(`/api/course/page/${slug}/`);
|
const mediaLibraryPageData: any = await itGet(`/api/course/page/${slug}/`);
|
||||||
|
|
||||||
if (!mediaLibraryPageData) {
|
if (!mediaLibraryPageData) {
|
||||||
throw `No mediaLibraryPageData found with: ${slug}`;
|
throw `No mediaLibraryPageData found with: ${slug}`;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export const useNotificationsStore = defineStore("notifications", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateUnreadCount() {
|
async function updateUnreadCount() {
|
||||||
const data = await itGet("/notifications/api/unread_count/");
|
const data: any = await itGet("/notifications/api/unread_count/");
|
||||||
hasUnread.value = data.unread_count !== 0;
|
hasUnread.value = data.unread_count !== 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ export const useUserStore = defineStore({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async fetchUser() {
|
async fetchUser() {
|
||||||
const data = await itGetCached("/api/core/me/");
|
const data: any = await itGetCached("/api/core/me/");
|
||||||
this.$state = data;
|
this.$state = data;
|
||||||
this.loggedIn = true;
|
this.loggedIn = true;
|
||||||
await setLocale(data.language);
|
await setLocale(data.language);
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,8 @@ export interface CourseConfiguration {
|
||||||
enable_circle_documents: boolean;
|
enable_circle_documents: boolean;
|
||||||
enable_learning_mentor: boolean;
|
enable_learning_mentor: boolean;
|
||||||
enable_competence_certificates: boolean;
|
enable_competence_certificates: boolean;
|
||||||
|
is_uk: boolean;
|
||||||
|
is_vv: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Course {
|
export interface Course {
|
||||||
|
|
@ -207,6 +209,8 @@ export interface CourseCategory {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
general: boolean;
|
general: boolean;
|
||||||
|
is_uk: boolean;
|
||||||
|
is_vv: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MediaLibraryContentBlockValue = {
|
export type MediaLibraryContentBlockValue = {
|
||||||
|
|
@ -451,7 +455,6 @@ export interface CourseSession {
|
||||||
title: string;
|
title: string;
|
||||||
start_date: string;
|
start_date: string;
|
||||||
end_date: string;
|
end_date: string;
|
||||||
due_dates: DueDate[];
|
|
||||||
actions: string[];
|
actions: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -607,3 +610,5 @@ export type User = {
|
||||||
course_session_experts: any[];
|
course_session_experts: any[];
|
||||||
language: string;
|
language: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DashboardPersonsPageMode = "default" | "competenceMetrics";
|
||||||
|
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
import { login } from "./helpers";
|
|
||||||
|
|
||||||
// constants
|
|
||||||
const COURSE_SELECT = "[data-cy=appointments-course-select]";
|
|
||||||
const SESSION_SELECT = "[data-cy=appointments-session-select]";
|
|
||||||
const CIRCLE_SELECT = "[data-cy=appointments-circle-select]";
|
|
||||||
const APPOINTMENTS = "[data-cy=appointments-list]";
|
|
||||||
|
|
||||||
describe("appointments.cy.js", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.manageCommand("cypress_reset");
|
|
||||||
login("test-student2@example.com", "test");
|
|
||||||
cy.visit("/course/test-lehrgang/appointments");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("preselects first course (Test Lehrgang)", () => {
|
|
||||||
cy.visit("/course/test-lehrgang/appointments");
|
|
||||||
cy.get(COURSE_SELECT).should("contain", "Test Lehrgang");
|
|
||||||
cy.get(SESSION_SELECT).should("contain", "Bern");
|
|
||||||
cy.get(CIRCLE_SELECT).should("contain", "Alle");
|
|
||||||
|
|
||||||
cy.get(".cy-single-due-date").should("have.length", 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("can filter by circle", () => {
|
|
||||||
cy.get(CIRCLE_SELECT).click();
|
|
||||||
cy.get(CIRCLE_SELECT).contains("Fahrzeug").click();
|
|
||||||
|
|
||||||
// THEN
|
|
||||||
cy.get(APPOINTMENTS).should("not.contain", "Keine Termine");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("can switch course session", () => {
|
|
||||||
cy.get(SESSION_SELECT).click();
|
|
||||||
cy.get(SESSION_SELECT).contains("Zürich").click();
|
|
||||||
cy.get(SESSION_SELECT).should("contain", "Zürich");
|
|
||||||
|
|
||||||
// THEN
|
|
||||||
cy.get(APPOINTMENTS).should("contain", "Keine Termine");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -101,7 +101,7 @@ describe("circle.cy.js", () => {
|
||||||
.should("contain", "Feedback");
|
.should("contain", "Feedback");
|
||||||
|
|
||||||
cy.visit("/course/test-lehrgang/learn/reisen");
|
cy.visit("/course/test-lehrgang/learn/reisen");
|
||||||
cy.get("[data-cy=\"lp-learning-sequence\"]").should("have.length", 3);
|
cy.get("[data-cy=\"lp-learning-sequence\"]").should("have.length", 4);
|
||||||
cy.get("[data-cy=\"lp-learning-content\"]").should("have.length", 9);
|
cy.get("[data-cy=\"lp-learning-content\"]").should("have.length", 11);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,9 @@ describe("selfEvaluation.cy.js", () => {
|
||||||
cy.get("[data-cy=\"self-evaluation-unknown\"]").should("have.text", "4");
|
cy.get("[data-cy=\"self-evaluation-unknown\"]").should("have.text", "4");
|
||||||
|
|
||||||
|
|
||||||
// learning unit id = 687 also known as:
|
// learning unit id = 692 also known as:
|
||||||
// Bedarfsanalyse, Ist- und Soll-Situation <<Reisen>>
|
// Bedarfsanalyse, Ist- und Soll-Situation <<Reisen>>
|
||||||
const identifier = "self-eval-687"
|
const identifier = "self-eval-692"
|
||||||
|
|
||||||
// data in KompetenzNavi/Selbsteinschätzungen is correct
|
// data in KompetenzNavi/Selbsteinschätzungen is correct
|
||||||
cy.visit("/course/test-lehrgang/competence/self-evaluation-and-feedback");
|
cy.visit("/course/test-lehrgang/competence/self-evaluation-and-feedback");
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ describe("dashboardSupervisor.cy.js", () => {
|
||||||
});
|
});
|
||||||
it("contains correct details link", () => {
|
it("contains correct details link", () => {
|
||||||
clickOnDetailsLink("attendance");
|
clickOnDetailsLink("attendance");
|
||||||
cy.url().should("contain", "/statistic/attendance");
|
cy.url().should("contain", "/statistic/test-lehrgang/attendance");
|
||||||
|
|
||||||
// might be improved: roughly check
|
// might be improved: roughly check
|
||||||
// that the correct data is displayed
|
// that the correct data is displayed
|
||||||
|
|
@ -63,14 +63,6 @@ describe("dashboardSupervisor.cy.js", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("overall summary box", () => {
|
|
||||||
it("contains correct numbers (members, experts etc.)", () => {
|
|
||||||
getDashboardStatistics("participant.count").should("have.text", "4");
|
|
||||||
getDashboardStatistics("expert.count").should("have.text", "2");
|
|
||||||
getDashboardStatistics("session.count").should("have.text", "2");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("feedback summary box", () => {
|
describe("feedback summary box", () => {
|
||||||
it("contains correct numbers", () => {
|
it("contains correct numbers", () => {
|
||||||
getDashboardStatistics("feedback.average").should("have.text", "3.3");
|
getDashboardStatistics("feedback.average").should("have.text", "3.3");
|
||||||
|
|
@ -78,7 +70,7 @@ describe("dashboardSupervisor.cy.js", () => {
|
||||||
});
|
});
|
||||||
it("contains correct details link", () => {
|
it("contains correct details link", () => {
|
||||||
clickOnDetailsLink("feedback");
|
clickOnDetailsLink("feedback");
|
||||||
cy.url().should("contain", "/statistic/feedback");
|
cy.url().should("contain", "/statistic/test-lehrgang/feedback");
|
||||||
|
|
||||||
// might be improved: roughly check
|
// might be improved: roughly check
|
||||||
// that the correct data is displayed
|
// that the correct data is displayed
|
||||||
|
|
@ -96,7 +88,7 @@ describe("dashboardSupervisor.cy.js", () => {
|
||||||
});
|
});
|
||||||
it("contains correct details link", () => {
|
it("contains correct details link", () => {
|
||||||
clickOnDetailsLink("competence");
|
clickOnDetailsLink("competence");
|
||||||
cy.url().should("contain", "/statistic/competence");
|
cy.url().should("contain", "/statistic/test-lehrgang/competence");
|
||||||
|
|
||||||
// might be improved: roughly check
|
// might be improved: roughly check
|
||||||
// that the correct data is displayed
|
// that the correct data is displayed
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { login } from "./helpers";
|
||||||
|
|
||||||
|
function selectDropboxItem(dropboxSelector, item) {
|
||||||
|
cy.get(dropboxSelector).click();
|
||||||
|
cy.get(dropboxSelector).contains(item).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("dueDates.cy.js", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.manageCommand("cypress_reset");
|
||||||
|
login("test-student2@example.com", "test");
|
||||||
|
cy.visit("/dashboard/due-dates");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can filter due dates by dropbox selects", () => {
|
||||||
|
cy.get('[data-cy="due-date-list"]').children().should("have.length", 7);
|
||||||
|
|
||||||
|
// can filter by session
|
||||||
|
selectDropboxItem('[data-cy="select-session"]', "Zürich");
|
||||||
|
cy.get('[data-cy="due-date-list"]').children().should("have.length", 1);
|
||||||
|
selectDropboxItem('[data-cy="select-session"]', "Bern");
|
||||||
|
cy.get('[data-cy="due-date-list"]').children().should("have.length", 6);
|
||||||
|
selectDropboxItem('[data-cy="select-session"]', "Alle");
|
||||||
|
cy.get('[data-cy="due-date-list"]').children().should("have.length", 7);
|
||||||
|
|
||||||
|
// can filter by circle
|
||||||
|
selectDropboxItem('[data-cy="select-circle"]', "Fahrzeug");
|
||||||
|
cy.get('[data-cy="due-date-list"]').children().should("have.length", 6);
|
||||||
|
selectDropboxItem('[data-cy="select-circle"]', "Reisen");
|
||||||
|
cy.get('[data-cy="due-date-list"]').children().should("have.length", 1);
|
||||||
|
selectDropboxItem('[data-cy="select-circle"]', "Alle");
|
||||||
|
cy.get('[data-cy="due-date-list"]').children().should("have.length", 7);
|
||||||
|
|
||||||
|
// can filter by types
|
||||||
|
selectDropboxItem('[data-cy="select-type"]', "Präsenzkurs");
|
||||||
|
cy.get('[data-cy="due-date-list"]').children().should("have.length", 3);
|
||||||
|
selectDropboxItem('[data-cy="select-type"]', "Bewertung");
|
||||||
|
cy.get('[data-cy="due-date-list"]').children().should("have.length", 1);
|
||||||
|
|
||||||
|
// combination
|
||||||
|
selectDropboxItem('[data-cy="select-session"]', "Bern");
|
||||||
|
selectDropboxItem('[data-cy="select-type"]', "Präsenzkurs");
|
||||||
|
cy.get('[data-cy="due-date-list"]').children().should("have.length", 2);
|
||||||
|
selectDropboxItem('[data-cy="select-session"]', "Zürich");
|
||||||
|
cy.get('[data-cy="due-date-list"]').children().should("have.length", 1);
|
||||||
|
selectDropboxItem('[data-cy="select-type"]', "Bewertung");
|
||||||
|
cy.get('[data-cy="due-date-list"]').should("contain", "Keine Termine");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,11 +1,20 @@
|
||||||
export const MENTOR_OVERVIEW_URL = "/course/versicherungsvermittler-in/learning-mentor";
|
export const MENTOR_TASKS_URL_VV =
|
||||||
export const MENTOR_MENTEES_URL = "/course/versicherungsvermittler-in/learning-mentor/participants";
|
"/course/versicherungsvermittler-in/learning-mentor/tasks";
|
||||||
|
export const MENTOR_MENTEES_URL_VV =
|
||||||
|
"/course/versicherungsvermittler-in/learning-mentor";
|
||||||
|
export const MENTOR_MENTEES_URL_UK = "/course/test-lehrgang/learning-mentor";
|
||||||
|
|
||||||
|
export const MAIN_NAVIGATION_MENTOR_LINK =
|
||||||
|
"[data-cy=navigation-learning-mentor-link]";
|
||||||
|
|
||||||
export const MENTOR_DASHBOARD_LINK = "[data-cy=lm-dashboard-link]";
|
export const MENTOR_DASHBOARD_LINK = "[data-cy=lm-dashboard-link]";
|
||||||
export const MEMBER_DASHBOARD_LINK = "[data-cy=progress-dashboard-continue-course-link]";
|
export const MEMBER_DASHBOARD_LINK =
|
||||||
|
"[data-cy=progress-dashboard-continue-course-link]";
|
||||||
export const MENTOR_MAIN_NAVIGATION = "[data-cy=lm-main-navigation]";
|
export const MENTOR_MAIN_NAVIGATION = "[data-cy=lm-main-navigation]";
|
||||||
export const MENTOR_OVERVIEW_NAVIGATION_LINK = "[data-cy=lm-overview-navigation-link]";
|
export const MENTOR_OVERVIEW_NAVIGATION_LINK =
|
||||||
export const MENTOR_MENTEES_NAVIGATION_LINK = "[data-cy=lm-mentees-navigation-link]";
|
"[data-cy=lm-overview-navigation-link]";
|
||||||
|
export const MENTOR_MENTEES_NAVIGATION_LINK =
|
||||||
|
"[data-cy=lm-mentees-navigation-link]";
|
||||||
|
|
||||||
// /participants
|
// /participants
|
||||||
export const MENTOR_MY_MENTEES = "[data-cy=lm-my-mentees]";
|
export const MENTOR_MY_MENTEES = "[data-cy=lm-my-mentees]";
|
||||||
|
|
@ -17,7 +26,5 @@ export const MENTOR_MENTEE_PROFILE = "[data-cy=lm-my-mentee-profile]";
|
||||||
|
|
||||||
export const MENTEE_MENTOR_LIST_ITEM = "[data-cy=lm-my-mentor-list-item]";
|
export const MENTEE_MENTOR_LIST_ITEM = "[data-cy=lm-my-mentor-list-item]";
|
||||||
export const MENTEE_MENTOR_REMOVE = "[data-cy=lm-my-mentor-remove]";
|
export const MENTEE_MENTOR_REMOVE = "[data-cy=lm-my-mentor-remove]";
|
||||||
|
export const MENTEE_MENTORS_TITLE = "[data-cy=lm-my-lms-title]";
|
||||||
|
export const MENTEE_INVITE_MENTOR = "[data-cy=lm-invite-mentor-button]";
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
import {login} from "../../helpers";
|
import { login } from "../../helpers";
|
||||||
import {
|
import {
|
||||||
|
MAIN_NAVIGATION_MENTOR_LINK,
|
||||||
MEMBER_DASHBOARD_LINK,
|
MEMBER_DASHBOARD_LINK,
|
||||||
|
MENTEE_INVITE_MENTOR,
|
||||||
MENTEE_MENTOR_LIST_ITEM,
|
MENTEE_MENTOR_LIST_ITEM,
|
||||||
MENTEE_MENTOR_REMOVE,
|
MENTEE_MENTOR_REMOVE,
|
||||||
|
MENTEE_MENTORS_TITLE,
|
||||||
MENTOR_MENTEES_NAVIGATION_LINK,
|
MENTOR_MENTEES_NAVIGATION_LINK,
|
||||||
MENTOR_MENTEES_URL,
|
MENTOR_MENTEES_URL_UK,
|
||||||
|
MENTOR_MENTEES_URL_VV,
|
||||||
MENTOR_MY_MENTEES,
|
MENTOR_MY_MENTEES,
|
||||||
MENTOR_MY_MENTORS,
|
MENTOR_MY_MENTORS,
|
||||||
MENTOR_OVERVIEW_NAVIGATION_LINK,
|
MENTOR_OVERVIEW_NAVIGATION_LINK,
|
||||||
MENTOR_OVERVIEW_URL
|
MENTOR_TASKS_URL_VV,
|
||||||
} from "../constants";
|
} from "../constants";
|
||||||
|
|
||||||
describe("memberOnly.cy.js", () => {
|
describe("memberOnly.cy.js", () => {
|
||||||
|
|
@ -23,22 +27,22 @@ describe("memberOnly.cy.js", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows NO mentees navigation link", () => {
|
it("shows NO mentees navigation link", () => {
|
||||||
cy.visit(MENTOR_OVERVIEW_URL);
|
cy.visit(MENTOR_TASKS_URL_VV);
|
||||||
cy.get(MENTOR_MENTEES_NAVIGATION_LINK).should("not.exist");
|
cy.get(MENTOR_MENTEES_NAVIGATION_LINK).should("not.exist");
|
||||||
})
|
});
|
||||||
|
|
||||||
it("shows NO overview navigation link", () => {
|
it("shows NO overview navigation link", () => {
|
||||||
cy.visit(MENTOR_OVERVIEW_URL);
|
cy.visit(MENTOR_TASKS_URL_VV);
|
||||||
cy.get(MENTOR_OVERVIEW_NAVIGATION_LINK).should("not.exist");
|
cy.get(MENTOR_OVERVIEW_NAVIGATION_LINK).should("not.exist");
|
||||||
})
|
});
|
||||||
|
|
||||||
it("shows NO mentees", () => {
|
it("shows NO mentees", () => {
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.get(MENTOR_MY_MENTEES).should("not.exist");
|
cy.get(MENTOR_MY_MENTEES).should("not.exist");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows my mentors", () => {
|
it("shows my mentors", () => {
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.get(MENTOR_MY_MENTORS).should("exist");
|
cy.get(MENTOR_MY_MENTORS).should("exist");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -47,12 +51,44 @@ describe("memberOnly.cy.js", () => {
|
||||||
const mentor = "Micheala Weber-Mentor";
|
const mentor = "Micheala Weber-Mentor";
|
||||||
|
|
||||||
// when
|
// when
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.contains(MENTEE_MENTOR_LIST_ITEM, mentor)
|
cy.contains(MENTEE_MENTOR_LIST_ITEM, mentor)
|
||||||
.find(MENTEE_MENTOR_REMOVE)
|
.find(MENTEE_MENTOR_REMOVE)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
cy.contains(MENTOR_MY_MENTORS, mentor).should("not.exist");
|
cy.contains(MENTOR_MY_MENTORS, mentor).should("not.exist");
|
||||||
})
|
});
|
||||||
|
|
||||||
|
it("uses term Lernbegleitung in VV-course", () => {
|
||||||
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
|
cy.get(MAIN_NAVIGATION_MENTOR_LINK).should("contain", "Lernbegleitung");
|
||||||
|
cy.get(MENTEE_MENTORS_TITLE).should("contain", "Meine Lernbegleiter");
|
||||||
|
cy.get(MENTEE_INVITE_MENTOR).should(
|
||||||
|
"contain",
|
||||||
|
"Neue Lernbegleitung einladen"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("memberOnly.cy.js üK", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.manageCommand("cypress_reset --set-only-is-uk-flag");
|
||||||
|
login("test-student1@example.com", "test");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows my mentors", () => {
|
||||||
|
cy.visit(MENTOR_MENTEES_URL_UK);
|
||||||
|
cy.get(MENTOR_MY_MENTORS).should("exist");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses term Praxisbildner in UK-course", () => {
|
||||||
|
cy.visit(MENTOR_MENTEES_URL_UK);
|
||||||
|
cy.get(MAIN_NAVIGATION_MENTOR_LINK).should("contain", "Praxisbildner");
|
||||||
|
cy.get(MENTEE_MENTORS_TITLE).should("contain", "Meine Praxisbildner");
|
||||||
|
cy.get(MENTEE_INVITE_MENTOR).should(
|
||||||
|
"contain",
|
||||||
|
"Neuen Praxisbildner einladen"
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import {login} from "../../helpers";
|
import { login } from "../../helpers";
|
||||||
import {
|
import {
|
||||||
MEMBER_DASHBOARD_LINK,
|
MEMBER_DASHBOARD_LINK,
|
||||||
MENTEE_MENTOR_LIST_ITEM,
|
MENTEE_MENTOR_LIST_ITEM,
|
||||||
|
|
@ -8,11 +8,11 @@ import {
|
||||||
MENTOR_MENTEE_PROFILE,
|
MENTOR_MENTEE_PROFILE,
|
||||||
MENTOR_MENTEE_REMOVE,
|
MENTOR_MENTEE_REMOVE,
|
||||||
MENTOR_MENTEES_NAVIGATION_LINK,
|
MENTOR_MENTEES_NAVIGATION_LINK,
|
||||||
MENTOR_MENTEES_URL,
|
MENTOR_MENTEES_URL_VV,
|
||||||
MENTOR_MY_MENTEES,
|
MENTOR_MY_MENTEES,
|
||||||
MENTOR_MY_MENTORS,
|
MENTOR_MY_MENTORS,
|
||||||
MENTOR_OVERVIEW_NAVIGATION_LINK,
|
MENTOR_OVERVIEW_NAVIGATION_LINK,
|
||||||
MENTOR_OVERVIEW_URL
|
MENTOR_TASKS_URL_VV,
|
||||||
} from "../constants";
|
} from "../constants";
|
||||||
|
|
||||||
describe("mentorAndMember.cy.js", () => {
|
describe("mentorAndMember.cy.js", () => {
|
||||||
|
|
@ -27,34 +27,34 @@ describe("mentorAndMember.cy.js", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows the learning mentor navigation", () => {
|
it("shows the learning mentor navigation", () => {
|
||||||
cy.visit(MENTOR_OVERVIEW_URL);
|
cy.visit(MENTOR_TASKS_URL_VV);
|
||||||
cy.get(MENTOR_MAIN_NAVIGATION).should("exist");
|
cy.get(MENTOR_MAIN_NAVIGATION).should("exist");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows the mentees navigation link", () => {
|
it("shows the mentees navigation link", () => {
|
||||||
cy.visit(MENTOR_OVERVIEW_URL);
|
cy.visit(MENTOR_TASKS_URL_VV);
|
||||||
cy.get(MENTOR_MENTEES_NAVIGATION_LINK).click();
|
cy.get(MENTOR_MENTEES_NAVIGATION_LINK).click();
|
||||||
cy.url().should("include", MENTOR_MENTEES_URL);
|
cy.url().should("include", MENTOR_MENTEES_URL_VV);
|
||||||
})
|
});
|
||||||
|
|
||||||
it("shows the overview navigation link", () => {
|
it("shows the overview navigation link", () => {
|
||||||
cy.visit(MENTOR_OVERVIEW_URL);
|
cy.visit(MENTOR_TASKS_URL_VV);
|
||||||
cy.get(MENTOR_OVERVIEW_NAVIGATION_LINK).click();
|
cy.get(MENTOR_OVERVIEW_NAVIGATION_LINK).click();
|
||||||
cy.url().should("include", MENTOR_OVERVIEW_URL);
|
cy.url().should("include", MENTOR_TASKS_URL_VV);
|
||||||
})
|
});
|
||||||
|
|
||||||
it("shows my mentees", () => {
|
it("shows my mentees", () => {
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.get(MENTOR_MY_MENTEES).should("exist");
|
cy.get(MENTOR_MY_MENTEES).should("exist");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows my mentors", () => {
|
it("shows my mentors", () => {
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.get(MENTOR_MY_MENTORS).should("exist");
|
cy.get(MENTOR_MY_MENTORS).should("exist");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows the correct mentees", () => {
|
it("shows the correct mentees", () => {
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.get(MENTOR_MY_MENTEES).should("contain", "Viktor Vollgas");
|
cy.get(MENTOR_MY_MENTEES).should("contain", "Viktor Vollgas");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -63,50 +63,55 @@ describe("mentorAndMember.cy.js", () => {
|
||||||
const mentee = "Viktor Vollgas";
|
const mentee = "Viktor Vollgas";
|
||||||
|
|
||||||
// when
|
// when
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.contains(MENTOR_MENTEE_LIST_ITEM, mentee)
|
cy.contains(MENTOR_MENTEE_LIST_ITEM, mentee)
|
||||||
.find(MENTOR_MENTEE_PROFILE)
|
.find(MENTOR_MENTEE_PROFILE)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
const expectedMenteeProfileUrl = "/course/versicherungsvermittler-in/profile/5ff59857-8de5-415e-a387-4449f9a0337a"
|
const expectedMenteeProfileUrl =
|
||||||
|
"/course/versicherungsvermittler-in/profile/5ff59857-8de5-415e-a387-4449f9a0337a";
|
||||||
cy.url().should("include", expectedMenteeProfileUrl);
|
cy.url().should("include", expectedMenteeProfileUrl);
|
||||||
cy.contains(mentee).should("exist");
|
cy.contains(mentee).should("exist");
|
||||||
})
|
});
|
||||||
|
|
||||||
it("can remove a mentee", () => {
|
it("can remove a mentee", () => {
|
||||||
// given
|
// given
|
||||||
const mentee = "Viktor Vollgas";
|
const mentee = "Viktor Vollgas";
|
||||||
|
|
||||||
// when
|
// when
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.contains(MENTOR_MENTEE_LIST_ITEM, mentee)
|
cy.contains(MENTOR_MENTEE_LIST_ITEM, mentee)
|
||||||
.find(MENTOR_MENTEE_REMOVE)
|
.find(MENTOR_MENTEE_REMOVE)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
cy.contains(MENTOR_MENTEE_LIST_ITEM, mentee).should("not.exist");
|
cy.contains(MENTOR_MENTEE_LIST_ITEM, mentee).should("not.exist");
|
||||||
cy.contains("Aktuell begleitest du niemanden als Lernbegleitung").should("exist");
|
cy.contains("Aktuell begleitest du niemanden als Lernbegleitung").should(
|
||||||
})
|
"exist"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("shows the correct mentors", () => {
|
it("shows the correct mentors", () => {
|
||||||
const mentor = "Micheala Weber-Mentor";
|
const mentor = "Micheala Weber-Mentor";
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.get(MENTOR_MY_MENTORS).should("contain", mentor);
|
cy.get(MENTOR_MY_MENTORS).should("contain", mentor);
|
||||||
})
|
});
|
||||||
|
|
||||||
it("can remove a mentor", () => {
|
it("can remove a mentor", () => {
|
||||||
// given
|
// given
|
||||||
const mentor = "Micheala Weber-Mentor";
|
const mentor = "Micheala Weber-Mentor";
|
||||||
|
|
||||||
// when
|
// when
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.contains(MENTEE_MENTOR_LIST_ITEM, mentor)
|
cy.contains(MENTEE_MENTOR_LIST_ITEM, mentor)
|
||||||
.find(MENTEE_MENTOR_REMOVE)
|
.find(MENTEE_MENTOR_REMOVE)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
cy.contains(MENTOR_MY_MENTORS, mentor).should("not.exist");
|
cy.contains(MENTOR_MY_MENTORS, mentor).should("not.exist");
|
||||||
cy.contains("Aktuell hast du noch keine Person als Lernbegleitung eingeladen").should("exist");
|
cy.contains(
|
||||||
})
|
"Aktuell hast du noch keine Person als Lernbegleitung eingeladen"
|
||||||
|
).should("exist");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import {login} from "../../helpers";
|
import { login } from "../../helpers";
|
||||||
import {
|
import {
|
||||||
MENTOR_DASHBOARD_LINK,
|
MENTOR_DASHBOARD_LINK,
|
||||||
MENTOR_MAIN_NAVIGATION,
|
MENTOR_MAIN_NAVIGATION,
|
||||||
|
|
@ -6,11 +6,11 @@ import {
|
||||||
MENTOR_MENTEE_PROFILE,
|
MENTOR_MENTEE_PROFILE,
|
||||||
MENTOR_MENTEE_REMOVE,
|
MENTOR_MENTEE_REMOVE,
|
||||||
MENTOR_MENTEES_NAVIGATION_LINK,
|
MENTOR_MENTEES_NAVIGATION_LINK,
|
||||||
MENTOR_MENTEES_URL,
|
MENTOR_MENTEES_URL_VV,
|
||||||
MENTOR_MY_MENTEES,
|
MENTOR_MY_MENTEES,
|
||||||
MENTOR_MY_MENTORS,
|
MENTOR_MY_MENTORS,
|
||||||
MENTOR_OVERVIEW_NAVIGATION_LINK,
|
MENTOR_OVERVIEW_NAVIGATION_LINK,
|
||||||
MENTOR_OVERVIEW_URL
|
MENTOR_TASKS_URL_VV,
|
||||||
} from "../constants";
|
} from "../constants";
|
||||||
|
|
||||||
describe("mentorOnly.cy.js", () => {
|
describe("mentorOnly.cy.js", () => {
|
||||||
|
|
@ -22,38 +22,38 @@ describe("mentorOnly.cy.js", () => {
|
||||||
it("shows the correct dashboard", () => {
|
it("shows the correct dashboard", () => {
|
||||||
cy.visit("/");
|
cy.visit("/");
|
||||||
cy.get(MENTOR_DASHBOARD_LINK).click();
|
cy.get(MENTOR_DASHBOARD_LINK).click();
|
||||||
cy.url().should("include", MENTOR_OVERVIEW_URL);
|
cy.url().should("include", MENTOR_MENTEES_URL_VV);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows the learning mentor navigation", () => {
|
it("shows the learning mentor navigation", () => {
|
||||||
cy.visit(MENTOR_OVERVIEW_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.get(MENTOR_MAIN_NAVIGATION).should("exist");
|
cy.get(MENTOR_MAIN_NAVIGATION).should("exist");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows the mentees navigation link", () => {
|
it("shows the mentees navigation link", () => {
|
||||||
cy.visit(MENTOR_OVERVIEW_URL);
|
cy.visit(MENTOR_TASKS_URL_VV);
|
||||||
cy.get(MENTOR_MENTEES_NAVIGATION_LINK).click();
|
cy.get(MENTOR_MENTEES_NAVIGATION_LINK).click();
|
||||||
cy.url().should("include", MENTOR_MENTEES_URL);
|
cy.url().should("include", MENTOR_MENTEES_URL_VV);
|
||||||
})
|
});
|
||||||
|
|
||||||
it("shows the overview navigation link", () => {
|
it("shows the overview navigation link", () => {
|
||||||
cy.visit(MENTOR_OVERVIEW_URL);
|
cy.visit(MENTOR_TASKS_URL_VV);
|
||||||
cy.get(MENTOR_OVERVIEW_NAVIGATION_LINK).click();
|
cy.get(MENTOR_OVERVIEW_NAVIGATION_LINK).click();
|
||||||
cy.url().should("include", MENTOR_OVERVIEW_URL);
|
cy.url().should("include", MENTOR_TASKS_URL_VV);
|
||||||
})
|
});
|
||||||
|
|
||||||
it("shows my mentees", () => {
|
it("shows my mentees", () => {
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.get(MENTOR_MY_MENTEES).should("exist");
|
cy.get(MENTOR_MY_MENTEES).should("exist");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows no mentors", () => {
|
it("shows no mentors", () => {
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.get(MENTOR_MY_MENTORS).should("not.exist");
|
cy.get(MENTOR_MY_MENTORS).should("not.exist");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows the correct mentees", () => {
|
it("shows the correct mentees", () => {
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.get(MENTOR_MY_MENTEES).should("contain", "Robert Student-plus-Mentor");
|
cy.get(MENTOR_MY_MENTEES).should("contain", "Robert Student-plus-Mentor");
|
||||||
cy.get(MENTOR_MY_MENTEES).should("contain", "Viktor Vollgas");
|
cy.get(MENTOR_MY_MENTEES).should("contain", "Viktor Vollgas");
|
||||||
});
|
});
|
||||||
|
|
@ -63,29 +63,32 @@ describe("mentorOnly.cy.js", () => {
|
||||||
const mentee = "Viktor Vollgas";
|
const mentee = "Viktor Vollgas";
|
||||||
|
|
||||||
// when
|
// when
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.contains(MENTOR_MENTEE_LIST_ITEM, mentee)
|
cy.contains(MENTOR_MENTEE_LIST_ITEM, mentee)
|
||||||
.find(MENTOR_MENTEE_PROFILE)
|
.find(MENTOR_MENTEE_PROFILE)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
const expectedMenteeProfileUrl = "/course/versicherungsvermittler-in/profile/5ff59857-8de5-415e-a387-4449f9a0337a"
|
const expectedMenteeProfileUrl =
|
||||||
|
"/course/versicherungsvermittler-in/profile/5ff59857-8de5-415e-a387-4449f9a0337a";
|
||||||
cy.url().should("include", expectedMenteeProfileUrl);
|
cy.url().should("include", expectedMenteeProfileUrl);
|
||||||
cy.contains(mentee).should("exist");
|
cy.contains(mentee).should("exist");
|
||||||
})
|
});
|
||||||
|
|
||||||
it("can remove a mentee", () => {
|
it("can remove a mentee", () => {
|
||||||
// given
|
// given
|
||||||
const mentee = "Viktor Vollgas";
|
const mentee = "Viktor Vollgas";
|
||||||
|
|
||||||
// when
|
// when
|
||||||
cy.visit(MENTOR_MENTEES_URL);
|
cy.visit(MENTOR_MENTEES_URL_VV);
|
||||||
cy.contains(MENTOR_MENTEE_LIST_ITEM, mentee)
|
cy.contains(MENTOR_MENTEE_LIST_ITEM, mentee)
|
||||||
.find(MENTOR_MENTEE_REMOVE)
|
.find(MENTOR_MENTEE_REMOVE)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
cy.contains(MENTOR_MENTEE_LIST_ITEM, mentee).should("not.exist");
|
cy.contains(MENTOR_MENTEE_LIST_ITEM, mentee).should("not.exist");
|
||||||
cy.contains(MENTOR_MENTEE_LIST_ITEM, "Robert Student-plus-Mentor").should("exist")
|
cy.contains(MENTOR_MENTEE_LIST_ITEM, "Robert Student-plus-Mentor").should(
|
||||||
})
|
"exist"
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,19 @@ describe("login.cy.js", () => {
|
||||||
cy.get("#username").type("test-student1@example.com");
|
cy.get("#username").type("test-student1@example.com");
|
||||||
cy.get("#password").type("test");
|
cy.get("#password").type("test");
|
||||||
|
|
||||||
cy.get("[data-cy=\"login-button\"]").click();
|
cy.get('[data-cy="login-button"]').click();
|
||||||
cy.request("/api/core/me").its("status").should("eq", 200);
|
cy.request("/api/core/me").its("status").should("eq", 200);
|
||||||
|
|
||||||
cy.get("[data-cy=\"dashboard-title\"]").should("contain", "Dashboard");
|
cy.get('[data-cy="db-course-title"]')
|
||||||
|
.first()
|
||||||
|
.should("contain", "Test Lehrgang");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can login with helper function", () => {
|
it("can login with helper function", () => {
|
||||||
login("test-student1@example.com", "test");
|
login("test-student1@example.com", "test");
|
||||||
cy.visit("/");
|
cy.visit("/");
|
||||||
cy.request("/api/core/me").its("status").should("eq", 200);
|
cy.request("/api/core/me").its("status").should("eq", 200);
|
||||||
cy.get("[data-cy=\"dashboard-title\"]").should("contain", "Dashboard");
|
cy.get('[data-cy="db-course-title"]').should("contain", "Test Lehrgang");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("login will redirect to requested page", () => {
|
it("login will redirect to requested page", () => {
|
||||||
|
|
@ -36,9 +38,9 @@ describe("login.cy.js", () => {
|
||||||
cy.get("#username").type("test-student1@example.com");
|
cy.get("#username").type("test-student1@example.com");
|
||||||
cy.get("#password").type("test");
|
cy.get("#password").type("test");
|
||||||
|
|
||||||
cy.get("[data-cy=\"login-button\"]").click();
|
cy.get('[data-cy="login-button"]').click();
|
||||||
|
|
||||||
cy.get("[data-cy=\"learning-path-title\"]").should(
|
cy.get('[data-cy="learning-path-title"]').should(
|
||||||
"contain",
|
"contain",
|
||||||
"Test Lehrgang"
|
"Test Lehrgang"
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
# pylint: disable=unused-wildcard-import,wildcard-import,wrong-import-position
|
||||||
|
import os
|
||||||
|
|
||||||
|
from environs import Env
|
||||||
|
|
||||||
|
script_path = os.path.abspath(__file__)
|
||||||
|
script_dir = os.path.dirname(script_path)
|
||||||
|
|
||||||
|
env = Env()
|
||||||
|
env.read_env(f"{script_dir}/../../../env_secrets/local_daniel.env", recurse=False)
|
||||||
|
|
||||||
|
from .base import * # noqa
|
||||||
|
|
@ -39,6 +39,13 @@ from vbv_lernwelt.course.views import (
|
||||||
request_course_completion_for_user,
|
request_course_completion_for_user,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course_session.views import get_course_session_documents
|
from vbv_lernwelt.course_session.views import get_course_session_documents
|
||||||
|
from vbv_lernwelt.dashboard.views import (
|
||||||
|
get_dashboard_config,
|
||||||
|
get_dashboard_due_dates,
|
||||||
|
get_dashboard_persons,
|
||||||
|
get_mentee_count,
|
||||||
|
get_mentor_open_tasks_count,
|
||||||
|
)
|
||||||
from vbv_lernwelt.edoniq_test.views import (
|
from vbv_lernwelt.edoniq_test.views import (
|
||||||
export_students,
|
export_students,
|
||||||
export_students_and_trainers,
|
export_students_and_trainers,
|
||||||
|
|
@ -116,6 +123,14 @@ urlpatterns = [
|
||||||
re_path(r"api/notify/email_notification_settings/$", email_notification_settings,
|
re_path(r"api/notify/email_notification_settings/$", email_notification_settings,
|
||||||
name='email_notification_settings'),
|
name='email_notification_settings'),
|
||||||
|
|
||||||
|
# dashboard
|
||||||
|
path(r"api/dashboard/persons/", get_dashboard_persons, name="get_dashboard_persons"),
|
||||||
|
path(r"api/dashboard/duedates/", get_dashboard_due_dates, name="get_dashboard_due_dates"),
|
||||||
|
path(r"api/dashboard/config/", get_dashboard_config, name="get_dashboard_config"),
|
||||||
|
path(r"api/dashboard/course/<str:course_id>/mentees/", get_mentee_count, name="get_mentee_count"),
|
||||||
|
path(r"api/dashboard/course/<str:course_id>/open_tasks/", get_mentor_open_tasks_count,
|
||||||
|
name="get_mentor_open_tasks_count"),
|
||||||
|
|
||||||
# course
|
# course
|
||||||
path(r"api/course/sessions/", get_course_sessions, name="get_course_sessions"),
|
path(r"api/course/sessions/", get_course_sessions, name="get_course_sessions"),
|
||||||
path(r"api/course/page/<slug_or_id>/", course_page_api_view,
|
path(r"api/course/page/<slug_or_id>/", course_page_api_view,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@ from wagtail.blocks.list_block import ListBlock, ListValue
|
||||||
from wagtail.rich_text import RichText
|
from wagtail.rich_text import RichText
|
||||||
|
|
||||||
|
|
||||||
def create_uk_fahrzeug_casework(course_id=COURSE_UK, competence_certificate=None):
|
def create_uk_fahrzeug_casework(
|
||||||
|
course_id=COURSE_UK, competence_certificate=None, with_documents=False
|
||||||
|
):
|
||||||
assignment_list_page = (
|
assignment_list_page = (
|
||||||
CoursePage.objects.get(course_id=course_id)
|
CoursePage.objects.get(course_id=course_id)
|
||||||
.get_children()
|
.get_children()
|
||||||
|
|
@ -40,7 +42,6 @@ def create_uk_fahrzeug_casework(course_id=COURSE_UK, competence_certificate=None
|
||||||
needs_expert_evaluation=True,
|
needs_expert_evaluation=True,
|
||||||
competence_certificate=competence_certificate,
|
competence_certificate=competence_certificate,
|
||||||
effort_required="ca. 5 Stunden",
|
effort_required="ca. 5 Stunden",
|
||||||
solution_sample=ContentDocument.objects.get(title="Musterlösung Fahrzeug"),
|
|
||||||
intro_text=replace_whitespace(
|
intro_text=replace_whitespace(
|
||||||
"""
|
"""
|
||||||
<h3>Ausgangslage</h3>
|
<h3>Ausgangslage</h3>
|
||||||
|
|
@ -70,6 +71,11 @@ def create_uk_fahrzeug_casework(course_id=COURSE_UK, competence_certificate=None
|
||||||
evaluation_document_url="/static/media/assignments/UK_03_09_NACH_KN_Beurteilungsraster.pdf",
|
evaluation_document_url="/static/media/assignments/UK_03_09_NACH_KN_Beurteilungsraster.pdf",
|
||||||
evaluation_description="Diese geleitete Fallarbeit wird auf Grund des folgenden Beurteilungsintrument bewertet.",
|
evaluation_description="Diese geleitete Fallarbeit wird auf Grund des folgenden Beurteilungsintrument bewertet.",
|
||||||
)
|
)
|
||||||
|
if with_documents:
|
||||||
|
assignment.solution_sample = ContentDocument.objects.get(
|
||||||
|
title="Musterlösung Fahrzeug"
|
||||||
|
)
|
||||||
|
assignment.save()
|
||||||
|
|
||||||
assignment.evaluation_tasks = []
|
assignment.evaluation_tasks = []
|
||||||
assignment.evaluation_tasks.append(
|
assignment.evaluation_tasks.append(
|
||||||
|
|
@ -3591,7 +3597,7 @@ def create_uk_reflection(course_id=COURSE_UK):
|
||||||
assignment = AssignmentFactory(
|
assignment = AssignmentFactory(
|
||||||
parent=assignment_list_page,
|
parent=assignment_list_page,
|
||||||
assignment_type=AssignmentType.REFLECTION.name,
|
assignment_type=AssignmentType.REFLECTION.name,
|
||||||
title=f"Reflexion",
|
title="Reflexion",
|
||||||
effort_required="ca. 1 Stunde",
|
effort_required="ca. 1 Stunde",
|
||||||
intro_text=replace_whitespace(
|
intro_text=replace_whitespace(
|
||||||
"""
|
"""
|
||||||
|
|
@ -3747,7 +3753,7 @@ def create_uk_fr_reflection(course_id=COURSE_UK_FR, circle_title="Véhicule"):
|
||||||
assignment = AssignmentFactory(
|
assignment = AssignmentFactory(
|
||||||
parent=assignment_list_page,
|
parent=assignment_list_page,
|
||||||
assignment_type=AssignmentType.REFLECTION.name,
|
assignment_type=AssignmentType.REFLECTION.name,
|
||||||
title=f"Reflexion",
|
title="Reflexion",
|
||||||
effort_required="",
|
effort_required="",
|
||||||
intro_text=replace_whitespace(
|
intro_text=replace_whitespace(
|
||||||
"""
|
"""
|
||||||
|
|
@ -3900,7 +3906,7 @@ def create_uk_it_reflection(course_id=COURSE_UK_FR, circle_title="Véhicule"):
|
||||||
assignment = AssignmentFactory(
|
assignment = AssignmentFactory(
|
||||||
parent=assignment_list_page,
|
parent=assignment_list_page,
|
||||||
assignment_type=AssignmentType.REFLECTION.name,
|
assignment_type=AssignmentType.REFLECTION.name,
|
||||||
title=f"Riflessione",
|
title="Riflessione",
|
||||||
effort_required="",
|
effort_required="",
|
||||||
intro_text=replace_whitespace(
|
intro_text=replace_whitespace(
|
||||||
"""
|
"""
|
||||||
|
|
@ -4053,7 +4059,7 @@ def create_vv_reflection(
|
||||||
assignment = AssignmentFactory(
|
assignment = AssignmentFactory(
|
||||||
parent=assignment_list_page,
|
parent=assignment_list_page,
|
||||||
assignment_type=AssignmentType.REFLECTION.name,
|
assignment_type=AssignmentType.REFLECTION.name,
|
||||||
title=f"Reflexion",
|
title="Reflexion",
|
||||||
effort_required="ca. 1 Stunde",
|
effort_required="ca. 1 Stunde",
|
||||||
intro_text=replace_whitespace(
|
intro_text=replace_whitespace(
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,10 @@ class AssignmentObjectType(DjangoObjectType):
|
||||||
lp = self.find_attached_learning_content()
|
lp = self.find_attached_learning_content()
|
||||||
if lp:
|
if lp:
|
||||||
learning_content_page_id = lp.id
|
learning_content_page_id = lp.id
|
||||||
|
|
||||||
|
if not assignment_user_id:
|
||||||
|
assignment_user_id = getattr(info.context, "assignment_user_id", None)
|
||||||
|
|
||||||
return resolve_assignment_completion(
|
return resolve_assignment_completion(
|
||||||
info=info,
|
info=info,
|
||||||
course_session_id=course_session_id,
|
course_session_id=course_session_id,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ from vbv_lernwelt.competence.models import (
|
||||||
CompetenceCertificateList,
|
CompetenceCertificateList,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course.graphql.types import resolve_course_page
|
from vbv_lernwelt.course.graphql.types import resolve_course_page
|
||||||
|
from vbv_lernwelt.course.models import CourseSessionUser
|
||||||
|
from vbv_lernwelt.iam.permissions import can_view_profile
|
||||||
|
|
||||||
|
|
||||||
class CompetenceCertificateQuery(object):
|
class CompetenceCertificateQuery(object):
|
||||||
|
|
@ -24,6 +26,15 @@ class CompetenceCertificateQuery(object):
|
||||||
course_slug=graphene.String(),
|
course_slug=graphene.String(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
competence_certificate_list_for_user = graphene.Field(
|
||||||
|
CompetenceCertificateListObjectType,
|
||||||
|
id=graphene.ID(),
|
||||||
|
slug=graphene.String(),
|
||||||
|
course_id=graphene.ID(),
|
||||||
|
course_slug=graphene.String(),
|
||||||
|
user_id=graphene.UUID(),
|
||||||
|
)
|
||||||
|
|
||||||
def resolve_competence_certificate(root, info, id=None, slug=None):
|
def resolve_competence_certificate(root, info, id=None, slug=None):
|
||||||
return resolve_course_page(CompetenceCertificate, root, info, id=id, slug=slug)
|
return resolve_course_page(CompetenceCertificate, root, info, id=id, slug=slug)
|
||||||
|
|
||||||
|
|
@ -39,3 +50,26 @@ class CompetenceCertificateQuery(object):
|
||||||
course_id=course_id,
|
course_id=course_id,
|
||||||
course_slug=course_slug,
|
course_slug=course_slug,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def resolve_competence_certificate_list_for_user(
|
||||||
|
root, info, id=None, slug=None, course_id=None, course_slug=None, user_id=None
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
course_session_user = CourseSessionUser.objects.get(user__id=user_id)
|
||||||
|
except CourseSessionUser.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not can_view_profile(info.context.user, course_session_user):
|
||||||
|
return None
|
||||||
|
|
||||||
|
setattr(info.context, "assignment_user_id", user_id)
|
||||||
|
|
||||||
|
return resolve_course_page(
|
||||||
|
CompetenceCertificateList,
|
||||||
|
root,
|
||||||
|
info,
|
||||||
|
id=id,
|
||||||
|
slug=slug,
|
||||||
|
course_id=course_id,
|
||||||
|
course_slug=course_slug,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
from vbv_lernwelt.assignment.models import AssignmentType
|
||||||
|
from vbv_lernwelt.course_session.models import (
|
||||||
|
CourseSessionAssignment,
|
||||||
|
CourseSessionEdoniqTest,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def query_competence_course_session_assignments(course_session_ids, circle_ids=None):
|
||||||
|
if circle_ids is None:
|
||||||
|
circle_ids = []
|
||||||
|
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for csa in CourseSessionAssignment.objects.filter(
|
||||||
|
course_session_id__in=course_session_ids,
|
||||||
|
learning_content__content_assignment__assignment_type__in=[
|
||||||
|
AssignmentType.CASEWORK.value,
|
||||||
|
],
|
||||||
|
learning_content__content_assignment__competence_certificate__isnull=False,
|
||||||
|
):
|
||||||
|
if circle_ids and csa.learning_content.get_circle().id not in circle_ids:
|
||||||
|
continue
|
||||||
|
result.append(csa)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def query_competence_course_session_edoniq_tests(course_session_ids, circle_ids=None):
|
||||||
|
if circle_ids is None:
|
||||||
|
circle_ids = []
|
||||||
|
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for cset in CourseSessionEdoniqTest.objects.filter(
|
||||||
|
course_session_id__in=course_session_ids,
|
||||||
|
learning_content__content_assignment__competence_certificate__isnull=False,
|
||||||
|
):
|
||||||
|
if circle_ids and cset.learning_content.get_circle().id not in circle_ids:
|
||||||
|
continue
|
||||||
|
result.append(cset)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
@ -0,0 +1,307 @@
|
||||||
|
from django.utils import timezone
|
||||||
|
from graphene_django.utils import GraphQLTestCase
|
||||||
|
|
||||||
|
from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletion
|
||||||
|
from vbv_lernwelt.core.create_default_users import create_default_users
|
||||||
|
from vbv_lernwelt.course.creators.test_course import create_test_course
|
||||||
|
from vbv_lernwelt.course.creators.test_utils import (
|
||||||
|
add_course_session_group_supervisor,
|
||||||
|
add_course_session_user,
|
||||||
|
create_course_session_group,
|
||||||
|
create_user,
|
||||||
|
)
|
||||||
|
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||||
|
|
||||||
|
|
||||||
|
class TestCertificateList(GraphQLTestCase):
|
||||||
|
GRAPHQL_URL = "/server/graphql/"
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
create_default_users()
|
||||||
|
self.course = create_test_course(include_vv=False, with_sessions=True)
|
||||||
|
self.course_session = CourseSession.objects.get(title="Test Bern 2022 a")
|
||||||
|
assignment = Assignment.objects.get(
|
||||||
|
slug="test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.supervisor = create_user("supervisor")
|
||||||
|
group = create_course_session_group(course_session=self.course_session)
|
||||||
|
add_course_session_group_supervisor(group=group, user=self.supervisor)
|
||||||
|
|
||||||
|
self.member_one = create_user("member one")
|
||||||
|
add_course_session_user(
|
||||||
|
course_session=self.course_session,
|
||||||
|
user=self.member_one,
|
||||||
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.member_two = create_user("member two")
|
||||||
|
add_course_session_user(
|
||||||
|
course_session=self.course_session,
|
||||||
|
user=self.member_two,
|
||||||
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
|
)
|
||||||
|
|
||||||
|
AssignmentCompletion.objects.create(
|
||||||
|
assignment_user=self.member_one,
|
||||||
|
assignment=assignment,
|
||||||
|
learning_content_page=assignment.find_attached_learning_content(),
|
||||||
|
course_session=self.course_session,
|
||||||
|
completion_status="SUBMITTED",
|
||||||
|
submitted_at=timezone.now(),
|
||||||
|
completion_data={},
|
||||||
|
evaluation_max_points=10,
|
||||||
|
evaluation_points=5,
|
||||||
|
evaluation_passed=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
AssignmentCompletion.objects.create(
|
||||||
|
assignment_user=self.member_two,
|
||||||
|
assignment=assignment,
|
||||||
|
learning_content_page=assignment.find_attached_learning_content(),
|
||||||
|
course_session=self.course_session,
|
||||||
|
completion_status="SUBMITTED",
|
||||||
|
submitted_at=timezone.now(),
|
||||||
|
completion_data={},
|
||||||
|
evaluation_max_points=10,
|
||||||
|
evaluation_points=10,
|
||||||
|
evaluation_passed=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_supervisor_userprofile_certificate_summary(self):
|
||||||
|
self.client.force_login(self.supervisor)
|
||||||
|
|
||||||
|
query = f"""query competenceCertificateForUserQuery(
|
||||||
|
$courseSlug: String!,
|
||||||
|
$courseSessionId: ID!,
|
||||||
|
$userId: UUID!
|
||||||
|
) {{
|
||||||
|
competence_certificate_list_for_user(
|
||||||
|
course_slug: $courseSlug,
|
||||||
|
user_id: $userId
|
||||||
|
) {{
|
||||||
|
...CoursePageFields
|
||||||
|
competence_certificates {{
|
||||||
|
...CoursePageFields
|
||||||
|
assignments {{
|
||||||
|
...CoursePageFields
|
||||||
|
assignment_type
|
||||||
|
max_points
|
||||||
|
completion(course_session_id: $courseSessionId) {{
|
||||||
|
id
|
||||||
|
completion_status
|
||||||
|
submitted_at
|
||||||
|
evaluation_points
|
||||||
|
evaluation_max_points
|
||||||
|
evaluation_passed
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
learning_content {{
|
||||||
|
...CoursePageFields
|
||||||
|
circle {{
|
||||||
|
id
|
||||||
|
title
|
||||||
|
slug
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
fragment CoursePageFields on CoursePageInterface {{
|
||||||
|
title
|
||||||
|
id
|
||||||
|
slug
|
||||||
|
content_type
|
||||||
|
frontend_url
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
variables = {
|
||||||
|
"courseSessionId": str(self.course_session.id),
|
||||||
|
"courseSlug": self.course.slug,
|
||||||
|
"userId": str(self.member_one.id),
|
||||||
|
}
|
||||||
|
|
||||||
|
# WHEN
|
||||||
|
response = self.query(query, variables=variables)
|
||||||
|
|
||||||
|
# THEN
|
||||||
|
self.assertResponseNoErrors(response)
|
||||||
|
|
||||||
|
certificates = response.json()["data"]["competence_certificate_list_for_user"][
|
||||||
|
"competence_certificates"
|
||||||
|
]
|
||||||
|
self.assertEqual(len(certificates), 1)
|
||||||
|
|
||||||
|
assignments = certificates[0]["assignments"]
|
||||||
|
self.assertEqual(len(assignments), 2)
|
||||||
|
|
||||||
|
completion1 = assignments[0]["completion"]
|
||||||
|
self.assertEqual(completion1["completion_status"], "SUBMITTED")
|
||||||
|
self.assertEqual(completion1["evaluation_points"], 5)
|
||||||
|
self.assertEqual(completion1["evaluation_max_points"], 10)
|
||||||
|
self.assertEqual(completion1["evaluation_passed"], False)
|
||||||
|
|
||||||
|
completion2 = assignments[1]["completion"]
|
||||||
|
self.assertIsNone(completion2)
|
||||||
|
|
||||||
|
def test_member_cannot_see_other_user_certificate_summary(self):
|
||||||
|
self.client.force_login(self.member_one)
|
||||||
|
|
||||||
|
query = f"""query competenceCertificateForUserQuery(
|
||||||
|
$courseSlug: String!,
|
||||||
|
$courseSessionId: ID!,
|
||||||
|
$userId: UUID!
|
||||||
|
) {{
|
||||||
|
competence_certificate_list_for_user(
|
||||||
|
course_slug: $courseSlug,
|
||||||
|
user_id: $userId
|
||||||
|
) {{
|
||||||
|
...CoursePageFields
|
||||||
|
competence_certificates {{
|
||||||
|
...CoursePageFields
|
||||||
|
assignments {{
|
||||||
|
...CoursePageFields
|
||||||
|
assignment_type
|
||||||
|
max_points
|
||||||
|
completion(course_session_id: $courseSessionId) {{
|
||||||
|
id
|
||||||
|
completion_status
|
||||||
|
submitted_at
|
||||||
|
evaluation_points
|
||||||
|
evaluation_max_points
|
||||||
|
evaluation_passed
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
learning_content {{
|
||||||
|
...CoursePageFields
|
||||||
|
circle {{
|
||||||
|
id
|
||||||
|
title
|
||||||
|
slug
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
fragment CoursePageFields on CoursePageInterface {{
|
||||||
|
title
|
||||||
|
id
|
||||||
|
slug
|
||||||
|
content_type
|
||||||
|
frontend_url
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
variables = {
|
||||||
|
"courseSessionId": str(self.course_session.id),
|
||||||
|
"courseSlug": self.course.slug,
|
||||||
|
"userId": str(self.member_two.id),
|
||||||
|
}
|
||||||
|
|
||||||
|
# WHEN
|
||||||
|
response = self.query(query, variables=variables)
|
||||||
|
|
||||||
|
# THEN
|
||||||
|
self.assertResponseNoErrors(response)
|
||||||
|
self.assertIsNone(
|
||||||
|
response.json()["data"]["competence_certificate_list_for_user"]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_member_userprofile_certificate_summary(self):
|
||||||
|
self.client.force_login(self.member_one)
|
||||||
|
|
||||||
|
query = f"""query competenceCertificateForUserQuery(
|
||||||
|
$courseSlug: String!,
|
||||||
|
$courseSessionId: ID!,
|
||||||
|
) {{
|
||||||
|
competence_certificate_list(
|
||||||
|
course_slug: $courseSlug,
|
||||||
|
) {{
|
||||||
|
...CoursePageFields
|
||||||
|
competence_certificates {{
|
||||||
|
...CoursePageFields
|
||||||
|
assignments {{
|
||||||
|
...CoursePageFields
|
||||||
|
assignment_type
|
||||||
|
max_points
|
||||||
|
completion(course_session_id: $courseSessionId) {{
|
||||||
|
id
|
||||||
|
completion_status
|
||||||
|
submitted_at
|
||||||
|
evaluation_points
|
||||||
|
evaluation_max_points
|
||||||
|
evaluation_passed
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
learning_content {{
|
||||||
|
...CoursePageFields
|
||||||
|
circle {{
|
||||||
|
id
|
||||||
|
title
|
||||||
|
slug
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
fragment CoursePageFields on CoursePageInterface {{
|
||||||
|
title
|
||||||
|
id
|
||||||
|
slug
|
||||||
|
content_type
|
||||||
|
frontend_url
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
variables = {
|
||||||
|
"courseSessionId": str(self.course_session.id),
|
||||||
|
"courseSlug": self.course.slug,
|
||||||
|
}
|
||||||
|
|
||||||
|
# WHEN
|
||||||
|
response = self.query(query, variables=variables)
|
||||||
|
|
||||||
|
# THEN
|
||||||
|
self.assertResponseNoErrors(response)
|
||||||
|
|
||||||
|
certificates = response.json()["data"]["competence_certificate_list"][
|
||||||
|
"competence_certificates"
|
||||||
|
]
|
||||||
|
self.assertEqual(len(certificates), 1)
|
||||||
|
|
||||||
|
assignments = certificates[0]["assignments"]
|
||||||
|
self.assertEqual(len(assignments), 2)
|
||||||
|
|
||||||
|
completion1 = assignments[0]["completion"]
|
||||||
|
self.assertEqual(completion1["completion_status"], "SUBMITTED")
|
||||||
|
self.assertEqual(completion1["evaluation_points"], 5)
|
||||||
|
self.assertEqual(completion1["evaluation_max_points"], 10)
|
||||||
|
self.assertEqual(completion1["evaluation_passed"], False)
|
||||||
|
|
||||||
|
completion2 = assignments[1]["completion"]
|
||||||
|
self.assertIsNone(completion2)
|
||||||
|
|
@ -98,6 +98,11 @@ from vbv_lernwelt.self_evaluation_feedback.models import (
|
||||||
default=False,
|
default=False,
|
||||||
help="Will create a learning mentor for test user",
|
help="Will create a learning mentor for test user",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"--set-only-is-uk-flag/--no-set-only-is-uk-flag",
|
||||||
|
default=False,
|
||||||
|
help="Will set only the is_uk flag for the test course and enable learning mentors for the course",
|
||||||
|
)
|
||||||
def command(
|
def command(
|
||||||
create_assignment_completion,
|
create_assignment_completion,
|
||||||
create_assignment_evaluation,
|
create_assignment_evaluation,
|
||||||
|
|
@ -108,6 +113,7 @@ def command(
|
||||||
create_attendance_days,
|
create_attendance_days,
|
||||||
enable_circle_documents,
|
enable_circle_documents,
|
||||||
create_learning_mentor,
|
create_learning_mentor,
|
||||||
|
set_only_is_uk_flag,
|
||||||
):
|
):
|
||||||
print("cypress reset data")
|
print("cypress reset data")
|
||||||
CourseCompletion.objects.all().delete()
|
CourseCompletion.objects.all().delete()
|
||||||
|
|
@ -391,4 +397,13 @@ def command(
|
||||||
|
|
||||||
course = Course.objects.get(id=COURSE_TEST_ID)
|
course = Course.objects.get(id=COURSE_TEST_ID)
|
||||||
course.configuration.enable_circle_documents = enable_circle_documents
|
course.configuration.enable_circle_documents = enable_circle_documents
|
||||||
|
course.configuration.is_uk = True
|
||||||
|
|
||||||
|
if set_only_is_uk_flag:
|
||||||
|
course.configuration.is_vv = False
|
||||||
|
course.configuration.enable_learning_mentor = True
|
||||||
|
else:
|
||||||
|
course.configuration.is_vv = True
|
||||||
|
course.configuration.enable_learning_mentor = False
|
||||||
|
|
||||||
course.configuration.save()
|
course.configuration.save()
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
import structlog
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import JSONField
|
from django.db.models import JSONField
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Organisation(models.Model):
|
class Organisation(models.Model):
|
||||||
organisation_id = models.IntegerField(primary_key=True)
|
organisation_id = models.IntegerField(primary_key=True)
|
||||||
|
|
@ -107,15 +110,24 @@ class User(AbstractUser):
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def create_avatar_url(self, size=400):
|
||||||
|
try:
|
||||||
|
if self.avatar:
|
||||||
|
filter_spec = f"fill-{size}x{size}"
|
||||||
|
self.avatar.get_rendition(filter_spec)
|
||||||
|
url = reverse("user_image", kwargs={"image_id": self.avatar.id})
|
||||||
|
return f"{url}?filter={filter_spec}"
|
||||||
|
except Exception:
|
||||||
|
logger.warn("could not create avatar url", label="security", exc_info=True)
|
||||||
|
return "/static/avatars/myvbv-default-avatar.png"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def avatar_url(self):
|
def avatar_url(self):
|
||||||
if self.avatar:
|
return self.create_avatar_url()
|
||||||
filter_spec = "fill-400x400"
|
|
||||||
self.avatar.get_rendition(filter_spec)
|
@property
|
||||||
url = reverse("user_image", kwargs={"image_id": self.avatar.id})
|
def avatar_url_small(self):
|
||||||
return f"{url}?filter={filter_spec}"
|
return self.create_avatar_url(size=96)
|
||||||
else:
|
|
||||||
return "/static/avatars/myvbv-default-avatar.png"
|
|
||||||
|
|
||||||
|
|
||||||
class SecurityRequestResponseLog(models.Model):
|
class SecurityRequestResponseLog(models.Model):
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ class CourseConfigurationAdmin(admin.ModelAdmin):
|
||||||
"enable_circle_documents",
|
"enable_circle_documents",
|
||||||
"enable_learning_mentor",
|
"enable_learning_mentor",
|
||||||
"enable_competence_certificates",
|
"enable_competence_certificates",
|
||||||
|
"is_vv",
|
||||||
|
"is_uk",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from dateutil.relativedelta import MO, relativedelta, TH, TU
|
from dateutil.relativedelta import MO, relativedelta, TH, TU, WE
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
from wagtail.rich_text import RichText
|
from wagtail.rich_text import RichText
|
||||||
|
|
@ -47,6 +47,7 @@ from vbv_lernwelt.course.factories import CoursePageFactory
|
||||||
from vbv_lernwelt.course.models import (
|
from vbv_lernwelt.course.models import (
|
||||||
Course,
|
Course,
|
||||||
CourseCategory,
|
CourseCategory,
|
||||||
|
CourseConfiguration,
|
||||||
CoursePage,
|
CoursePage,
|
||||||
CourseSession,
|
CourseSession,
|
||||||
CourseSessionUser,
|
CourseSessionUser,
|
||||||
|
|
@ -97,16 +98,18 @@ from vbv_lernwelt.media_library.tests.media_library_factories import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
|
def create_test_course(
|
||||||
|
include_uk=True, include_vv=True, with_sessions=False, with_documents=False
|
||||||
|
):
|
||||||
# create_locales_for_wagtail()
|
# create_locales_for_wagtail()
|
||||||
create_default_collections()
|
create_default_collections()
|
||||||
create_default_content_documents()
|
if with_documents:
|
||||||
if UserImage.objects.count() == 0 and ContentImage.objects.count() == 0:
|
create_default_content_documents()
|
||||||
create_default_images()
|
if UserImage.objects.count() == 0 and ContentImage.objects.count() == 0:
|
||||||
|
create_default_images()
|
||||||
|
|
||||||
course: Course = create_test_course_with_categories()
|
course: Course = create_test_course_with_categories()
|
||||||
course.configuration.enable_learning_mentor = False
|
course.configuration.enable_learning_mentor = False
|
||||||
course.configuration.save()
|
|
||||||
|
|
||||||
competence_certificate = create_test_competence_navi()
|
competence_certificate = create_test_competence_navi()
|
||||||
|
|
||||||
|
|
@ -118,7 +121,9 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
|
||||||
|
|
||||||
if include_uk:
|
if include_uk:
|
||||||
create_uk_fahrzeug_casework(
|
create_uk_fahrzeug_casework(
|
||||||
course_id=COURSE_TEST_ID, competence_certificate=competence_certificate
|
course_id=COURSE_TEST_ID,
|
||||||
|
competence_certificate=competence_certificate,
|
||||||
|
with_documents=with_documents,
|
||||||
)
|
)
|
||||||
create_uk_fahrzeug_prep_assignment(course_id=COURSE_TEST_ID)
|
create_uk_fahrzeug_prep_assignment(course_id=COURSE_TEST_ID)
|
||||||
create_uk_condition_acceptance(course_id=COURSE_TEST_ID)
|
create_uk_condition_acceptance(course_id=COURSE_TEST_ID)
|
||||||
|
|
@ -128,11 +133,17 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
|
||||||
title="Edoniq Wissens- und Verständisfragen - Circle Fahrzeug (Demo)",
|
title="Edoniq Wissens- und Verständisfragen - Circle Fahrzeug (Demo)",
|
||||||
competence_certificate=competence_certificate,
|
competence_certificate=competence_certificate,
|
||||||
)
|
)
|
||||||
|
course.configuration.is_uk = True
|
||||||
|
|
||||||
if include_vv:
|
if include_vv:
|
||||||
create_vv_gewinnen_casework(course_id=COURSE_TEST_ID)
|
create_vv_gewinnen_casework(course_id=COURSE_TEST_ID)
|
||||||
|
course.configuration.is_vv = True
|
||||||
|
|
||||||
create_test_learning_path(include_uk=include_uk, include_vv=include_vv)
|
course.configuration.save()
|
||||||
|
|
||||||
|
create_test_learning_path(
|
||||||
|
include_uk=include_uk, include_vv=include_vv, with_documents=with_documents
|
||||||
|
)
|
||||||
create_test_media_library()
|
create_test_media_library()
|
||||||
|
|
||||||
if with_sessions:
|
if with_sessions:
|
||||||
|
|
@ -153,17 +164,41 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
|
||||||
location="Handelsschule KV Bern, Zimmer 123, Eigerstrasse 16, 3012 Bern",
|
location="Handelsschule KV Bern, Zimmer 123, Eigerstrasse 16, 3012 Bern",
|
||||||
trainer="Roland Grossenbacher, roland.grossenbacher@helvetia.ch",
|
trainer="Roland Grossenbacher, roland.grossenbacher@helvetia.ch",
|
||||||
)
|
)
|
||||||
tuesday_in_one_week = (
|
wednesday_in_four_weeks = (
|
||||||
datetime.now() + relativedelta(weekday=TU) + relativedelta(weeks=1)
|
datetime.now() + relativedelta(weekday=TU) + relativedelta(weeks=1)
|
||||||
)
|
)
|
||||||
csac.due_date.start = timezone.make_aware(
|
csac.due_date.start = timezone.make_aware(
|
||||||
tuesday_in_one_week.replace(hour=8, minute=30, second=0, microsecond=0)
|
wednesday_in_four_weeks.replace(hour=8, minute=30, second=0, microsecond=0)
|
||||||
)
|
)
|
||||||
csac.due_date.end = timezone.make_aware(
|
csac.due_date.end = timezone.make_aware(
|
||||||
tuesday_in_one_week.replace(hour=17, minute=0, second=0, microsecond=0)
|
wednesday_in_four_weeks.replace(hour=17, minute=0, second=0, microsecond=0)
|
||||||
)
|
)
|
||||||
csac.due_date.save()
|
csac.due_date.save()
|
||||||
|
|
||||||
|
if include_vv:
|
||||||
|
csac = CourseSessionAttendanceCourse.objects.create(
|
||||||
|
course_session=cs_bern,
|
||||||
|
learning_content=LearningContentAttendanceCourse.objects.get(
|
||||||
|
slug="test-lehrgang-lp-circle-reisen-lc-präsenzkurs-reisen"
|
||||||
|
),
|
||||||
|
location="Handelsschule KV Bern, Zimmer 123, Eigerstrasse 16, 3012 Bern",
|
||||||
|
trainer="Roland Grossenbacher, roland.grossenbacher@helvetia.ch",
|
||||||
|
)
|
||||||
|
wednesday_in_four_weeks = (
|
||||||
|
datetime.now() + relativedelta(weekday=TU) + relativedelta(weeks=4)
|
||||||
|
)
|
||||||
|
csac.due_date.start = timezone.make_aware(
|
||||||
|
wednesday_in_four_weeks.replace(
|
||||||
|
hour=8, minute=15, second=0, microsecond=0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
csac.due_date.end = timezone.make_aware(
|
||||||
|
wednesday_in_four_weeks.replace(
|
||||||
|
hour=17, minute=30, second=0, microsecond=0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
csac.due_date.save()
|
||||||
|
|
||||||
csa = CourseSessionAssignment.objects.create(
|
csa = CourseSessionAssignment.objects.create(
|
||||||
course_session=cs_bern,
|
course_session=cs_bern,
|
||||||
learning_content=LearningContentAssignment.objects.get(
|
learning_content=LearningContentAssignment.objects.get(
|
||||||
|
|
@ -187,7 +222,7 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
|
||||||
csa = CourseSessionAssignment.objects.create(
|
csa = CourseSessionAssignment.objects.create(
|
||||||
course_session=cs_bern,
|
course_session=cs_bern,
|
||||||
learning_content=LearningContentAssignment.objects.get(
|
learning_content=LearningContentAssignment.objects.get(
|
||||||
slug=f"test-lehrgang-lp-circle-fahrzeug-lc-fahrzeug-mein-erstes-auto"
|
slug="test-lehrgang-lp-circle-fahrzeug-lc-fahrzeug-mein-erstes-auto"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
next_monday = datetime.now() + relativedelta(weekday=MO(2))
|
next_monday = datetime.now() + relativedelta(weekday=MO(2))
|
||||||
|
|
@ -215,7 +250,7 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
|
||||||
_csa = CourseSessionAssignment.objects.create(
|
_csa = CourseSessionAssignment.objects.create(
|
||||||
course_session=cs_bern,
|
course_session=cs_bern,
|
||||||
learning_content=LearningContentAssignment.objects.get(
|
learning_content=LearningContentAssignment.objects.get(
|
||||||
slug=f"test-lehrgang-lp-circle-reisen-lc-mein-kundenstamm"
|
slug="test-lehrgang-lp-circle-reisen-lc-mein-kundenstamm"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -226,6 +261,24 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
|
||||||
id=TEST_COURSE_SESSION_ZURICH_ID,
|
id=TEST_COURSE_SESSION_ZURICH_ID,
|
||||||
start_date=now,
|
start_date=now,
|
||||||
)
|
)
|
||||||
|
csac = CourseSessionAttendanceCourse.objects.create(
|
||||||
|
course_session=cs_zurich,
|
||||||
|
learning_content=LearningContentAttendanceCourse.objects.get(
|
||||||
|
slug="test-lehrgang-lp-circle-fahrzeug-lc-präsenzkurs-fahrzeug"
|
||||||
|
),
|
||||||
|
location="siehe Anzeigetafel, Bildungszentrum Jungholz, Jungholzstrasse 43, 8050 Zürich-Oerlikon",
|
||||||
|
trainer="Catia Logarzo",
|
||||||
|
)
|
||||||
|
wednesday_in_four_weeks = (
|
||||||
|
datetime.now() + relativedelta(weekday=WE) + relativedelta(weeks=1)
|
||||||
|
)
|
||||||
|
csac.due_date.start = timezone.make_aware(
|
||||||
|
wednesday_in_four_weeks.replace(hour=8, minute=30, second=0, microsecond=0)
|
||||||
|
)
|
||||||
|
csac.due_date.end = timezone.make_aware(
|
||||||
|
wednesday_in_four_weeks.replace(hour=17, minute=0, second=0, microsecond=0)
|
||||||
|
)
|
||||||
|
csac.due_date.save()
|
||||||
|
|
||||||
region1 = CourseSessionGroup.objects.create(
|
region1 = CourseSessionGroup.objects.create(
|
||||||
name="Region 1",
|
name="Region 1",
|
||||||
|
|
@ -415,23 +468,29 @@ def create_test_course_with_categories(apps=None, schema_editor=None):
|
||||||
course.slug = course_page.slug
|
course.slug = course_page.slug
|
||||||
course.save()
|
course.save()
|
||||||
|
|
||||||
|
config = CourseConfiguration.objects.get_or_create(course=course)[0]
|
||||||
|
config.is_uk = True
|
||||||
|
config.save()
|
||||||
|
|
||||||
return course
|
return course
|
||||||
|
|
||||||
|
|
||||||
def create_test_learning_path(include_uk=True, include_vv=True):
|
def create_test_learning_path(include_uk=True, include_vv=True, with_documents=False):
|
||||||
course_page = CoursePage.objects.get(course_id=COURSE_TEST_ID)
|
course_page = CoursePage.objects.get(course_id=COURSE_TEST_ID)
|
||||||
lp = LearningPathFactory(title="Test Lernpfad", parent=course_page)
|
lp = LearningPathFactory(title="Test Lernpfad", parent=course_page)
|
||||||
|
|
||||||
if include_uk:
|
if include_uk:
|
||||||
TopicFactory(title="Circle ÜK", is_visible=False, parent=lp)
|
TopicFactory(title="Circle ÜK", is_visible=False, parent=lp)
|
||||||
create_test_uk_circle_fahrzeug(lp, title="Fahrzeug")
|
create_test_uk_circle_fahrzeug(
|
||||||
|
lp, title="Fahrzeug", with_documents=with_documents
|
||||||
|
)
|
||||||
|
|
||||||
if include_vv:
|
if include_vv:
|
||||||
TopicFactory(title="Circle VV", is_visible=False, parent=lp)
|
TopicFactory(title="Circle VV", is_visible=False, parent=lp)
|
||||||
create_test_circle_reisen(lp)
|
create_test_circle_reisen(lp)
|
||||||
|
|
||||||
|
|
||||||
def create_test_uk_circle_fahrzeug(lp, title="Fahrzeug"):
|
def create_test_uk_circle_fahrzeug(lp, title="Fahrzeug", with_documents=False):
|
||||||
circle = CircleFactory(
|
circle = CircleFactory(
|
||||||
title=title,
|
title=title,
|
||||||
parent=lp,
|
parent=lp,
|
||||||
|
|
@ -470,21 +529,25 @@ damit du erfolgreich mit deinem Lernpfad (durch-)starten kannst.
|
||||||
),
|
),
|
||||||
content_url=f"/course/{lp.get_course().slug}/media/handlungsfelder/{slugify(title)}",
|
content_url=f"/course/{lp.get_course().slug}/media/handlungsfelder/{slugify(title)}",
|
||||||
)
|
)
|
||||||
LearningContentAssignmentFactory(
|
(
|
||||||
title="Redlichkeitserklärung",
|
LearningContentAssignmentFactory(
|
||||||
parent=circle,
|
title="Redlichkeitserklärung",
|
||||||
content_assignment=Assignment.objects.get(
|
parent=circle,
|
||||||
slug__startswith="test-lehrgang-assignment-redlichkeits"
|
content_assignment=Assignment.objects.get(
|
||||||
|
slug__startswith="test-lehrgang-assignment-redlichkeits"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
LearningContentAssignmentFactory(
|
(
|
||||||
title="Fahrzeug - Mein erstes Auto",
|
LearningContentAssignmentFactory(
|
||||||
assignment_type="PREP_ASSIGNMENT",
|
title="Fahrzeug - Mein erstes Auto",
|
||||||
parent=circle,
|
assignment_type="PREP_ASSIGNMENT",
|
||||||
content_assignment=Assignment.objects.get(
|
parent=circle,
|
||||||
slug__startswith="test-lehrgang-assignment-fahrzeug-mein-erstes-auto"
|
content_assignment=Assignment.objects.get(
|
||||||
|
slug__startswith="test-lehrgang-assignment-fahrzeug-mein-erstes-auto"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
|
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=ActionCompetence.objects.get(competence_id="X1"),
|
parent=ActionCompetence.objects.get(competence_id="X1"),
|
||||||
|
|
@ -531,21 +594,24 @@ damit du erfolgreich mit deinem Lernpfad (durch-)starten kannst.
|
||||||
|
|
||||||
LearningSequenceFactory(title="Transfer", parent=circle, icon="it-icon-ls-end")
|
LearningSequenceFactory(title="Transfer", parent=circle, icon="it-icon-ls-end")
|
||||||
LearningUnitFactory(title="Transfer", parent=circle)
|
LearningUnitFactory(title="Transfer", parent=circle)
|
||||||
LearningContentAssignmentFactory(
|
(
|
||||||
title="Reflexion",
|
LearningContentAssignmentFactory(
|
||||||
assignment_type="REFLECTION",
|
title="Reflexion",
|
||||||
parent=circle,
|
assignment_type="REFLECTION",
|
||||||
content_assignment=Assignment.objects.get(
|
parent=circle,
|
||||||
slug__startswith=f"test-lehrgang-assignment-reflexion"
|
content_assignment=Assignment.objects.get(
|
||||||
|
slug__startswith="test-lehrgang-assignment-reflexion"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
|
|
||||||
assignment = Assignment.objects.get(
|
assignment = Assignment.objects.get(
|
||||||
slug__startswith="test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs"
|
slug__startswith="test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs"
|
||||||
)
|
)
|
||||||
assignment.solution_sample = ContentDocument.objects.get(
|
if with_documents:
|
||||||
title="Musterlösung Fahrzeug"
|
assignment.solution_sample = ContentDocument.objects.get(
|
||||||
)
|
title="Musterlösung Fahrzeug"
|
||||||
|
)
|
||||||
assignment.save()
|
assignment.save()
|
||||||
LearningContentAssignmentFactory(
|
LearningContentAssignmentFactory(
|
||||||
title="Überprüfen einer Motorfahrzeug-Versicherungspolice",
|
title="Überprüfen einer Motorfahrzeug-Versicherungspolice",
|
||||||
|
|
@ -574,13 +640,24 @@ def create_test_circle_reisen(lp):
|
||||||
description="Willkommen im Lehrgang Versicherungsvermitler VBV",
|
description="Willkommen im Lehrgang Versicherungsvermitler VBV",
|
||||||
)
|
)
|
||||||
LearningContentMediaLibraryFactory(
|
LearningContentMediaLibraryFactory(
|
||||||
title=f"Mediathek Reisen",
|
title="Mediathek Reisen",
|
||||||
|
parent=circle,
|
||||||
|
content_url="/course/test-lehrgang/media/handlungsfelder/reisen",
|
||||||
|
)
|
||||||
|
|
||||||
|
LearningSequenceFactory(title="Training", parent=circle)
|
||||||
|
LearningUnitFactory(title="Präsenzkurs", parent=circle)
|
||||||
|
LearningContentAttendanceCourseFactory(
|
||||||
|
title="Präsenzkurs Reisen",
|
||||||
|
parent=circle,
|
||||||
|
)
|
||||||
|
LearningUnitFactory(title="Unterlagen", parent=circle)
|
||||||
|
LearningContentPlaceholderFactory(
|
||||||
|
title="Unterlagen für den Unterricht",
|
||||||
parent=circle,
|
parent=circle,
|
||||||
content_url=f"/course/test-lehrgang/media/handlungsfelder/reisen",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
LearningSequenceFactory(title="Analyse", parent=circle)
|
LearningSequenceFactory(title="Analyse", parent=circle)
|
||||||
|
|
||||||
# analyse
|
# analyse
|
||||||
lu = LearningUnitFactory(
|
lu = LearningUnitFactory(
|
||||||
title="Bedarfsanalyse, Ist- und Soll-Situation",
|
title="Bedarfsanalyse, Ist- und Soll-Situation",
|
||||||
|
|
@ -596,25 +673,27 @@ def create_test_circle_reisen(lp):
|
||||||
content_url="https://s3.eu-central-1.amazonaws.com/myvbv-wbt.iterativ.ch/emma-und-ayla-campen-durch-amerika-analyse-xapi-FZoZOP9y/index.html",
|
content_url="https://s3.eu-central-1.amazonaws.com/myvbv-wbt.iterativ.ch/emma-und-ayla-campen-durch-amerika-analyse-xapi-FZoZOP9y/index.html",
|
||||||
)
|
)
|
||||||
|
|
||||||
LearningContentAssignmentFactory(
|
(
|
||||||
title="Mein Kundenstamm",
|
LearningContentAssignmentFactory(
|
||||||
assignment_type="PRAXIS_ASSIGNMENT",
|
title="Mein Kundenstamm",
|
||||||
parent=circle,
|
assignment_type="PRAXIS_ASSIGNMENT",
|
||||||
content_assignment=Assignment.objects.get(
|
parent=circle,
|
||||||
slug__startswith="test-lehrgang-assignment-mein-kundenstamm"
|
content_assignment=Assignment.objects.get(
|
||||||
|
slug__startswith="test-lehrgang-assignment-mein-kundenstamm"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
|
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=ActionCompetence.objects.get(competence_id="Y1"),
|
parent=ActionCompetence.objects.get(competence_id="Y1"),
|
||||||
competence_id=f"Y1.1",
|
competence_id="Y1.1",
|
||||||
title=f"Ich bin fähig zu Reisen eine Gesprächsführung zu machen",
|
title="Ich bin fähig zu Reisen eine Gesprächsführung zu machen",
|
||||||
learning_unit=lu,
|
learning_unit=lu,
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=ActionCompetence.objects.get(competence_id="Y2"),
|
parent=ActionCompetence.objects.get(competence_id="Y2"),
|
||||||
competence_id=f"Y2.1",
|
competence_id="Y2.1",
|
||||||
title=f"Ich bin fähig zu Reisen eine Analyse zu machen",
|
title="Ich bin fähig zu Reisen eine Analyse zu machen",
|
||||||
learning_unit=lu,
|
learning_unit=lu,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -627,7 +706,7 @@ def create_test_circle_reisen(lp):
|
||||||
parent=parent,
|
parent=parent,
|
||||||
)
|
)
|
||||||
LearningContentPlaceholderFactory(
|
LearningContentPlaceholderFactory(
|
||||||
title=f"Fachcheck Reisen",
|
title="Fachcheck Reisen",
|
||||||
parent=parent,
|
parent=parent,
|
||||||
)
|
)
|
||||||
LearningContentKnowledgeAssessmentFactory(
|
LearningContentKnowledgeAssessmentFactory(
|
||||||
|
|
@ -757,9 +836,9 @@ def create_test_media_library():
|
||||||
die der Fahrzeugbesitzer und die Fahrzeugbesitzerin in einem grösseren Schadenfall oft nur schwer selbst aufbringen kann.
|
die der Fahrzeugbesitzer und die Fahrzeugbesitzerin in einem grösseren Schadenfall oft nur schwer selbst aufbringen kann.
|
||||||
""".strip(),
|
""".strip(),
|
||||||
body=RichText(
|
body=RichText(
|
||||||
f"<h2>Lernmedien</h2>"
|
"<h2>Lernmedien</h2>"
|
||||||
f"<h3>Allgemeines</h3>"
|
"<h3>Allgemeines</h3>"
|
||||||
f"<ul><li>Mit Risiken im Strassenverkehr umgehen</li><li>Versicherungsschutz</li><li>Vertragsarten</li><li>Zusammenfassung</li></ul>"
|
"<ul><li>Mit Risiken im Strassenverkehr umgehen</li><li>Versicherungsschutz</li><li>Vertragsarten</li><li>Zusammenfassung</li></ul>"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -778,9 +857,9 @@ def create_test_media_library():
|
||||||
title=cat,
|
title=cat,
|
||||||
parent=media_lib_allgemeines,
|
parent=media_lib_allgemeines,
|
||||||
body=RichText(
|
body=RichText(
|
||||||
f"<h2>Lernmedien</h2>"
|
"<h2>Lernmedien</h2>"
|
||||||
f"<h3>Allgemeines</h3>"
|
"<h3>Allgemeines</h3>"
|
||||||
f"<ul><li>Mit Risiken im Strassenverkehr umgehen</li><li>Versicherungsschutz</li><li>Vertragsarten</li><li>Zusammenfassung</li></ul>"
|
"<ul><li>Mit Risiken im Strassenverkehr umgehen</li><li>Versicherungsschutz</li><li>Vertragsarten</li><li>Zusammenfassung</li></ul>"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,8 @@ class CourseConfigurationObjectType(DjangoObjectType):
|
||||||
"enable_circle_documents",
|
"enable_circle_documents",
|
||||||
"enable_learning_mentor",
|
"enable_learning_mentor",
|
||||||
"enable_competence_certificates",
|
"enable_competence_certificates",
|
||||||
|
"is_uk",
|
||||||
|
"is_vv",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ def command(course):
|
||||||
create_course_uk_it()
|
create_course_uk_it()
|
||||||
|
|
||||||
if COURSE_TEST_ID in course:
|
if COURSE_TEST_ID in course:
|
||||||
create_test_course(with_sessions=True)
|
create_test_course(with_sessions=True, with_documents=True)
|
||||||
|
|
||||||
if COURSE_UK_TRAINING in course:
|
if COURSE_UK_TRAINING in course:
|
||||||
create_course_training_de()
|
create_course_training_de()
|
||||||
|
|
@ -207,6 +207,8 @@ def create_versicherungsvermittlerin_course(
|
||||||
course_id=course_id,
|
course_id=course_id,
|
||||||
title=names[language],
|
title=names[language],
|
||||||
)
|
)
|
||||||
|
course.configuration.is_vv = True
|
||||||
|
course.configuration.save()
|
||||||
|
|
||||||
# assignments create assignments parent page
|
# assignments create assignments parent page
|
||||||
_assignment_list_page = AssignmentListPageFactory(
|
_assignment_list_page = AssignmentListPageFactory(
|
||||||
|
|
@ -354,6 +356,8 @@ def create_course_uk_de(course_id=COURSE_UK, lang="de"):
|
||||||
"it": "Corsi interaziendali",
|
"it": "Corsi interaziendali",
|
||||||
}
|
}
|
||||||
course = create_course_with_categories(course_id=course_id, title=names[lang])
|
course = create_course_with_categories(course_id=course_id, title=names[lang])
|
||||||
|
course.configuration.is_uk = True
|
||||||
|
course.configuration.save()
|
||||||
|
|
||||||
# assignments create assignments parent page
|
# assignments create assignments parent page
|
||||||
_assignment_list_page = AssignmentListPageFactory(
|
_assignment_list_page = AssignmentListPageFactory(
|
||||||
|
|
@ -387,6 +391,8 @@ def create_course_uk_de(course_id=COURSE_UK, lang="de"):
|
||||||
|
|
||||||
def create_course_uk_de_course_sessions():
|
def create_course_uk_de_course_sessions():
|
||||||
course = Course.objects.get(id=COURSE_UK)
|
course = Course.objects.get(id=COURSE_UK)
|
||||||
|
course.configuration.is_uk = True
|
||||||
|
course.configuration.save()
|
||||||
|
|
||||||
cs = CourseSession.objects.create(
|
cs = CourseSession.objects.create(
|
||||||
course_id=COURSE_UK,
|
course_id=COURSE_UK,
|
||||||
|
|
@ -500,6 +506,8 @@ def create_course_uk_fr():
|
||||||
course = create_course_with_categories(
|
course = create_course_with_categories(
|
||||||
course_id=COURSE_UK_FR, title="Cours interentreprises"
|
course_id=COURSE_UK_FR, title="Cours interentreprises"
|
||||||
)
|
)
|
||||||
|
course.configuration.is_uk = True
|
||||||
|
course.configuration.save()
|
||||||
|
|
||||||
# assignments create assignments parent page
|
# assignments create assignments parent page
|
||||||
_assignment_list_page = AssignmentListPageFactory(
|
_assignment_list_page = AssignmentListPageFactory(
|
||||||
|
|
@ -550,6 +558,8 @@ def create_course_uk_it():
|
||||||
course = create_course_with_categories(
|
course = create_course_with_categories(
|
||||||
course_id=COURSE_UK_IT, title="Corso interaziendale"
|
course_id=COURSE_UK_IT, title="Corso interaziendale"
|
||||||
)
|
)
|
||||||
|
course.configuration.is_uk = True
|
||||||
|
course.configuration.save()
|
||||||
|
|
||||||
# assignments create assignments parent page
|
# assignments create assignments parent page
|
||||||
_assignment_list_page = AssignmentListPageFactory(
|
_assignment_list_page = AssignmentListPageFactory(
|
||||||
|
|
@ -651,6 +661,8 @@ def create_course_training_de():
|
||||||
course = create_course_with_categories(
|
course = create_course_with_categories(
|
||||||
course_id=COURSE_UK_TRAINING, title="myVBV Training"
|
course_id=COURSE_UK_TRAINING, title="myVBV Training"
|
||||||
)
|
)
|
||||||
|
course.configuration.is_uk = True
|
||||||
|
course.configuration.save()
|
||||||
|
|
||||||
# assignments create assignments parent page
|
# assignments create assignments parent page
|
||||||
_assignment_list_page = AssignmentListPageFactory(
|
_assignment_list_page = AssignmentListPageFactory(
|
||||||
|
|
@ -741,6 +753,8 @@ def create_course_training_fr():
|
||||||
course = create_course_with_categories(
|
course = create_course_with_categories(
|
||||||
course_id=COURSE_UK_TRAINING_FR, title="myVBV Training (FR)"
|
course_id=COURSE_UK_TRAINING_FR, title="myVBV Training (FR)"
|
||||||
)
|
)
|
||||||
|
course.configuration.is_uk = True
|
||||||
|
course.configuration.save()
|
||||||
|
|
||||||
# assignments create assignments parent page
|
# assignments create assignments parent page
|
||||||
_assignment_list_page = AssignmentListPageFactory(
|
_assignment_list_page = AssignmentListPageFactory(
|
||||||
|
|
@ -828,6 +842,8 @@ def create_course_training_it():
|
||||||
course = create_course_with_categories(
|
course = create_course_with_categories(
|
||||||
course_id=COURSE_UK_TRAINING_IT, title="myVBV Training (IT)"
|
course_id=COURSE_UK_TRAINING_IT, title="myVBV Training (IT)"
|
||||||
)
|
)
|
||||||
|
course.configuration.is_uk = True
|
||||||
|
course.configuration.save()
|
||||||
|
|
||||||
# assignments create assignments parent page
|
# assignments create assignments parent page
|
||||||
_assignment_list_page = AssignmentListPageFactory(
|
_assignment_list_page = AssignmentListPageFactory(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
# Generated by Django 3.2.20 on 2024-04-03 09:32
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
TEST_COURSE_ID = -1
|
||||||
|
|
||||||
|
UK_COURSE_IDS = [
|
||||||
|
-3, # uk-de
|
||||||
|
-6, # uk-training-de
|
||||||
|
-5, # uk-fr
|
||||||
|
-7, # uk-training-fr
|
||||||
|
-8, # uk-it
|
||||||
|
-9, # uk-training-it
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
VV_COURSE_IDS = [
|
||||||
|
-4, # vv-de
|
||||||
|
-10, # vv-fr
|
||||||
|
-11, # vv-it
|
||||||
|
-12, # vv-prüfung
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def forward_migration(apps, schema_editor):
|
||||||
|
Course = apps.get_model("course", "Course")
|
||||||
|
CourseConfiguration = apps.get_model("course", "CourseConfiguration")
|
||||||
|
|
||||||
|
for course in Course.objects.all():
|
||||||
|
config, created = CourseConfiguration.objects.get_or_create(
|
||||||
|
course=course,
|
||||||
|
)
|
||||||
|
|
||||||
|
# -> disable unnecessary features
|
||||||
|
if course.id in UK_COURSE_IDS:
|
||||||
|
config.is_uk = True
|
||||||
|
elif course.id in VV_COURSE_IDS:
|
||||||
|
config.is_vv = True
|
||||||
|
|
||||||
|
config.save()
|
||||||
|
|
||||||
|
|
||||||
|
def backward_migration(apps, schema_editor):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("course", "0007_auto_20240226_1553"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="courseconfiguration",
|
||||||
|
name="is_uk",
|
||||||
|
field=models.BooleanField(default=False, verbose_name="ÜK-Lehrgang"),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="courseconfiguration",
|
||||||
|
name="is_vv",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=False, verbose_name="Versicherungsvermittler-Lehrgang"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.RunPython(forward_migration, backward_migration),
|
||||||
|
]
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue