Handle closing of self evaluation in KompetenzNavi
This commit is contained in:
parent
5dfdd470ae
commit
dfa03baa25
|
|
@ -1,179 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import PerformanceCriteriaRow from "@/pages/competence-old/PerformanceCriteriaRow.vue";
|
||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||
import ItProgress from "@/components/ui/ItProgress.vue";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import maxBy from "lodash/maxBy";
|
||||
import orderBy from "lodash/orderBy";
|
||||
import * as log from "loglevel";
|
||||
import { computed } from "vue";
|
||||
|
||||
log.debug("CompetenceIndexPage created");
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
}>();
|
||||
|
||||
log.debug("CompetenceIndexPage created", props);
|
||||
|
||||
const competenceStore = useCompetenceStore();
|
||||
|
||||
const failedCriteria = computed(() => {
|
||||
return competenceStore
|
||||
.flatPerformanceCriteria()
|
||||
.filter((criteria) => {
|
||||
return criteria.completion_status === "FAIL";
|
||||
})
|
||||
.slice(0, 3);
|
||||
});
|
||||
|
||||
const lastUpdatedCompetences = computed(() => {
|
||||
return orderBy(
|
||||
competenceStore.competences(),
|
||||
[
|
||||
(competence) => {
|
||||
let criteria = competence.children;
|
||||
if (competenceStore.selectedCircle.id != "all") {
|
||||
criteria = criteria.filter((criteria) => {
|
||||
return (
|
||||
criteria.circle.translation_key === competenceStore.selectedCircle.id
|
||||
);
|
||||
});
|
||||
}
|
||||
return (
|
||||
maxBy(criteria, "completion_status_updated_at")
|
||||
?.completion_status_updated_at || ""
|
||||
);
|
||||
},
|
||||
],
|
||||
["desc"]
|
||||
).slice(0, 3);
|
||||
});
|
||||
|
||||
const countStatus = computed(() => {
|
||||
return competenceStore.calcStatusCount(competenceStore.flatPerformanceCriteria());
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container-large lg:mt-4">
|
||||
<div
|
||||
v-if="competenceStore.competenceProfilePage()"
|
||||
class="mb-10 flex flex-col justify-between lg:flex-row lg:items-center"
|
||||
>
|
||||
<h1>{{ $t("competences.title") }}</h1>
|
||||
<ItDropdownSelect
|
||||
v-model="competenceStore.selectedCircle"
|
||||
class="mt-4 w-full lg:mt-0 lg:w-96"
|
||||
:items="competenceStore.availableCircles"
|
||||
></ItDropdownSelect>
|
||||
</div>
|
||||
<div class="l mb-4 bg-white px-8 py-4 lg:mb-8 lg:py-8">
|
||||
<div>
|
||||
<h3 class="border-b pb-4">{{ $t("competences.lastImprovements") }}</h3>
|
||||
<ul>
|
||||
<li
|
||||
v-for="competence in lastUpdatedCompetences"
|
||||
:key="competence.id"
|
||||
class="flex flex-col border-b py-4 last:mb-8 lg:flex-row lg:items-center"
|
||||
>
|
||||
<p class="mb-4 inline-block lg:mb-0 lg:mr-5 lg:w-1/4">
|
||||
{{ competence.competence_id }} {{ competence.title }}
|
||||
</p>
|
||||
<ItProgress
|
||||
:status-count="
|
||||
competenceStore.calcStatusCount(
|
||||
competenceStore.criteriaByCompetence(competence)
|
||||
)
|
||||
"
|
||||
></ItProgress>
|
||||
</li>
|
||||
</ul>
|
||||
<router-link
|
||||
:to="`${
|
||||
competenceStore.competenceProfilePage()?.frontend_url
|
||||
}-old/competences`"
|
||||
class="btn-text inline-flex items-center py-2 pl-0"
|
||||
>
|
||||
<span>{{ $t("general.showAll") }}</span>
|
||||
<it-icon-arrow-right></it-icon-arrow-right>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="l mb-4 bg-white px-8 py-4 lg:mb-8 lg:py-8">
|
||||
<h3 class="mb-4 border-b pb-4 lg:border-0 lg:pb-0">
|
||||
{{ $t("competences.assessment") }}
|
||||
</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 border-b pb-4 lg:mb-0 lg:border-b-0 lg:border-r 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">{{ countStatus.FAIL }}</p>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="mb-4 inline-block flex-1 border-b pb-4 lg:mb-0 lg:border-b-0 lg:border-r 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">
|
||||
{{ countStatus.SUCCESS }}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="lg:mb-0flex-1 border-b pb-4 lg:border-b-0 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">
|
||||
{{ countStatus.UNKNOWN }}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<router-link
|
||||
:to="`${competenceStore.competenceProfilePage()?.frontend_url}-old/criteria`"
|
||||
class="btn-text inline-flex items-center py-2 pl-0"
|
||||
>
|
||||
<span>{{ $t("general.showAll") }}</span>
|
||||
<it-icon-arrow-right></it-icon-arrow-right>
|
||||
</router-link>
|
||||
</div>
|
||||
<div
|
||||
v-if="failedCriteria.length > 0"
|
||||
class="l mb-4 bg-white px-8 py-4 lg:mb-8 lg:py-8"
|
||||
>
|
||||
<div class="mb-4 flex flex-row items-center border-b pb-4">
|
||||
<it-icon-smiley-thinking class="mr-5 h-11 w-11"></it-icon-smiley-thinking>
|
||||
<h3>«{{ $t("selfEvaluation.no") }}»</h3>
|
||||
</div>
|
||||
<ul class="mb-6">
|
||||
<li
|
||||
v-for="criteria in failedCriteria"
|
||||
:key="criteria.id"
|
||||
class="mb-4 border-b pb-4"
|
||||
>
|
||||
<PerformanceCriteriaRow
|
||||
:criteria="criteria"
|
||||
:course-slug="props.courseSlug"
|
||||
></PerformanceCriteriaRow>
|
||||
</li>
|
||||
</ul>
|
||||
<router-link
|
||||
:to="`${competenceStore.competenceProfilePage()?.frontend_url}-old/criteria`"
|
||||
class="btn-text inline-flex items-center py-2 pl-0"
|
||||
>
|
||||
<span>{{ $t("general.showAll") }}</span>
|
||||
<it-icon-arrow-right></it-icon-arrow-right>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import * as log from "loglevel";
|
||||
import { onMounted } from "vue";
|
||||
|
||||
log.debug("CompetenceParentPage created");
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
}>();
|
||||
|
||||
const competenceStore = useCompetenceStore();
|
||||
|
||||
onMounted(async () => {
|
||||
log.debug("CompetenceParentPage mounted", props.courseSlug);
|
||||
|
||||
try {
|
||||
const competencePageSlug = props.courseSlug + "-competence";
|
||||
await competenceStore.loadCompetenceProfilePage(competencePageSlug);
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-gray-200">
|
||||
<main>
|
||||
<router-view></router-view>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { default as PerformanceCriteriaRow } from "@/pages/competence-old/PerformanceCriteriaRow.vue";
|
||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import type { CourseCompletionStatus } from "@/types";
|
||||
import * as log from "loglevel";
|
||||
import type { Ref } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import { useTranslation } from "i18next-vue";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
}>();
|
||||
|
||||
interface MenuItem {
|
||||
id: CourseCompletionStatus;
|
||||
name: string;
|
||||
iconName: string;
|
||||
}
|
||||
|
||||
log.debug("PerformanceCriteriaPage created", props);
|
||||
|
||||
const competenceStore = useCompetenceStore();
|
||||
|
||||
const shownCriteria = computed(() => {
|
||||
return competenceStore.flatPerformanceCriteria().filter((criteria) => {
|
||||
return criteria.completion_status === activeMenuItem.value.id;
|
||||
});
|
||||
});
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const mobileMenuItems: MenuItem[] = [
|
||||
{
|
||||
id: "FAIL",
|
||||
name: `«${t("selfEvaluation.no")}»`,
|
||||
iconName: "it-icon-smiley-thinking",
|
||||
},
|
||||
{
|
||||
id: "SUCCESS",
|
||||
name: `«${t("selfEvaluation.yes")}»`,
|
||||
iconName: "it-icon-smiley-happy",
|
||||
},
|
||||
{
|
||||
id: "UNKNOWN",
|
||||
name: t("competences.notAssessed"),
|
||||
iconName: "it-icon-smiley-neutral",
|
||||
},
|
||||
];
|
||||
|
||||
const activeMenuItem: Ref<MenuItem> = ref(mobileMenuItems[0]);
|
||||
|
||||
function updateActiveState(status: CourseCompletionStatus) {
|
||||
activeMenuItem.value =
|
||||
mobileMenuItems.find((item) => status === item.id) || mobileMenuItems[0];
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container-large">
|
||||
<nav class="py-4 lg:pb-8">
|
||||
<router-link
|
||||
class="btn-text inline-flex items-center pl-0"
|
||||
:to="`${competenceStore.competenceProfilePage()?.frontend_url}-old`"
|
||||
>
|
||||
<it-icon-arrow-left />
|
||||
<span>{{ $t("general.back") }}</span>
|
||||
</router-link>
|
||||
</nav>
|
||||
<div class="mb-4 flex flex-col items-center justify-between lg:mb-10 lg:flex-row">
|
||||
<h1>Einschätzungen</h1>
|
||||
<ItDropdownSelect
|
||||
v-model="competenceStore.selectedCircle"
|
||||
class="mt-4 w-full lg:mt-0 lg:w-96"
|
||||
:items="competenceStore.availableCircles"
|
||||
></ItDropdownSelect>
|
||||
<div class="w-full lg:hidden">
|
||||
<ItDropdownSelect
|
||||
v-model="activeMenuItem"
|
||||
class="mt-4 w-full lg:mt-0 lg:w-96"
|
||||
:items="mobileMenuItems"
|
||||
></ItDropdownSelect>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white p-8">
|
||||
<div
|
||||
class="mb-4 flex hidden flex-col justify-between border-b pb-4 lg:block lg:flex-row lg:items-center"
|
||||
>
|
||||
<button
|
||||
v-for="item in mobileMenuItems"
|
||||
:key="item.id"
|
||||
:class="{
|
||||
'bg-gray-200': activeMenuItem.id === item.id,
|
||||
'mr-6': item.id !== 'UNKNOWN',
|
||||
}"
|
||||
class="mr-6 inline-block px-2 py-4"
|
||||
@click="updateActiveState(item.id)"
|
||||
>
|
||||
<div class="flex flex-row items-center">
|
||||
<span class="mr-2 inline-block">{{ item.name }}</span>
|
||||
<component :is="item.iconName"></component>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<ul class="mb-6">
|
||||
<li
|
||||
v-for="criteria in shownCriteria"
|
||||
:key="criteria.id"
|
||||
class="mb-4 border-b pb-4"
|
||||
>
|
||||
<PerformanceCriteriaRow
|
||||
:criteria="criteria"
|
||||
:show-state="true"
|
||||
:course-slug="props.courseSlug"
|
||||
></PerformanceCriteriaRow>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { useCircleStore } from "@/stores/circle";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import * as log from "loglevel";
|
||||
|
||||
import LearningContentContainer from "@/pages/learningPath/learningContentPage/LearningContentContainer.vue";
|
||||
import { computed } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
criteriaSlug: string;
|
||||
}>();
|
||||
|
||||
log.debug("SinglePerformanceCriteriaPage.vue setup");
|
||||
|
||||
const competenceStore = useCompetenceStore();
|
||||
const circleStore = useCircleStore();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const singleCriteria = computed(() => {
|
||||
return competenceStore.flatPerformanceCriteria().find((criteria) => {
|
||||
return criteria.slug === props.criteriaSlug;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="singleCriteria" class="absolute bottom-0 top-0 w-full bg-white">
|
||||
<LearningContentContainer @exit="router.back()">
|
||||
<div v-if="singleCriteria" class="container-medium">
|
||||
<div class="mt-4 border p-6 lg:mt-8 lg:p-12">
|
||||
<h2 class="heading-2">
|
||||
{{ singleCriteria.competence_id }} {{ singleCriteria.title }}
|
||||
</h2>
|
||||
|
||||
<div class="mt-4 flex flex-col justify-between gap-6 lg:mt-8 lg:flex-row">
|
||||
<button
|
||||
class="inline-flex flex-1 items-center border p-4 text-left"
|
||||
:class="{
|
||||
'border-green-500': singleCriteria.completion_status === 'SUCCESS',
|
||||
'border-2': singleCriteria.completion_status === 'SUCCESS',
|
||||
}"
|
||||
data-cy="success"
|
||||
@click="circleStore.markCompletion(singleCriteria, 'SUCCESS')"
|
||||
>
|
||||
<it-icon-smiley-happy class="mr-4 h-16 w-16"></it-icon-smiley-happy>
|
||||
<span class="text-large font-bold">{{ $t("selfEvaluation.yes") }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="inline-flex flex-1 items-center border p-4 text-left"
|
||||
:class="{
|
||||
'border-orange-500': singleCriteria.completion_status === 'FAIL',
|
||||
'border-2': singleCriteria.completion_status === 'FAIL',
|
||||
}"
|
||||
data-cy="fail"
|
||||
@click="circleStore.markCompletion(singleCriteria, 'FAIL')"
|
||||
>
|
||||
<it-icon-smiley-thinking class="mr-4 h-16 w-16"></it-icon-smiley-thinking>
|
||||
<span class="text-xl font-bold">{{ $t("selfEvaluation.no") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LearningContentContainer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
import PerformanceCriteriaRow from "@/pages/competence-old/PerformanceCriteriaRow.vue";
|
||||
import ItProgress from "@/components/ui/ItProgress.vue";
|
||||
import ItToggleArrow from "@/components/ui/ItToggleArrow.vue";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import type { CompetencePage } from "@/types";
|
||||
import log from "loglevel";
|
||||
import { ref } from "vue";
|
||||
import { ref, watch } from "vue";
|
||||
import SinglePerformanceCriteriaRow from "@/pages/competence/SinglePerformanceCriteriaRow.vue";
|
||||
|
||||
const competenceStore = useCompetenceStore();
|
||||
|
||||
|
|
@ -14,17 +13,26 @@ interface Props {
|
|||
courseSlug: string;
|
||||
showAssessAgain?: boolean;
|
||||
isInline?: boolean;
|
||||
allOpen?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
showAssessAgain: true,
|
||||
isInline: false,
|
||||
allOpen: false,
|
||||
});
|
||||
|
||||
log.debug("PerformanceCriteriaRow created", props);
|
||||
|
||||
const isOpen = ref(false);
|
||||
|
||||
watch(
|
||||
() => props.allOpen,
|
||||
(newValue) => {
|
||||
isOpen.value = newValue;
|
||||
}
|
||||
);
|
||||
|
||||
const togglePerformanceCriteria = () => {
|
||||
isOpen.value = !isOpen.value;
|
||||
};
|
||||
|
|
@ -32,7 +40,7 @@ const togglePerformanceCriteria = () => {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<div :class="{ 'mb-4 border-b pb-8': isOpen }" class="-mx-8 px-8">
|
||||
<div class="-mx-8 border-b px-8">
|
||||
<div
|
||||
class="flex flex-row items-center justify-between"
|
||||
:class="props.isInline ? '' : 'mb-4'"
|
||||
|
|
@ -41,40 +49,27 @@ const togglePerformanceCriteria = () => {
|
|||
@click="togglePerformanceCriteria()"
|
||||
>
|
||||
<h2 :class="props.isInline ? ['text-bold', 'w-2/5'] : 'text-large'">
|
||||
{{ competence.competence_id }} {{ competence.title }}
|
||||
{{ competence.title }} ({{ competence.competence_id }})
|
||||
</h2>
|
||||
<ItProgress
|
||||
v-if="isInline"
|
||||
class="w-[330px]"
|
||||
:status-count="
|
||||
competenceStore.calcStatusCount(
|
||||
competenceStore.criteriaByCompetence(competence)
|
||||
)
|
||||
"
|
||||
></ItProgress>
|
||||
<ItToggleArrow :is-open="isOpen" :small="isInline"></ItToggleArrow>
|
||||
</div>
|
||||
<ItProgress
|
||||
v-if="!isInline"
|
||||
:status-count="
|
||||
competenceStore.calcStatusCount(
|
||||
competenceStore.criteriaByCompetence(competence)
|
||||
)
|
||||
"
|
||||
></ItProgress>
|
||||
</div>
|
||||
<ul v-if="isOpen">
|
||||
<li class="my-4 border-b pb-4 font-bold">
|
||||
{{ $t("a.Leistungsziele") }}
|
||||
</li>
|
||||
|
||||
<li
|
||||
v-for="performanceCriteria in competenceStore.criteriaByCompetence(competence)"
|
||||
:key="performanceCriteria.id"
|
||||
class="mb-4 border-b pb-4 last:border-0"
|
||||
class="my-4 border-b pb-4 last:border-0"
|
||||
>
|
||||
<PerformanceCriteriaRow
|
||||
<SinglePerformanceCriteriaRow
|
||||
:criteria="performanceCriteria"
|
||||
:show-state="true"
|
||||
:show-state="false"
|
||||
:course-slug="props.courseSlug"
|
||||
:show-assess-again="props.showAssessAgain"
|
||||
></PerformanceCriteriaRow>
|
||||
></SinglePerformanceCriteriaRow>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import CompetenceDetail from "@/pages/competence/ActionCompetenceDetail.vue";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import * as log from "loglevel";
|
||||
import { ref } from "vue";
|
||||
|
||||
log.debug("CompetenceListPage created");
|
||||
|
||||
|
|
@ -10,21 +11,45 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const competenceStore = useCompetenceStore();
|
||||
|
||||
const isOpenAll = ref(false);
|
||||
|
||||
function toggleOpen() {
|
||||
log.debug("toggleOpen");
|
||||
isOpenAll.value = !isOpenAll.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container-large">
|
||||
<h2 class="mb-4 lg:py-4">{{ $t("a.Handlungskompetenzen") }}</h2>
|
||||
<h2 class="py-4">{{ $t("a.Handlungskompetenzen") }}</h2>
|
||||
|
||||
<div class="flex justify-between py-8">
|
||||
<div class="w-3/4 text-xl">
|
||||
{{ $t("competence.listPageDescription") }}
|
||||
</div>
|
||||
<div class="whitespace-nowrap">
|
||||
<button class="link" @click="toggleOpen()">
|
||||
<span v-if="isOpenAll">
|
||||
{{ $t("a.Alle zuklappen") }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ $t("a.Alle aufklappen") }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul v-if="competenceStore.competenceProfilePage()">
|
||||
<li
|
||||
v-for="competence in competenceStore.competences()"
|
||||
:key="competence.id"
|
||||
class="mb-8 bg-white p-8"
|
||||
class="mb-8 bg-white px-8 pt-6"
|
||||
>
|
||||
<CompetenceDetail
|
||||
:competence="competence"
|
||||
:course-slug="props.courseSlug"
|
||||
:all-open="isOpenAll"
|
||||
></CompetenceDetail>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -26,31 +26,17 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
></it-icon-smiley-thinking>
|
||||
<it-icon-smiley-neutral v-else></it-icon-smiley-neutral>
|
||||
</div>
|
||||
<div class="mb-4 pr-5 lg:mb-0 lg:mr-10">
|
||||
<h4 class="text-bold mb-2">
|
||||
{{ criteria.competence_id }} {{ criteria.title }}
|
||||
</h4>
|
||||
<p class="hidden lg:block">
|
||||
{{ $t("general.learningUnit") }}:
|
||||
<router-link class="link" :to="criteria.learning_unit.frontend_url">
|
||||
{{ criteria.learning_unit.title }}
|
||||
</router-link>
|
||||
</p>
|
||||
<div class="mb-4 pr-4 lg:mb-0 lg:mr-8">
|
||||
{{ criteria.title }}
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-2 lg:hidden">
|
||||
{{ $t("general.learningUnit") }}:
|
||||
<router-link class="link" :to="criteria.learning_unit.frontend_url">
|
||||
{{ criteria.learning_unit.title }}
|
||||
</router-link>
|
||||
</p>
|
||||
<span class="whitespace-nowrap">
|
||||
<router-link
|
||||
v-if="props.showAssessAgain"
|
||||
class="link"
|
||||
:to="`/course/${props.courseSlug}/competence-old/criteria/${criteria.slug}`"
|
||||
:to="criteria.learning_unit.evaluate_url"
|
||||
>
|
||||
{{ $t("competences.assessAgain") }}
|
||||
{{ $t("competences.assessAgain", { x: criteria.circle.title }) }}
|
||||
</router-link>
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -16,6 +16,7 @@ import MediaLibraryBlock from "./blocks/MediaLibraryBlock.vue";
|
|||
import PlaceholderBlock from "./blocks/PlaceholderBlock.vue";
|
||||
import RichTextBlock from "./blocks/RichTextBlock.vue";
|
||||
import VideoBlock from "./blocks/VideoBlock.vue";
|
||||
import { getPreviousRoute } from "@/router/history";
|
||||
|
||||
const circleStore = useCircleStore();
|
||||
|
||||
|
|
@ -25,6 +26,8 @@ const props = defineProps<{
|
|||
|
||||
log.debug("LearningContentParent setup", props.learningContent);
|
||||
|
||||
const previousRoute = getPreviousRoute();
|
||||
|
||||
// can't use the type as component name, as some are reserved HTML components, e.g. video
|
||||
const COMPONENTS: Record<LearningContentType, Component> = {
|
||||
"learnpath.LearningContentAssignment": AssignmentBlock,
|
||||
|
|
@ -45,7 +48,7 @@ const component = computed(() => {
|
|||
});
|
||||
|
||||
function handleFinishedLearningContent() {
|
||||
circleStore.continueFromLearningContent(props.learningContent);
|
||||
circleStore.continueFromLearningContent(props.learningContent, previousRoute);
|
||||
}
|
||||
|
||||
eventBus.on("finishedLearningContent", handleFinishedLearningContent);
|
||||
|
|
@ -57,7 +60,7 @@ onUnmounted(() => {
|
|||
|
||||
<template>
|
||||
<LearningContentContainer
|
||||
@exit="circleStore.closeLearningContent(props.learningContent)"
|
||||
@exit="circleStore.closeLearningContent(props.learningContent, previousRoute)"
|
||||
>
|
||||
<div>
|
||||
<component :is="component" :content="learningContent"></component>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import LearningContentMultiLayout from "@/pages/learningPath/learningContentPage
|
|||
import eventBus from "@/utils/eventBus";
|
||||
import { useRouteQuery } from "@vueuse/router";
|
||||
import { computed, onUnmounted } from "vue";
|
||||
import { getPreviousRoute } from "@/router/history";
|
||||
|
||||
log.debug("LearningContent.vue setup");
|
||||
|
||||
|
|
@ -18,6 +19,8 @@ const courseSession = useCurrentCourseSession();
|
|||
|
||||
const questionIndex = useRouteQuery("step", "0", { transform: Number, mode: "push" });
|
||||
|
||||
const previousRoute = getPreviousRoute();
|
||||
|
||||
const props = defineProps<{
|
||||
learningUnit: LearningUnit;
|
||||
}>();
|
||||
|
|
@ -52,7 +55,7 @@ function handleBack() {
|
|||
}
|
||||
|
||||
function handleFinishedLearningContent() {
|
||||
circleStore.closeSelfEvaluation(props.learningUnit);
|
||||
circleStore.closeSelfEvaluation(props.learningUnit, previousRoute);
|
||||
}
|
||||
|
||||
eventBus.on("finishedLearningContent", handleFinishedLearningContent);
|
||||
|
|
@ -65,7 +68,7 @@ onUnmounted(() => {
|
|||
<template>
|
||||
<div v-if="learningUnit">
|
||||
<LearningContentContainer
|
||||
@exit="circleStore.closeSelfEvaluation(props.learningUnit)"
|
||||
@exit="circleStore.closeSelfEvaluation(props.learningUnit, previousRoute)"
|
||||
>
|
||||
<LearningContentMultiLayout
|
||||
:current-step="questionIndex"
|
||||
|
|
|
|||
|
|
@ -23,9 +23,16 @@ export const addToHistory: NavigationGuard = (to, from, next) => {
|
|||
next();
|
||||
};
|
||||
|
||||
export function getPreviousRoute() {
|
||||
if (routeHistory.length > 0) {
|
||||
return routeHistory[routeHistory.length - 1];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function routerBackOrFallback(router: Router, fallbackRoute: RouteLocationRaw) {
|
||||
// Check the latest route in history
|
||||
const previousRoute = routeHistory[routeHistory.length - 1];
|
||||
const previousRoute = getPreviousRoute();
|
||||
if (previousRoute) {
|
||||
router.back();
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -97,35 +97,6 @@ const router = createRouter({
|
|||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
path: "/course/:courseSlug/competence-old",
|
||||
props: true,
|
||||
component: () => import("@/pages/competence-old/CompetenceParentPage.vue"),
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
props: true,
|
||||
component: () => import("@/pages/competence-old/CompetenceIndexPage.vue"),
|
||||
},
|
||||
{
|
||||
path: "competences",
|
||||
props: true,
|
||||
component: () => import("@/pages/competence/ActionCompetenceListPage.vue"),
|
||||
},
|
||||
{
|
||||
path: "criteria",
|
||||
props: true,
|
||||
component: () => import("@/pages/competence-old/PerformanceCriteriaPage.vue"),
|
||||
},
|
||||
{
|
||||
path: "criteria/:criteriaSlug",
|
||||
props: true,
|
||||
component: () =>
|
||||
import("@/pages/competence-old/SinglePerformanceCriteriaPage.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/course/:courseSlug/learn",
|
||||
component: () =>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { routerBackOrFallback } from "@/router/history";
|
||||
import type { Circle } from "@/services/circle";
|
||||
import { useCompletionStore } from "@/stores/completion";
|
||||
import { useLearningPathStore } from "@/stores/learningPath";
|
||||
|
|
@ -12,6 +11,7 @@ import type {
|
|||
} from "@/types";
|
||||
import * as log from "loglevel";
|
||||
import { defineStore } from "pinia";
|
||||
import type { RouteLocationNormalized } from "vue-router";
|
||||
|
||||
export type CircleStoreState = {
|
||||
circle: Circle | undefined;
|
||||
|
|
@ -127,22 +127,36 @@ export const useCircleStore = defineStore({
|
|||
path: learningContent.frontend_url,
|
||||
});
|
||||
},
|
||||
closeLearningContent(learningContent: LearningContentInterface) {
|
||||
routerBackOrFallback(this.router, {
|
||||
path: `${this.circle?.frontend_url}`,
|
||||
hash: createLearningUnitHash(learningContent.parentLearningUnit),
|
||||
});
|
||||
closeLearningContent(
|
||||
learningContent: LearningContentInterface,
|
||||
returnRoute?: RouteLocationNormalized
|
||||
) {
|
||||
if (returnRoute) {
|
||||
this.router.push(returnRoute);
|
||||
} else {
|
||||
this.router.push({
|
||||
path: `${this.circle?.frontend_url}`,
|
||||
hash: createLearningUnitHash(learningContent.parentLearningUnit),
|
||||
});
|
||||
}
|
||||
},
|
||||
openSelfEvaluation(learningUnit: LearningUnit) {
|
||||
this.router.push({
|
||||
path: learningUnit.evaluate_url,
|
||||
});
|
||||
},
|
||||
closeSelfEvaluation(learningUnit: LearningUnit) {
|
||||
routerBackOrFallback(this.router, {
|
||||
path: `${this.circle?.frontend_url}`,
|
||||
hash: createLearningUnitHash(learningUnit),
|
||||
});
|
||||
closeSelfEvaluation(
|
||||
learningUnit: LearningUnit,
|
||||
returnRoute?: RouteLocationNormalized
|
||||
) {
|
||||
if (returnRoute) {
|
||||
this.router.push(returnRoute);
|
||||
} else {
|
||||
this.router.push({
|
||||
path: `${this.circle?.frontend_url}`,
|
||||
hash: createLearningUnitHash(learningUnit),
|
||||
});
|
||||
}
|
||||
},
|
||||
calcSelfEvaluationStatus(learningUnit: LearningUnit): CourseCompletionStatus {
|
||||
if (learningUnit.children.length > 0) {
|
||||
|
|
@ -159,7 +173,10 @@ export const useCircleStore = defineStore({
|
|||
}
|
||||
return "UNKNOWN";
|
||||
},
|
||||
continueFromLearningContent(currentLearningContent: LearningContentInterface) {
|
||||
continueFromLearningContent(
|
||||
currentLearningContent: LearningContentInterface,
|
||||
returnRoute?: RouteLocationNormalized
|
||||
) {
|
||||
if (currentLearningContent) {
|
||||
if (currentLearningContent.can_user_self_toggle_course_completion) {
|
||||
this.markCompletion(currentLearningContent, "SUCCESS");
|
||||
|
|
@ -167,7 +184,7 @@ export const useCircleStore = defineStore({
|
|||
// reload completion data anyway
|
||||
currentLearningContent.parentCircle?.parentLearningPath?.reloadCompletionData();
|
||||
}
|
||||
this.closeLearningContent(currentLearningContent);
|
||||
this.closeLearningContent(currentLearningContent, returnRoute);
|
||||
} else {
|
||||
log.error("currentLearningContent is undefined");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue