Refactor design of learning contents in circle page

This commit is contained in:
Daniel Egger 2023-09-07 18:24:49 +02:00
parent 853c8746ea
commit e43e351e9d
7 changed files with 118 additions and 122 deletions

View File

@ -1,22 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import type { CheckboxItem } from "@/components/ui/checkbox.types"; import type { CheckboxItem } from "@/components/ui/checkbox.types";
import log from "loglevel"; import log from "loglevel";
import {
itCheckboxDefaultIconCheckedTailwindClass,
itCheckboxDefaultIconUncheckedTailwindClass,
} from "@/constants";
interface Props { interface Props {
checkboxItem: CheckboxItem<any>; checkboxItem: CheckboxItem<any>;
disabled?: boolean; disabled?: boolean;
iconCheckedPath?: string; iconCheckedTailwindClass?: string;
iconCheckedHoverPath?: string; iconUncheckedTailwindClass?: string;
iconUncheckedPath?: string;
iconUncheckedHoverPath?: string;
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
disabled: false, disabled: false,
iconCheckedPath: "/static/icons/icon-checkbox-checked.svg", iconCheckedTailwindClass: itCheckboxDefaultIconCheckedTailwindClass,
iconCheckedHoverPath: "/static/icons/icon-checkbox-checked-hover.svg", iconUncheckedTailwindClass: itCheckboxDefaultIconUncheckedTailwindClass,
iconUncheckedPath: "/static/icons/icon-checkbox-unchecked.svg",
iconUncheckedHoverPath: "/static/icons/icon-checkbox-unchecked-hover.svg",
}); });
const emit = defineEmits(["toggle"]); const emit = defineEmits(["toggle"]);
@ -51,8 +51,8 @@ const input = () => {
class="flex h-8 items-center bg-contain bg-no-repeat pl-8 disabled:opacity-50" class="flex h-8 items-center bg-contain bg-no-repeat pl-8 disabled:opacity-50"
:class=" :class="
checkboxItem.checked checkboxItem.checked
? `bg-[url(${props.iconCheckedPath})] hover:bg-[url(${props.iconCheckedHoverPath})]` ? props.iconCheckedTailwindClass
: `bg-[url(${props.iconUncheckedPath})] hover:bg-[url(${props.iconUncheckedHoverPath})]` : props.iconUncheckedTailwindClass
" "
tabindex="0" tabindex="0"
@keydown.stop="keydown" @keydown.stop="keydown"

View File

@ -3,3 +3,9 @@ import type { CourseCompletionStatus } from "@/types";
export const COMPLETION_SUCCESS: CourseCompletionStatus = "SUCCESS"; export const COMPLETION_SUCCESS: CourseCompletionStatus = "SUCCESS";
export const COMPLETION_FAILURE: CourseCompletionStatus = "FAIL"; export const COMPLETION_FAILURE: CourseCompletionStatus = "FAIL";
export const COMPLETION_UNKNOWN: CourseCompletionStatus = "UNKNOWN"; export const COMPLETION_UNKNOWN: CourseCompletionStatus = "UNKNOWN";
export const itCheckboxDefaultIconCheckedTailwindClass =
"bg-[url(/static/icons/icon-checkbox-checked.svg)] hover:bg-[url(/static/icons/icon-checkbox-checked-hover.svg)]";
export const itCheckboxDefaultIconUncheckedTailwindClass =
"bg-[url(/static/icons/icon-checkbox-unchecked.svg)] hover:bg-[url(/static/icons/icon-checkbox-unchecked-hover.svg)]";

View File

@ -11,16 +11,12 @@
"Anwesenheit prüfen": "Anwesenheit prüfen", "Anwesenheit prüfen": "Anwesenheit prüfen",
"Anwesenheitskontrolle Präsenzkurse": "Anwesenheitskontrolle Präsenzkurse", "Anwesenheitskontrolle Präsenzkurse": "Anwesenheitskontrolle Präsenzkurse",
"Benutzername": "Benutzername", "Benutzername": "Benutzername",
"Die Anwesenheit wurde definitiv bestätigt": [],
"Ergebnisse anschauen": "Ergebnisse anschauen", "Ergebnisse anschauen": "Ergebnisse anschauen",
"Ergebnisse anzeigen": "Ergebnisse anzeigen", "Ergebnisse anzeigen": "Ergebnisse anzeigen",
"Feedback": "Feedback", "Feedback": "Feedback",
"Feedback anschauen": "Feedback anschauen", "Feedback anschauen": "Feedback anschauen",
"Feedback: Feedback zum Lehrgang": "Feedback: Feedback zum Lehrgang", "Feedback: Feedback zum Lehrgang": "Feedback: Feedback zum Lehrgang",
"Freigabetermin Bewertungen:": "Freigabetermin Bewertungen:", "Freigabetermin Bewertungen:": "Freigabetermin Bewertungen:",
"Hier überprüfst und bestätigst du die Anwesenheit deiner Teilnehmenden": [],
"Ich will die Anwesenheit der untenstehenden Personen definitiv bestätigen": [],
"Keine Auftragsdetails verfügbar": [],
"MS Teams öffnen": "MS Teams öffnen", "MS Teams öffnen": "MS Teams öffnen",
"Nächste Termine": "Nächste Termine", "Nächste Termine": "Nächste Termine",
"Passwort": "Passwort", "Passwort": "Passwort",
@ -335,4 +331,4 @@
"x von y Feedbacks abgegeben": "{{x}} von {{y}} Feedbacks abgegeben", "x von y Feedbacks abgegeben": "{{x}} von {{y}} Feedbacks abgegeben",
"x von y abgeschlossen": "{{x}} von {{y}} abgeschlossen", "x von y abgeschlossen": "{{x}} von {{y}} abgeschlossen",
"zufrieden": "zufrieden" "zufrieden": "zufrieden"
} }

