194 lines
6.2 KiB
Vue
194 lines
6.2 KiB
Vue
<script setup lang="ts">
|
|
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
|
|
import { useCircleStore } from "@/stores/circle";
|
|
import type {
|
|
CourseCompletionStatus,
|
|
LearningContent,
|
|
LearningSequence,
|
|
} from "@/types";
|
|
import { humanizeDuration } from "@/utils/humanizeDuration";
|
|
import _ from "lodash";
|
|
import { computed } from "vue";
|
|
import LearningContentBadge from "./LearningContentTypeBadge.vue";
|
|
|
|
const props = defineProps<{
|
|
learningSequence: LearningSequence;
|
|
}>();
|
|
|
|
const circleStore = useCircleStore();
|
|
|
|
function toggleCompleted(learningContent: LearningContent) {
|
|
let completionStatus: CourseCompletionStatus = "success";
|
|
if (learningContent.completion_status === "success") {
|
|
completionStatus = "fail";
|
|
}
|
|
circleStore.markCompletion(learningContent, completionStatus);
|
|
}
|
|
|
|
const someFinished = computed(() => {
|
|
if (props.learningSequence && circleStore.circle) {
|
|
return circleStore.circle.someFinishedInLearningSequence(
|
|
props.learningSequence.translation_key
|
|
);
|
|
}
|
|
|
|
return false;
|
|
});
|
|
|
|
const allFinished = computed(() => {
|
|
if (props.learningSequence && circleStore.circle) {
|
|
return circleStore.circle.allFinishedInLearningSequence(
|
|
props.learningSequence.translation_key
|
|
);
|
|
}
|
|
|
|
return false;
|
|
});
|
|
|
|
const continueTranslationKeyTuple = computed(() => {
|
|
if (props.learningSequence && circleStore.circle) {
|
|
const lastFinished = _.findLast(
|
|
circleStore.circle.flatLearningContents,
|
|
(learningContent) => {
|
|
return learningContent.completion_status === "success";
|
|
}
|
|
);
|
|
|
|
if (!lastFinished) {
|
|
// must be the first
|
|
return [circleStore.circle.flatLearningContents[0].translation_key, true];
|
|
}
|
|
|
|
if (lastFinished && lastFinished.nextLearningContent) {
|
|
return [lastFinished.nextLearningContent.translation_key, false];
|
|
}
|
|
}
|
|
|
|
return "";
|
|
});
|
|
|
|
const learningSequenceBorderClass = computed(() => {
|
|
let result: string[] = [];
|
|
if (props.learningSequence && circleStore.circle) {
|
|
if (allFinished.value) {
|
|
result = ["border-l-4", "border-l-green-500"];
|
|
} else if (someFinished.value) {
|
|
result = ["border-l-4", "border-l-sky-500"];
|
|
} else {
|
|
result = ["border-l-gray-500"];
|
|
}
|
|
}
|
|
|
|
return result;
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div :id="learningSequence.slug" class="mb-8 learning-sequence">
|
|
<div class="flex items-center gap-4 mb-2 text-blue-900">
|
|
<component :is="learningSequence.icon" />
|
|
<h3 class="text-large font-semibold">
|
|
{{ learningSequence.title }}
|
|
</h3>
|
|
<div>{{ humanizeDuration(learningSequence.minutes) }}</div>
|
|
</div>
|
|
|
|
<div
|
|
class="bg-white px-4 lg:px-6 border border-gray-500"
|
|
:class="learningSequenceBorderClass"
|
|
>
|
|
<div
|
|
v-for="learningUnit in learningSequence.learningUnits"
|
|
:id="learningUnit.slug"
|
|
:key="learningUnit.id"
|
|
class="pt-3 lg:pt-6"
|
|
>
|
|
<div v-if="learningUnit.title" class="pb-3 lg:pg-6 flex gap-4 text-blue-900">
|
|
<div class="font-semibold">{{ learningUnit.title }}</div>
|
|
<div class="whitespace-nowrap">
|
|
{{ humanizeDuration(learningUnit.minutes) }}
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-for="learningContent in learningUnit.learningContents"
|
|
:key="learningContent.id"
|
|
class="flex gap-4 pb-3 lg:pb-6"
|
|
>
|
|
<ItCheckbox
|
|
:model-value="learningContent.completion_status === 'success'"
|
|
:on-toggle="() => toggleCompleted(learningContent)"
|
|
:data-cy="`${learningContent.slug}`"
|
|
>
|
|
<span class="flex flex-wrap gap-4 items-center">
|
|
<div
|
|
class="cursor-pointer w-full sm:w-auto"
|
|
@click.stop="circleStore.openLearningContent(learningContent)"
|
|
>
|
|
{{ learningContent.title }}
|
|
</div>
|
|
|
|
<div
|
|
class="flex items-center gap-4 flex-col justify-between sm:flex-row sm:grow"
|
|
>
|
|
<button
|
|
v-if="
|
|
learningContent.translation_key === continueTranslationKeyTuple[0]
|
|
"
|
|
class="btn-blue order-1 sm:order-none"
|
|
data-cy="ls-continue-button"
|
|
@click.stop="circleStore.openLearningContent(learningContent)"
|
|
>
|
|
<span v-if="continueTranslationKeyTuple[1]" class="whitespace-nowrap"
|
|
>Los geht's
|
|
</span>
|
|
<span v-else class="whitespace-nowrap"> Weiter geht's </span>
|
|
</button>
|
|
<div class="hidden sm:block"></div>
|
|
<div class="w-full sm:w-auto">
|
|
<LearningContentBadge
|
|
:learning-content-type="learningContent.contents[0].type"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</span>
|
|
</ItCheckbox>
|
|
</div>
|
|
|
|
<div
|
|
v-if="learningUnit.children.length"
|
|
class="hover:cursor-pointer"
|
|
:data-cy="`${learningUnit.slug}`"
|
|
@click="circleStore.openSelfEvaluation(learningUnit)"
|
|
>
|
|
<div
|
|
v-if="circleStore.calcSelfEvaluationStatus(learningUnit)"
|
|
class="flex items-center gap-4 pb-3 lg:pb-6 self-evaluation-success"
|
|
>
|
|
<it-icon-smiley-happy class="w-8 h-8 flex-none" data-cy="success" />
|
|
<div>Selbsteinschätzung: Ich kann das.</div>
|
|
</div>
|
|
<div
|
|
v-else-if="circleStore.calcSelfEvaluationStatus(learningUnit) === false"
|
|
class="flex items-center gap-4 pb-3 lg:pb-6 self-evaluation-fail"
|
|
>
|
|
<it-icon-smiley-thinking class="w-8 h-8 flex-none" data-cy="fail" />
|
|
<div>Selbsteinschätzung: Muss ich nochmals anschauen</div>
|
|
</div>
|
|
<div
|
|
v-else
|
|
class="flex items-center gap-4 pb-3 lg:pb-6 self-evaluation-unknown"
|
|
>
|
|
<it-icon-smiley-neutral class="w-8 h-8 flex-none" data-cy="unknown" />
|
|
<div>Selbsteinschätzung</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr v-if="!learningUnit.last" class="-mx-4 text-gray-500" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped></style>
|