View File

@ -21,6 +21,7 @@
"Ergebnisse abgegeben": "Résultats remis", "Ergebnisse abgegeben": "Résultats remis",
"Gesamtpunktzahl": "Score total", "Gesamtpunktzahl": "Score total",
"Höchstpunktzahl": "Score maximum", "Höchstpunktzahl": "Score maximum",
"KompetenzNavi": "NaviCompétence",
"Punkte": "points", "Punkte": "points",
"Zwischenstand": "Point intermédiaire", "Zwischenstand": "Point intermédiaire",
"Übersicht": "Aperçu" "Übersicht": "Aperçu"
@ -310,4 +311,4 @@
"x von y Ergebnisse abgegeben": "{{x}} de {{y}} résultats remis", "x von y Ergebnisse abgegeben": "{{x}} de {{y}} résultats remis",
"x von y Feedbacks abgegeben": "{{x}} de {{y}} feedbacks remis", "x von y Feedbacks abgegeben": "{{x}} de {{y}} feedbacks remis",
"x von y abgeschlossen": "{{x}} de {{y}} achevée/s" "x von y abgeschlossen": "{{x}} de {{y}} achevée/s"
} }

View File

@ -21,6 +21,7 @@
"Ergebnisse abgegeben": "Risultati consegnati", "Ergebnisse abgegeben": "Risultati consegnati",
"Gesamtpunktzahl": "Punteggio totale", "Gesamtpunktzahl": "Punteggio totale",
"Höchstpunktzahl": "Punteggio massimo", "Höchstpunktzahl": "Punteggio massimo",
"KompetenzNavi": "NaviCompetenze",
"Punkte": "punti", "Punkte": "punti",
"Zwischenstand": "Punto intermedio", "Zwischenstand": "Punto intermedio",
"Übersicht": "Panoramica" "Übersicht": "Panoramica"
@ -310,4 +311,4 @@
"x von y Ergebnisse abgegeben": "{{x}} di {{y}} risultati consegnati/o", "x von y Ergebnisse abgegeben": "{{x}} di {{y}} risultati consegnati/o",
"x von y Feedbacks abgegeben": "{{x}} di {{y}} feedback consegnati/o", "x von y Feedbacks abgegeben": "{{x}} di {{y}} feedback consegnati/o",
"x von y abgeschlossen": "{{x}} di {{y}} completate/a" "x von y abgeschlossen": "{{x}} di {{y}} completate/a"
} }

View File

@ -6,12 +6,18 @@ import { useCircleStore } from "@/stores/circle";
import type { import type {
CourseCompletionStatus, CourseCompletionStatus,
LearningContent, LearningContent,
LearningContentAssignment,
LearningContentEdoniqTest,
LearningContentInterface, LearningContentInterface,
LearningSequence, LearningSequence,
} from "@/types"; } from "@/types";
import { humanizeDuration } from "@/utils/humanizeDuration";
import findLast from "lodash/findLast"; import findLast from "lodash/findLast";
import { computed } from "vue"; import { computed } from "vue";
import { humanizeDuration } from "../../../utils/humanizeDuration";
import {
itCheckboxDefaultIconCheckedTailwindClass,
itCheckboxDefaultIconUncheckedTailwindClass,
} from "@/constants";
type Props = { type Props = {
learningSequence: LearningSequence; learningSequence: LearningSequence;
@ -97,35 +103,22 @@ function belongsToCompetenceCertificate(lc: LearningContent) {
); );
} }
function checkboxIconCheckedPath(lc: LearningContent) { type LearninContentWithCompetenceCertificate =
| LearningContentAssignment
| LearningContentEdoniqTest;
function checkboxIconCheckedTailwindClass(lc: LearningContent) {
if (belongsToCompetenceCertificate(lc)) { if (belongsToCompetenceCertificate(lc)) {
return "/static/icons/icon-lc-competence-certificate-checked.svg"; return "bg-[url(/static/icons/icon-lc-competence-certificate-checked.svg)]";
} }
return "/static/icons/icon-checkbox-checked.svg"; return itCheckboxDefaultIconCheckedTailwindClass;
} }
function checkboxIconCheckedHoverPath(lc: LearningContent) { function checkboxIconUncheckedTailwindClass(lc: LearningContent) {
if (belongsToCompetenceCertificate(lc)) { if (belongsToCompetenceCertificate(lc)) {
return "/static/icons/icon-lc-competence-certificate-checked.svg"; return "bg-[url(/static/icons/icon-lc-competence-certificate.svg)]";
} }
return itCheckboxDefaultIconUncheckedTailwindClass;
return "/static/icons/icon-checkbox-checked-hover.svg";
}
function checkboxIconUncheckedPath(lc: LearningContent) {
if (belongsToCompetenceCertificate(lc)) {
return "/static/icons/icon-lc-competence-certificate.svg";
}
return "/static/icons/icon-checkbox-unchecked.svg";
}
function checkboxIconUncheckedHoverPath(lc: LearningContent) {
if (belongsToCompetenceCertificate(lc)) {
return "/static/icons/icon-lc-competence-certificate.svg";
}
return "/static/icons/icon-checkbox-unchecked-hover.svg";
} }
</script> </script>
@ -163,33 +156,36 @@ function checkboxIconUncheckedHoverPath(lc: LearningContent) {
<li <li
v-for="learningContent in learningUnit.learningContents" v-for="learningContent in learningUnit.learningContents"
:key="learningContent.id" :key="learningContent.id"
class="flex items-center gap-4 pb-3 lg:pb-6"
> >
<div v-if="props.readonly"> <div class="flex items-center gap-4 pb-6">
<it-icon-check <div v-if="props.readonly">
v-if="learningContent.completion_status === 'SUCCESS'" <it-icon-check
class="block h-8 w-8" v-if="learningContent.completion_status === 'SUCCESS'"
></it-icon-check> class="block h-8 w-8"
<div v-else class="h-8 w-8"></div> ></it-icon-check>
</div> <div v-else class="h-8 w-8"></div>
<ItCheckbox </div>
v-else <ItCheckbox
:checkbox-item="{ v-else
value: learningContent.completion_status, :checkbox-item="{
checked: learningContent.completion_status === 'SUCCESS', value: learningContent.completion_status,
}" checked: learningContent.completion_status === 'SUCCESS',
:disabled="!learningContent.can_user_self_toggle_course_completion" }"
:data-cy="`${learningContent.slug}-checkbox`" :disabled="!learningContent.can_user_self_toggle_course_completion"
:icon-checked-path="checkboxIconCheckedPath(learningContent)" :data-cy="`${learningContent.slug}-checkbox`"
:icon-checked-hover-path="checkboxIconCheckedHoverPath(learningContent)" :icon-checked-tailwind-class="
:icon-unchecked-path="checkboxIconUncheckedPath(learningContent)" checkboxIconCheckedTailwindClass(learningContent)
:icon-unchecked-hover-path=" "
checkboxIconUncheckedHoverPath(learningContent) :icon-unchecked-tailwind-class="
" checkboxIconUncheckedTailwindClass(learningContent)
@toggle="toggleCompleted(learningContent)" "
/> @toggle="toggleCompleted(learningContent)"
<div class="flex-auto pt-1 sm:pt-0"> />
<div class="flex items-center gap-4"> <div class="flex flex-auto flex-col gap-4 xl:flex-row xl:justify-between">
<div class="xl:order-last">
<LearningContentBadge :learning-content="learningContent" />
</div>
<div> <div>
<div v-if="props.readonly" class="w-full text-left sm:w-auto"> <div v-if="props.readonly" class="w-full text-left sm:w-auto">
{{ learningContent.title }} {{ learningContent.title }}
@ -202,52 +198,51 @@ function checkboxIconUncheckedHoverPath(lc: LearningContent) {
> >
{{ learningContent.title }} {{ learningContent.title }}
</button> </button>
<div
v-if="belongsToCompetenceCertificate(learningContent)"
class="text-sm text-gray-800"
>
{{ $t("circlePage.Dieser Inhalt gehört zu x") }}
<br />
<router-link
v-if="learningContent.competence_certificate?.frontend_url"
:to="learningContent.competence_certificate.frontend_url"
class="link"
data-cy="show-results"
>
{{ $t("circlePage.Im KompetenzNavi anschauen") }}
</router-link>
</div>
</div>
<div
class="flex flex-col items-center justify-between gap-4 sm:grow md:flex-row lg:flex-col xl:flex-row"
>
<button
v-if="
learningContent.translation_key ===
continueTranslationKeyTuple[0] && !props.readonly
"
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"
>
{{ $t("general.start") }}
</span>
<span v-else class="whitespace-nowrap">
{{ $t("general.nextStep") }}
</span>
</button>
<div class="hidden sm:block"></div>
<div class="w-full sm:w-auto">
<LearningContentBadge :learning-content="learningContent" />
</div>
</div> </div>
</div> </div>
</div> </div>
<div
v-if="belongsToCompetenceCertificate(learningContent)"
class="mb-8 ml-16 text-sm text-gray-800"
>
{{
$t("circlePage.Dieser Inhalt gehört zu x", {
x: (learningContent as LearninContentWithCompetenceCertificate)
?.competence_certificate?.title,
})
}}
<br />
<router-link
v-if="(learningContent as LearninContentWithCompetenceCertificate).competence_certificate?.frontend_url"
:to="(learningContent as LearninContentWithCompetenceCertificate).competence_certificate?.frontend_url ?? ''"
class="link"
data-cy="show-results"
>
{{ $t("circlePage.Im KompetenzNavi anschauen") }}
</router-link>
</div>
<div
v-if="
learningContent.translation_key === continueTranslationKeyTuple[0] &&
!props.readonly
"
class="mb-6"
>
<button
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">
{{ $t("general.start") }}
</span>
<span v-else class="whitespace-nowrap">
{{ $t("general.nextStep") }}
</span>
</button>
</div>
</li> </li>
</ol> </ol>
@ -259,24 +254,21 @@ function checkboxIconUncheckedHoverPath(lc: LearningContent) {
> >
<div <div
v-if="circleStore.calcSelfEvaluationStatus(learningUnit) === 'SUCCESS'" v-if="circleStore.calcSelfEvaluationStatus(learningUnit) === 'SUCCESS'"
class="self-evaluation-success flex items-center gap-4 pb-3 lg:pb-6" class="self-evaluation-success flex items-center gap-4 pb-6"
> >
<it-icon-smiley-happy class="h-8 w-8 flex-none" data-cy="success" /> <it-icon-smiley-happy class="mr-4 h-8 w-8 flex-none" data-cy="success" />
<div>{{ $t("selfEvaluation.selfEvaluationYes") }}</div> <div>{{ $t("selfEvaluation.selfEvaluationYes") }}</div>
</div> </div>
<div <div
v-else-if="circleStore.calcSelfEvaluationStatus(learningUnit) === 'FAIL'" v-else-if="circleStore.calcSelfEvaluationStatus(learningUnit) === 'FAIL'"
class="self-evaluation-fail flex items-center gap-4 pb-3 lg:pb-6" class="self-evaluation-fail flex items-center gap-4 pb-6"
> >
<it-icon-smiley-thinking class="h-8 w-8 flex-none" data-cy="fail" /> <it-icon-smiley-thinking class="mr-4 h-8 w-8 flex-none" data-cy="fail" />
<div>{{ $t("selfEvaluation.selfEvaluationNo") }}</div> <div>{{ $t("selfEvaluation.selfEvaluationNo") }}</div>
</div> </div>
<div <div v-else class="self-evaluation-unknown flex items-center gap-4 pb-6">
v-else <it-icon-smiley-neutral class="mr-4 h-8 w-8 flex-none" data-cy="unknown" />
class="self-evaluation-unknown flex items-center gap-4 pb-3 lg:pb-6" <div>{{ $t("a.Selbsteinschätzung") }}</div>
>
<it-icon-smiley-neutral class="h-8 w-8 flex-none" data-cy="unknown" />
<div>{{ $t("selfEvaluation.selfEvaluation") }}</div>
</div> </div>
</div> </div>

View File

@ -6,9 +6,9 @@ from vbv_lernwelt.competence.serializers import (
from vbv_lernwelt.core.utils import get_django_content_type from vbv_lernwelt.core.utils import get_django_content_type
from vbv_lernwelt.course.serializer_helpers import get_course_serializer_class from vbv_lernwelt.course.serializer_helpers import get_course_serializer_class
from vbv_lernwelt.learnpath.models import ( from vbv_lernwelt.learnpath.models import (
LearningUnit,
LearningContentEdoniqTest,
LearningContentAssignment, LearningContentAssignment,
LearningContentEdoniqTest,
LearningUnit,
) )