Merge branch 'develop' into feat/geteilter-bereich

This commit is contained in:
Livio Bieri 2024-03-05 09:24:02 +01:00
commit 61a7f77650
10 changed files with 261 additions and 275 deletions

View File

@ -0,0 +1,34 @@
<script setup lang="ts">
type Category = "default" | "certificate";
export interface Props {
completed?: boolean;
category?: Category;
}
withDefaults(defineProps<Props>(), {
completed: false,
category: "default",
});
</script>
<template>
<component
:is="
completed
? 'it-icon-lc-competence-certificate-checked'
: 'it-icon-lc-competence-certificate'
"
v-if="category === 'certificate'"
class="h-[32px] w-[32px]"
:aria-checked="completed"
></component>
<div
v-else
class="flex h-[32px] w-[32px] items-center justify-center rounded-full border"
:class="[completed ? 'border-black' : 'border-gray-500']"
:aria-checked="completed"
>
<it-icon-check v-if="completed" class="block h-6 w-6 text-black"></it-icon-check>
</div>
</template>

View File

@ -4,28 +4,24 @@ import { showIcon } from "@/pages/learningPath/circlePage/learningSequenceUtils"
import { useCircleStore } from "@/stores/circle"; import { useCircleStore } from "@/stores/circle";
import type { import type {
CircleType, CircleType,
CourseCompletionStatus,
LearningContent, LearningContent,
LearningContentAssignment, LearningContentAssignment,
LearningContentEdoniqTest, LearningContentEdoniqTest,
LearningContentWithCompletion,
LearningSequence, LearningSequence,
} from "@/types"; } from "@/types";
import type { Ref } from "vue"; import type { Ref } from "vue";
import { computed } from "vue"; import { computed } from "vue";
import {
itCheckboxDefaultIconCheckedTailwindClass,
itCheckboxDefaultIconUncheckedTailwindClass,
} from "@/constants";
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
import { import {
allFinishedInLearningSequence, allFinishedInLearningSequence,
calcSelfEvaluationStatus,
circleFlatLearningContents, circleFlatLearningContents,
performanceCriteriaHasStatus,
performanceCriteriaStatusCount,
someFinishedInLearningSequence, someFinishedInLearningSequence,
} from "@/services/circle"; } from "@/services/circle";
import { useCourseDataWithCompletion } from "@/composables";
import { findLastIndex } from "lodash"; import { findLastIndex } from "lodash";
import CompletionStatus from "@/components/circle/CompletionStatus.vue";
type Props = { type Props = {
courseSlug: string; courseSlug: string;
@ -41,16 +37,6 @@ const props = withDefaults(defineProps<Props>(), {
const circleStore = useCircleStore(); const circleStore = useCircleStore();
const lpQueryResult = useCourseDataWithCompletion(props.courseSlug);
function toggleCompleted(learningContent: LearningContentWithCompletion) {
let completionStatus: CourseCompletionStatus = "SUCCESS";
if (learningContent.completion_status === "SUCCESS") {
completionStatus = "FAIL";
}
lpQueryResult.markCompletion(learningContent, completionStatus);
}
const someFinished = computed(() => { const someFinished = computed(() => {
if (props.learningSequence) { if (props.learningSequence) {
return someFinishedInLearningSequence(props.learningSequence); return someFinishedInLearningSequence(props.learningSequence);
@ -116,20 +102,6 @@ function belongsToCompetenceCertificate(lc: LearningContent) {
type LearninContentWithCompetenceCertificate = type LearninContentWithCompetenceCertificate =
| LearningContentAssignment | LearningContentAssignment
| LearningContentEdoniqTest; | LearningContentEdoniqTest;
function checkboxIconCheckedTailwindClass(lc: LearningContent) {
if (belongsToCompetenceCertificate(lc)) {
return "bg-[url(/static/icons/icon-lc-competence-certificate-checked.svg)]";
}
return itCheckboxDefaultIconCheckedTailwindClass;
}
function checkboxIconUncheckedTailwindClass(lc: LearningContent) {
if (belongsToCompetenceCertificate(lc)) {
return "bg-[url(/static/icons/icon-lc-competence-certificate.svg)]";
}
return itCheckboxDefaultIconUncheckedTailwindClass;
}
</script> </script>
<template> <template>
@ -143,9 +115,6 @@ function checkboxIconUncheckedTailwindClass(lc: LearningContent) {
<h3 class="text-large font-semibold"> <h3 class="text-large font-semibold">
{{ learningSequence.title }} {{ learningSequence.title }}
</h3> </h3>
<!-- <div v-if="learningSequence.minutes > 0">-->
<!-- {{ humanizeDuration(learningSequence.minutes) }}-->
<!-- </div>-->
</div> </div>
<ol class="border bg-white px-4 lg:px-6" :class="learningSequenceBorderClass"> <ol class="border bg-white px-4 lg:px-6" :class="learningSequenceBorderClass">
@ -163,9 +132,6 @@ function checkboxIconUncheckedTailwindClass(lc: LearningContent) {
<div class="font-semibold"> <div class="font-semibold">
{{ learningUnit.title }} {{ learningUnit.title }}
</div> </div>
<!-- <div v-if="learningUnit.minutes > 0" class="whitespace-nowrap">-->
<!-- {{ humanizeDuration(learningUnit.minutes) }}-->
<!-- </div>-->
</div> </div>
<ol> <ol>
<li <li
@ -175,43 +141,23 @@ function checkboxIconUncheckedTailwindClass(lc: LearningContent) {
> >
<div class="pb-6"> <div class="pb-6">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div v-if="props.readonly"> <CompletionStatus
<it-icon-check :data-cy="`${learningContent.slug}-status`"
v-if="learningContent.completion_status === 'SUCCESS'" :completed="learningContent.completion_status === 'SUCCESS'"
class="block h-8 w-8" :category="
></it-icon-check> belongsToCompetenceCertificate(learningContent)
<div v-else class="h-8 w-8"></div> ? 'certificate'
</div> : 'default'
<ItCheckbox
v-else
:checkbox-item="{
value: learningContent.completion_status,
checked: learningContent.completion_status === 'SUCCESS',
}"
:data-cy="`${learningContent.slug}-checkbox`"
:icon-checked-tailwind-class="
checkboxIconCheckedTailwindClass(learningContent)
"
:icon-unchecked-tailwind-class="
checkboxIconUncheckedTailwindClass(learningContent)
"
@toggle="toggleCompleted(learningContent)"
@click="
(event: MouseEvent) => {
// when disabled open the learning content directly
if (!learningContent.can_user_self_toggle_course_completion) {
circleStore.openLearningContent(learningContent);
event.preventDefault();
event.stopPropagation();
}
}
" "
/> />
<div <div
class="flex flex-auto flex-col gap-4 xl:flex-row xl:justify-between" class="flex flex-auto flex-col gap-4 xl:flex-row xl:items-center xl:justify-between"
> >
<div class="xl:order-last"> <div class="xl:order-last">
<LearningContentBadge :learning-content="learningContent" /> <LearningContentBadge
class="py-1"
:learning-content="learningContent"
/>
</div> </div>
<div> <div>
@ -231,7 +177,7 @@ function checkboxIconUncheckedTailwindClass(lc: LearningContent) {
</div> </div>
<div <div
v-if="belongsToCompetenceCertificate(learningContent) && !hideLinks" v-if="belongsToCompetenceCertificate(learningContent) && !hideLinks"
class="ml-16 text-sm text-gray-800" class="ml-12 text-sm text-gray-800"
> >
{{ {{
$t("circlePage.Dieser Inhalt gehört zu x", { $t("circlePage.Dieser Inhalt gehört zu x", {
@ -276,27 +222,54 @@ function checkboxIconUncheckedTailwindClass(lc: LearningContent) {
<div <div
v-if="learningUnit.performance_criteria.length" v-if="learningUnit.performance_criteria.length"
class="pb-6"
:class="{ 'cursor-pointer': !props.readonly }" :class="{ 'cursor-pointer': !props.readonly }"
:data-cy="`${learningUnit.slug}`" :data-cy="`${learningUnit.slug}`"
@click="!props.readonly && circleStore.openSelfEvaluation(learningUnit)" @click="!props.readonly && circleStore.openSelfEvaluation(learningUnit)"
> >
<div <div
v-if="calcSelfEvaluationStatus(learningUnit) === 'SUCCESS'" class="flex items-center gap-4"
class="self-evaluation-success flex items-center gap-4 pb-6" :data-cy="
performanceCriteriaHasStatus(learningUnit.performance_criteria)
? 'status'
: 'no-status'
"
> >
<it-icon-smiley-happy class="mr-4 h-8 w-8 flex-none" data-cy="success" /> <CompletionStatus />
<div>{{ $t("selfEvaluation.selfEvaluationYes") }}</div> {{ $t("a.Selbsteinschätzung") }}
</div> </div>
<div <div
v-else-if="calcSelfEvaluationStatus(learningUnit) === 'FAIL'" v-if="performanceCriteriaHasStatus(learningUnit.performance_criteria)"
class="self-evaluation-fail flex items-center gap-4 pb-6" class="flex items-center space-x-2 pl-12"
> >
<it-icon-smiley-thinking class="mr-4 h-8 w-8 flex-none" data-cy="fail" /> <it-icon-smiley-happy data-cy="success"></it-icon-smiley-happy>
<div>{{ $t("selfEvaluation.selfEvaluationNo") }}</div> <span class="pr-2">
</div> {{
<div v-else class="self-evaluation-unknown flex items-center gap-4 pb-6"> performanceCriteriaStatusCount(
<it-icon-smiley-neutral class="mr-4 h-8 w-8 flex-none" data-cy="unknown" /> learningUnit.performance_criteria,
<div>{{ $t("a.Selbsteinschätzung") }}</div> "SUCCESS"
)
}}
</span>
<it-icon-smiley-thinking data-cy="fail"></it-icon-smiley-thinking>
<span class="pr-2">
{{
performanceCriteriaStatusCount(
learningUnit.performance_criteria,
"FAIL"
)
}}
</span>
<it-icon-smiley-neutral data-cy="unknown"></it-icon-smiley-neutral>
<span>
{{
performanceCriteriaStatusCount(
learningUnit.performance_criteria,
"UNKNOWN"
)
}}
</span>
</div> </div>
</div> </div>
</li> </li>

View File

@ -2,7 +2,7 @@ import type {
CircleType, CircleType,
CourseCompletionStatus, CourseCompletionStatus,
LearningSequence, LearningSequence,
LearningUnit, LearningUnitPerformanceCriteria,
} from "@/types"; } from "@/types";
export function circleFlatChildren(circle: CircleType) { export function circleFlatChildren(circle: CircleType) {
@ -49,22 +49,15 @@ export function allFinishedInLearningSequence(ls: LearningSequence) {
}); });
} }
export function calcSelfEvaluationStatus( export function performanceCriteriaStatusCount(
learningUnit: LearningUnit performanceCriteria: LearningUnitPerformanceCriteria[],
): CourseCompletionStatus { status: CourseCompletionStatus
if (learningUnit.performance_criteria.length > 0) {
if (
learningUnit.performance_criteria.every((q) => q.completion_status === "SUCCESS")
) { ) {
return "SUCCESS"; return performanceCriteria.filter((pc) => pc.completion_status === status).length;
} }
if (
learningUnit.performance_criteria.every( export function performanceCriteriaHasStatus(
(q) => q.completion_status === "FAIL" || q.completion_status === "SUCCESS" performanceCriteria: LearningUnitPerformanceCriteria[]
)
) { ) {
return "FAIL"; return performanceCriteria.some((pc) => pc.completion_status !== "UNKNOWN");
}
}
return "UNKNOWN";
} }

View File

@ -113,8 +113,8 @@ function completePraxisAssignment() {
}); });
cy.reload(); cy.reload();
cy.get( cy.get(
"[data-cy=\"test-lehrgang-lp-circle-reisen-lc-mein-kundenstamm-checkbox\"]" "[data-cy=\"test-lehrgang-lp-circle-reisen-lc-mein-kundenstamm-status\"]"
).should("have.class", "cy-checked"); ).should("have.attr", "aria-checked", "true");
} }
describe("assignmentStudent.cy.js", () => { describe("assignmentStudent.cy.js", () => {
@ -338,8 +338,8 @@ describe("assignmentStudent.cy.js", () => {
cy.visit("/course/test-lehrgang/learn/fahrzeug/"); cy.visit("/course/test-lehrgang/learn/fahrzeug/");
cy.get( cy.get(
"[data-cy=\"test-lehrgang-lp-circle-fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice-checkbox\"]" "[data-cy=\"test-lehrgang-lp-circle-fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice-status\"]"
).should("have.class", "cy-checked"); ).should("have.attr", "aria-checked", "true");
//reopening page should get directly to last step //reopening page should get directly to last step
cy.visit( cy.visit(

View File

@ -9,71 +9,49 @@ describe("circle.cy.js", () => {
}); });
it("can open circle page", () => { it("can open circle page", () => {
cy.get('[data-cy="circle-title"]').should("contain", "Fahrzeug"); cy.get("[data-cy=\"circle-title\"]").should("contain", "Fahrzeug");
});
it("can toggle learning content", () => {
cy.get('[data-cy="circle-title"]').should("contain", "Fahrzeug");
cy.get(
'[data-cy="test-lehrgang-lp-circle-fahrzeug-lc-handlungsfeld-fahrzeug-checkbox"]'
).should("have.class", "cy-unchecked");
cy.get(
'[data-cy="test-lehrgang-lp-circle-fahrzeug-lc-handlungsfeld-fahrzeug-checkbox"]'
).click();
cy.get(
'[data-cy="test-lehrgang-lp-circle-fahrzeug-lc-handlungsfeld-fahrzeug-checkbox"]'
).should("have.class", "cy-checked");
// completion data should still be there after reload
cy.reload();
cy.get(
'[data-cy="test-lehrgang-lp-circle-fahrzeug-lc-handlungsfeld-fahrzeug-checkbox"]'
).should("have.class", "cy-checked");
}); });
it("can open learning contents and complete them by continuing", () => { it("can open learning contents and complete them by continuing", () => {
cy.get( cy.get(
'[data-cy="test-lehrgang-lp-circle-fahrzeug-lc-verschaffe-dir-einen-überblick"]' "[data-cy=\"test-lehrgang-lp-circle-fahrzeug-lc-verschaffe-dir-einen-überblick\"]"
).click(); ).click();
cy.get('[data-cy="lc-title"]').should( cy.get("[data-cy=\"lc-title\"]").should(
"contain", "contain",
"Verschaffe dir einen Überblick" "Verschaffe dir einen Überblick"
); );
cy.get('[data-cy="complete-and-continue"]').click({ force: true }); cy.get("[data-cy=\"complete-and-continue\"]").click({ force: true });
cy.get('[data-cy="circle-title"]').should("contain", "Fahrzeug"); cy.get("[data-cy=\"circle-title\"]").should("contain", "Fahrzeug");
cy.get('[data-cy="ls-continue-button"]').click(); cy.get("[data-cy=\"ls-continue-button\"]").click();
cy.get('[data-cy="lc-title"]').should( cy.get("[data-cy=\"lc-title\"]").should(
"contain", "contain",
"Handlungsfeld «Fahrzeug»" "Handlungsfeld «Fahrzeug»"
); );
cy.get('[data-cy="complete-and-continue"]').click({ force: true }); cy.get("[data-cy=\"complete-and-continue\"]").click({ force: true });
cy.get('[data-cy="circle-title"]').should("contain", "Fahrzeug"); cy.get("[data-cy=\"circle-title\"]").should("contain", "Fahrzeug");
cy.get( cy.get(
'[data-cy="test-lehrgang-lp-circle-fahrzeug-lc-verschaffe-dir-einen-überblick-checkbox"]' "[data-cy=\"test-lehrgang-lp-circle-fahrzeug-lc-verschaffe-dir-einen-überblick-status\"]"
).should("have.class", "cy-checked"); ).should("have.attr", "aria-checked", "true");
cy.get( cy.get(
'[data-cy="test-lehrgang-lp-circle-fahrzeug-lc-handlungsfeld-fahrzeug-checkbox"]' "[data-cy=\"test-lehrgang-lp-circle-fahrzeug-lc-handlungsfeld-fahrzeug-status\"]"
).should("have.class", "cy-checked"); ).should("have.attr", "aria-checked", "true");
}); });
it("continue button works", () => { it("continue button works", () => {
cy.get('[data-cy="ls-continue-button"]').should("contain", "Los geht's"); cy.get("[data-cy=\"ls-continue-button\"]").should("contain", "Los geht's");
cy.get('[data-cy="ls-continue-button"]').click(); cy.get("[data-cy=\"ls-continue-button\"]").click();
cy.get('[data-cy="lc-title"]').should( cy.get("[data-cy=\"lc-title\"]").should(
"contain", "contain",
"Verschaffe dir einen Überblick" "Verschaffe dir einen Überblick"
); );
cy.get('[data-cy="complete-and-continue"]').click({ force: true }); cy.get("[data-cy=\"complete-and-continue\"]").click({ force: true });
cy.get('[data-cy="ls-continue-button"]').should("contain", "Weiter geht's"); cy.get("[data-cy=\"ls-continue-button\"]").should("contain", "Weiter geht's");
cy.get('[data-cy="ls-continue-button"]').click(); cy.get("[data-cy=\"ls-continue-button\"]").click();
cy.get('[data-cy="lc-title"]').should( cy.get("[data-cy=\"lc-title\"]").should(
"contain", "contain",
"Handlungsfeld «Fahrzeug»" "Handlungsfeld «Fahrzeug»"
); );
@ -81,43 +59,43 @@ describe("circle.cy.js", () => {
it("can open learning content by url", () => { it("can open learning content by url", () => {
cy.visit("/course/test-lehrgang/learn/fahrzeug/handlungsfeld-fahrzeug"); cy.visit("/course/test-lehrgang/learn/fahrzeug/handlungsfeld-fahrzeug");
cy.get('[data-cy="lc-title"]').should( cy.get("[data-cy=\"lc-title\"]").should(
"contain", "contain",
"Handlungsfeld «Fahrzeug»" "Handlungsfeld «Fahrzeug»"
); );
cy.get('[data-cy="close-learning-content"]').click(); cy.get("[data-cy=\"close-learning-content\"]").click();
cy.get('[data-cy="circle-title"]').should("contain", "Fahrzeug"); cy.get("[data-cy=\"circle-title\"]").should("contain", "Fahrzeug");
}); });
it("checks number of sequences and contents", () => { it("checks number of sequences and contents", () => {
cy.get('[data-cy="lp-learning-sequence"]').should("have.length", 3); cy.get("[data-cy=\"lp-learning-sequence\"]").should("have.length", 3);
cy.get('[data-cy="lp-learning-sequence"]') cy.get("[data-cy=\"lp-learning-sequence\"]")
.first() .first()
.should("contain", "Vorbereitung"); .should("contain", "Vorbereitung");
cy.get('[data-cy="lp-learning-sequence"]') cy.get("[data-cy=\"lp-learning-sequence\"]")
.eq(1) .eq(1)
.should("contain", "Training"); .should("contain", "Training");
cy.get('[data-cy="lp-learning-sequence"]') cy.get("[data-cy=\"lp-learning-sequence\"]")
.last() .last()
.should("contain", "Transfer"); .should("contain", "Transfer");
cy.get('[data-cy="lp-learning-content"]').should("have.length", 10); cy.get("[data-cy=\"lp-learning-content\"]").should("have.length", 10);
cy.get('[data-cy="lp-learning-content"]') cy.get("[data-cy=\"lp-learning-content\"]")
.first() .first()
.should("contain", "Verschaffe dir einen Überblick"); .should("contain", "Verschaffe dir einen Überblick");
cy.get('[data-cy="lp-learning-content"]') cy.get("[data-cy=\"lp-learning-content\"]")
.eq(4) .eq(4)
.should("contain", "Präsenzkurs Fahrzeug"); .should("contain", "Präsenzkurs Fahrzeug");
cy.get('[data-cy="lp-learning-content"]') cy.get("[data-cy=\"lp-learning-content\"]")
.eq(7) .eq(7)
.should("contain", "Reflexion"); .should("contain", "Reflexion");
cy.get('[data-cy="lp-learning-content"]') cy.get("[data-cy=\"lp-learning-content\"]")
.last() .last()
.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", 3);
cy.get('[data-cy="lp-learning-content"]').should("have.length", 9); cy.get("[data-cy=\"lp-learning-content\"]").should("have.length", 9);
}); });
}); });

View File

@ -12,21 +12,21 @@ describe("selfEvaluation.cy.js", () => {
}); });
it("self evaluation should be neutral", () => { it("self evaluation should be neutral", () => {
cy.get('[data-cy="test-lehrgang-lp-circle-reisen-lu-reisen"]') cy.get("[data-cy=\"test-lehrgang-lp-circle-reisen-lu-reisen\"]")
.find('[data-cy="unknown"]') .find("[data-cy=\"no-status\"]")
.should("exist"); .should("exist");
}); });
it("self evaluation from KompetenzNavi", () => { it("self evaluation from KompetenzNavi", () => {
cy.get('[data-cy="test-lehrgang-lp-circle-reisen-lu-reisen"]') cy.get("[data-cy=\"test-lehrgang-lp-circle-reisen-lu-reisen\"]")
.find('[data-cy="unknown"]') .find("[data-cy=\"no-status\"]")
.should("exist"); .should("exist");
// data in KompetenzNavi/Übersicht is correct // data in KompetenzNavi/Übersicht is correct
cy.visit("/course/test-lehrgang/competence"); cy.visit("/course/test-lehrgang/competence");
cy.get('[data-cy="self-evaluation-fail"]').should("have.text", "0"); cy.get("[data-cy=\"self-evaluation-fail\"]").should("have.text", "0");
cy.get('[data-cy="self-evaluation-success"]').should("have.text", "0"); cy.get("[data-cy=\"self-evaluation-success\"]").should("have.text", "0");
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 = 687 also known as:

View File

@ -30,23 +30,23 @@ describe("feedbackStudent.cy.js", () => {
// fill feedback form // fill feedback form
// step 1 // step 1
cy.url().should("include", "step=1"); cy.url().should("include", "step=1");
cy.get('[data-cy="question-1"]').should( cy.get("[data-cy=\"question-1\"]").should(
"contain", "contain",
"Zufriedenheit insgesamt" "Zufriedenheit insgesamt"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="radio-4"]').click(); cy.get("[data-cy=\"radio-4\"]").click();
cy.wait(200); cy.wait(200);
cy.learningContentMultiLayoutNextStep(); cy.learningContentMultiLayoutNextStep();
cy.wait(200); cy.wait(200);
// step 2 // step 2
cy.url().should("include", "step=2"); cy.url().should("include", "step=2");
cy.get('[data-cy="question-2"]').should( cy.get("[data-cy=\"question-2\"]").should(
"contain", "contain",
"Zielerreichung insgesamt" "Zielerreichung insgesamt"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
// the system should store after every step -> check stored data // the system should store after every step -> check stored data
cy.loadFeedbackResponse("feedback_user_id", TEST_STUDENT1_USER_ID).then( cy.loadFeedbackResponse("feedback_user_id", TEST_STUDENT1_USER_ID).then(
(ac) => { (ac) => {
@ -55,67 +55,67 @@ describe("feedbackStudent.cy.js", () => {
expect(ac.data.instructor_competence).to.equal(null); expect(ac.data.instructor_competence).to.equal(null);
} }
); );
cy.get('[data-cy="radio-3"]').click(); cy.get("[data-cy=\"radio-3\"]").click();
cy.wait(200); cy.wait(200);
cy.learningContentMultiLayoutNextStep(); cy.learningContentMultiLayoutNextStep();
cy.wait(200); cy.wait(200);
// step 3 // step 3
cy.url().should("include", "step=3"); cy.url().should("include", "step=3");
cy.get('[data-cy="question-3"]').should( cy.get("[data-cy=\"question-3\"]").should(
"contain", "contain",
"Wie beurteilst du deine Sicherheit bezüglichen den Themen nach dem Kurs?" "Wie beurteilst du deine Sicherheit bezüglichen den Themen nach dem Kurs?"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="radio-80"]').click(); cy.get("[data-cy=\"radio-80\"]").click();
cy.wait(200); cy.wait(200);
cy.learningContentMultiLayoutNextStep(); cy.learningContentMultiLayoutNextStep();
cy.wait(200); cy.wait(200);
// step 4 // step 4
cy.url().should("include", "step=4"); cy.url().should("include", "step=4");
cy.get('[data-cy="question-4"]').should( cy.get("[data-cy=\"question-4\"]").should(
"contain", "contain",
"Waren die Vorbereitungsaufträge klar und verständlich?" "Waren die Vorbereitungsaufträge klar und verständlich?"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="radio-false"]').click(); cy.get("[data-cy=\"radio-false\"]").click();
cy.wait(200); cy.wait(200);
cy.learningContentMultiLayoutNextStep(); cy.learningContentMultiLayoutNextStep();
cy.wait(200); cy.wait(200);
// step 5 // step 5
cy.url().should("include", "step=5"); cy.url().should("include", "step=5");
cy.get('[data-cy="question-5"]').should( cy.get("[data-cy=\"question-5\"]").should(
"contain", "contain",
"Wie beurteilst du die Themensicherheit und Fachkompetenz des Kursleiters/der Kursleiterin?" "Wie beurteilst du die Themensicherheit und Fachkompetenz des Kursleiters/der Kursleiterin?"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="radio-2"]').click(); cy.get("[data-cy=\"radio-2\"]").click();
cy.wait(200); cy.wait(200);
cy.learningContentMultiLayoutNextStep(); cy.learningContentMultiLayoutNextStep();
cy.wait(200); cy.wait(200);
// step 6 // step 6
cy.url().should("include", "step=6"); cy.url().should("include", "step=6");
cy.get('[data-cy="question-6"]').should( cy.get("[data-cy=\"question-6\"]").should(
"contain", "contain",
"Wurden Fragen und Anregungen der Kursteilnehmenden ernst genommen und aufgegriffen?" "Wurden Fragen und Anregungen der Kursteilnehmenden ernst genommen und aufgegriffen?"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="radio-1"]').click(); cy.get("[data-cy=\"radio-1\"]").click();
cy.wait(200); cy.wait(200);
cy.learningContentMultiLayoutNextStep(); cy.learningContentMultiLayoutNextStep();
cy.wait(200); cy.wait(200);
// step 7 // step 7
cy.url().should("include", "step=7"); cy.url().should("include", "step=7");
cy.get('[data-cy="question-7"]').should( cy.get("[data-cy=\"question-7\"]").should(
"contain", "contain",
"Was möchtest du dem Kursleiter/der Kursleiterin sonst noch sagen?" "Was möchtest du dem Kursleiter/der Kursleiterin sonst noch sagen?"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="it-textarea-instructor_open_feedback"]').type( cy.get("[data-cy=\"it-textarea-instructor_open_feedback\"]").type(
"Der Kursleiter ist eigentlich ganz nett." "Der Kursleiter ist eigentlich ganz nett."
); );
cy.wait(200); cy.wait(200);
@ -124,25 +124,25 @@ describe("feedbackStudent.cy.js", () => {
// step 8 // step 8
cy.url().should("include", "step=8"); cy.url().should("include", "step=8");
cy.get('[data-cy="question-8"]').should( cy.get("[data-cy=\"question-8\"]").should(
"contain", "contain",
"Würdest du den Kurs weiterempfehlen?" "Würdest du den Kurs weiterempfehlen?"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="radio-true"]').click(); cy.get("[data-cy=\"radio-true\"]").click();
cy.wait(200); cy.wait(200);
cy.learningContentMultiLayoutNextStep(); cy.learningContentMultiLayoutNextStep();
cy.wait(200); cy.wait(200);
// step 9 // step 9
cy.url().should("include", "step=9"); cy.url().should("include", "step=9");
cy.get('[data-cy="question-9"]').should( cy.get("[data-cy=\"question-9\"]").should(
"contain", "contain",
"Was hat dir besonders gut gefallen?" "Was hat dir besonders gut gefallen?"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="it-textarea-course_positive_feedback"]').type( cy.get("[data-cy=\"it-textarea-course_positive_feedback\"]").type(
"Ich bin zufrieden mit den meisten Dingen." "Ich bin zufrieden mit den meisten Dingen."
); );
cy.wait(200); cy.wait(200);
@ -151,12 +151,12 @@ describe("feedbackStudent.cy.js", () => {
// step 10 // step 10
cy.url().should("include", "step=10"); cy.url().should("include", "step=10");
cy.get('[data-cy="question-10"]').should( cy.get("[data-cy=\"question-10\"]").should(
"contain", "contain",
"Wo siehst du Verbesserungspotential?" "Wo siehst du Verbesserungspotential?"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="it-textarea-course_negative_feedback"]').type( cy.get("[data-cy=\"it-textarea-course_negative_feedback\"]").type(
"Ich bin unzufrieden mit einigen Sachen." "Ich bin unzufrieden mit einigen Sachen."
); );
cy.wait(200); cy.wait(200);
@ -164,8 +164,8 @@ describe("feedbackStudent.cy.js", () => {
cy.wait(200); cy.wait(200);
cy.url().should("include", "step=11"); cy.url().should("include", "step=11");
cy.get('[data-cy="sendFeedbackButton"]').click(); cy.get("[data-cy=\"sendFeedbackButton\"]").click();
cy.get('[data-cy="complete-and-continue"]').click({ force: true }); cy.get("[data-cy=\"complete-and-continue\"]").click({ force: true });
// marked complete in circle // marked complete in circle
cy.url().should((url) => { cy.url().should((url) => {
@ -173,8 +173,8 @@ describe("feedbackStudent.cy.js", () => {
}); });
cy.reload(); cy.reload();
cy.get( cy.get(
'[data-cy="test-lehrgang-lp-circle-fahrzeug-lc-feedback-checkbox"]' "[data-cy=\"test-lehrgang-lp-circle-fahrzeug-lc-feedback-status\"]"
).should("have.class", "cy-checked"); ).should("have.attr", "aria-checked", "true");
// reopening page should get directly to last step // reopening page should get directly to last step
cy.visit("/course/test-lehrgang/learn/fahrzeug/feedback"); cy.visit("/course/test-lehrgang/learn/fahrzeug/feedback");
@ -197,7 +197,7 @@ describe("feedbackStudent.cy.js", () => {
proficiency: 80, proficiency: 80,
satisfaction: 4, satisfaction: 4,
would_recommend: true, would_recommend: true,
feedback_type: "uk", feedback_type: "uk"
}); });
} }
); );
@ -219,7 +219,7 @@ describe("feedbackStudent.cy.js", () => {
cy.url().should((url) => { cy.url().should((url) => {
expect(url).to.match(/\/reisen\/feedback(\?step=0)?$/); expect(url).to.match(/\/reisen\/feedback(\?step=0)?$/);
}); });
cy.get('[data-cy="introduction"]').contains( cy.get("[data-cy=\"introduction\"]").contains(
"Wir bitten dich um dein Feedback. Es hilft uns, damit wir deine Lernerlebnisse verbessern können." "Wir bitten dich um dein Feedback. Es hilft uns, damit wir deine Lernerlebnisse verbessern können."
); );
@ -230,23 +230,23 @@ describe("feedbackStudent.cy.js", () => {
// fill feedback form // fill feedback form
// step 1 // step 1
cy.url().should("include", "step=1"); cy.url().should("include", "step=1");
cy.get('[data-cy="question-1"]').should( cy.get("[data-cy=\"question-1\"]").should(
"contain", "contain",
"Zufriedenheit insgesamt" "Zufriedenheit insgesamt"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="radio-4"]').click(); cy.get("[data-cy=\"radio-4\"]").click();
cy.wait(200); cy.wait(200);
cy.learningContentMultiLayoutNextStep(); cy.learningContentMultiLayoutNextStep();
cy.wait(200); cy.wait(200);
// step 2 // step 2
cy.url().should("include", "step=2"); cy.url().should("include", "step=2");
cy.get('[data-cy="question-2"]').should( cy.get("[data-cy=\"question-2\"]").should(
"contain", "contain",
"Zielerreichung insgesamt" "Zielerreichung insgesamt"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
// the system should store after every step -> check stored data // the system should store after every step -> check stored data
cy.loadFeedbackResponse("feedback_user_id", TEST_STUDENT1_USER_ID).then( cy.loadFeedbackResponse("feedback_user_id", TEST_STUDENT1_USER_ID).then(
(ac) => { (ac) => {
@ -255,56 +255,56 @@ describe("feedbackStudent.cy.js", () => {
expect(ac.data.course_positive_feedback).to.equal(null); expect(ac.data.course_positive_feedback).to.equal(null);
} }
); );
cy.get('[data-cy="radio-3"]').click(); cy.get("[data-cy=\"radio-3\"]").click();
cy.wait(200); cy.wait(200);
cy.learningContentMultiLayoutNextStep(); cy.learningContentMultiLayoutNextStep();
cy.wait(200); cy.wait(200);
// step 3 // step 3
cy.url().should("include", "step=3"); cy.url().should("include", "step=3");
cy.get('[data-cy="question-3"]').should( cy.get("[data-cy=\"question-3\"]").should(
"contain", "contain",
"Wie beurteilst du deine Sicherheit bezüglichen den Themen nach dem Circle?" "Wie beurteilst du deine Sicherheit bezüglichen den Themen nach dem Circle?"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="radio-80"]').click(); cy.get("[data-cy=\"radio-80\"]").click();
cy.wait(200); cy.wait(200);
cy.learningContentMultiLayoutNextStep(); cy.learningContentMultiLayoutNextStep();
cy.wait(200); cy.wait(200);
// step 4 // step 4
cy.url().should("include", "step=4"); cy.url().should("include", "step=4");
cy.get('[data-cy="question-4"]').should( cy.get("[data-cy=\"question-4\"]").should(
"contain", "contain",
"Waren die Praxisaufträge klar und verständlich?" "Waren die Praxisaufträge klar und verständlich?"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="radio-false"]').click(); cy.get("[data-cy=\"radio-false\"]").click();
cy.wait(200); cy.wait(200);
cy.learningContentMultiLayoutNextStep(); cy.learningContentMultiLayoutNextStep();
cy.wait(200); cy.wait(200);
// step 5 // step 5
cy.url().should("include", "step=5"); cy.url().should("include", "step=5");
cy.get('[data-cy="question-5"]').should( cy.get("[data-cy=\"question-5\"]").should(
"contain", "contain",
"Würdest du den Circle weiterempfehlen?" "Würdest du den Circle weiterempfehlen?"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="radio-false"]').click(); cy.get("[data-cy=\"radio-false\"]").click();
cy.wait(200); cy.wait(200);
cy.learningContentMultiLayoutNextStep(); cy.learningContentMultiLayoutNextStep();
cy.wait(200); cy.wait(200);
// step 6 // step 6
cy.url().should("include", "step=6"); cy.url().should("include", "step=6");
cy.get('[data-cy="question-6"]').should( cy.get("[data-cy=\"question-6\"]").should(
"contain", "contain",
"Was hat dir besonders gut gefallen?" "Was hat dir besonders gut gefallen?"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="it-textarea-course_positive_feedback"]').type( cy.get("[data-cy=\"it-textarea-course_positive_feedback\"]").type(
"Der Circle ist eigentlich ganz nett." "Der Circle ist eigentlich ganz nett."
); );
cy.wait(200); cy.wait(200);
@ -313,12 +313,12 @@ describe("feedbackStudent.cy.js", () => {
// step 7 // step 7
cy.url().should("include", "step=7"); cy.url().should("include", "step=7");
cy.get('[data-cy="question-7"]').should( cy.get("[data-cy=\"question-7\"]").should(
"contain", "contain",
"Wo siehst du Verbesserungspotential?" "Wo siehst du Verbesserungspotential?"
); );
cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get("[data-cy=\"next-step\"]").should("be.disabled");
cy.get('[data-cy="it-textarea-course_negative_feedback"]').type( cy.get("[data-cy=\"it-textarea-course_negative_feedback\"]").type(
"Ich bin unzufrieden mit einigen Sachen." "Ich bin unzufrieden mit einigen Sachen."
); );
cy.wait(200); cy.wait(200);
@ -326,8 +326,8 @@ describe("feedbackStudent.cy.js", () => {
cy.wait(200); cy.wait(200);
cy.url().should("include", "step=8"); cy.url().should("include", "step=8");
cy.get('[data-cy="sendFeedbackButton"]').click(); cy.get("[data-cy=\"sendFeedbackButton\"]").click();
cy.get('[data-cy="complete-and-continue"]').click({ force: true }); cy.get("[data-cy=\"complete-and-continue\"]").click({ force: true });
// marked complete in circle // marked complete in circle
cy.url().should((url) => { cy.url().should((url) => {
@ -335,8 +335,8 @@ describe("feedbackStudent.cy.js", () => {
}); });
cy.reload(); cy.reload();
cy.get( cy.get(
'[data-cy="test-lehrgang-lp-circle-reisen-lc-feedback-checkbox"]' "[data-cy=\"test-lehrgang-lp-circle-reisen-lc-feedback-status\"]"
).should("have.class", "cy-checked"); ).should("have.attr", "aria-checked", "true");
// reopening page should get directly to last step // reopening page should get directly to last step
cy.visit("/course/test-lehrgang/learn/reisen/feedback"); cy.visit("/course/test-lehrgang/learn/reisen/feedback");
@ -354,7 +354,7 @@ describe("feedbackStudent.cy.js", () => {
proficiency: 80, proficiency: 80,
satisfaction: 4, satisfaction: 4,
would_recommend: false, would_recommend: false,
feedback_type: "vv", feedback_type: "vv"
}); });
} }
); );

View File

@ -8,55 +8,54 @@ describe("learningPath.cy.js", () => {
}); });
it("can open learningPath page", () => { it("can open learningPath page", () => {
cy.get('[data-cy="learning-path-title"]').should( cy.get("[data-cy=\"learning-path-title\"]").should(
"contain", "contain",
"Test Lehrgang" "Test Lehrgang"
); );
}); });
it("can click on circle to open it", () => { it("can click on circle to open it", () => {
cy.get('[data-cy="circle-Fahrzeug"]').click({ force: true }); cy.get("[data-cy=\"circle-Fahrzeug\"]").click({ force: true });
cy.url().should("include", "/course/test-lehrgang/learn/fahrzeug"); cy.url().should("include", "/course/test-lehrgang/learn/fahrzeug");
cy.get('[data-cy="circle-title"]').should("contain", "Fahrzeug"); cy.get("[data-cy=\"circle-title\"]").should("contain", "Fahrzeug");
}); });
it("switch between list and path view", () => { it("switch between list and path view", () => {
cy.get('[data-cy="lp-path-view"]').should("be.visible"); cy.get("[data-cy=\"lp-path-view\"]").should("be.visible");
cy.get('[data-cy="view-switch"]').click(); cy.get("[data-cy=\"view-switch\"]").click();
cy.get('[data-cy="lp-list-view"]').should("be.visible"); cy.get("[data-cy=\"lp-list-view\"]").should("be.visible");
cy.get('[data-cy="view-switch"]').click(); cy.get("[data-cy=\"view-switch\"]").click();
cy.get('[data-cy="lp-path-view"]').should("be.visible"); cy.get("[data-cy=\"lp-path-view\"]").should("be.visible");
}); });
it("weiter gehts button will open next circle", () => { it("weiter gehts button will open next circle", () => {
// first click will open first circle // first click will open first circle
cy.get('[data-cy="lp-continue-button"]') cy.get("[data-cy=\"lp-continue-button\"]")
.filter(":visible") .filter(":visible")
.should("contain", "Los geht's") .should("contain", "Los geht's")
.click(); .click();
cy.get('[data-cy="circle-title"]').should("contain", "Fahrzeug"); cy.get("[data-cy=\"circle-title\"]").should("contain", "Fahrzeug");
cy.get('[data-cy="back-to-learning-path-button"]').click(); cy.get("[data-cy=\"back-to-learning-path-button\"]").click();
// mark a learning content in second circle // mark a learning content in second circle
cy.get('[data-cy="circle-Reisen"]').click({ force: true }); cy.get("[data-cy=\"circle-Reisen\"]").click({ force: true });
cy.get( cy.get("[data-cy=\"ls-continue-button\"]").click();
'[data-cy="test-lehrgang-lp-circle-reisen-lc-fachcheck-reisen-checkbox"]' cy.get("[data-cy=\"complete-and-continue\"]").click({ force: true });
).click(); cy.get("[data-cy=\"back-to-learning-path-button\"]").click();
cy.get('[data-cy="back-to-learning-path-button"]').click();
// click on continue should go to unit-test-circle // click on continue should go to unit-test-circle
cy.get('[data-cy="lp-continue-button"]') cy.get("[data-cy=\"lp-continue-button\"]")
.filter(":visible") .filter(":visible")
.should("contain", "Weiter geht's") .should("contain", "Weiter geht's")
.click(); .click();
cy.get('[data-cy="circle-title"]').should("contain", "Reisen"); cy.get("[data-cy=\"circle-title\"]").should("contain", "Reisen");
}); });
it("checks contents", () => { it("checks contents", () => {
cy.get('[data-cy="lp-topic"]').should("have.length", 2); cy.get("[data-cy=\"lp-topic\"]").should("have.length", 2);
cy.get('[data-cy="lp-topic"]').first().should("contain", "Circle ÜK"); cy.get("[data-cy=\"lp-topic\"]").first().should("contain", "Circle ÜK");
cy.get('[data-cy="lp-topic"]').eq(1).should("contain", "Circle VV"); cy.get("[data-cy=\"lp-topic\"]").eq(1).should("contain", "Circle VV");
cy.get(".cy-lp-circle").should("have.length", 2); cy.get(".cy-lp-circle").should("have.length", 2);
cy.get(".cy-lp-circle").first().should("contain", "Fahrzeug"); cy.get(".cy-lp-circle").first().should("contain", "Fahrzeug");

View File

@ -40,7 +40,7 @@ IT_VV_TEST_COURSE = "Iterativ VV Testkurs"
IT_UK_TEST_COURSE = "Iterativ üK Testkurs" IT_UK_TEST_COURSE = "Iterativ üK Testkurs"
IT_UK_TEST_REGION = "Iterativ Region" IT_UK_TEST_REGION = "Iterativ Region"
TIME_FORMAT = "%d.%m.%Y, %H:%M" TIME_FORMAT = "%d.%m.%Y, %H:%M"
PASSWORD = "KqaDm3-x8zhCKHLWDV_oiqFrYWHg" PASSWORD = "myvbv1234"
logger = structlog.get_logger(__name__) logger = structlog.get_logger(__name__)
@ -176,17 +176,21 @@ def add_trainers_to_course_session(
def get_or_create_users_uk(): def get_or_create_users_uk():
members = [ members = [
_create_or_update_user( _create_or_update_user(
f"teilnehmer{n}.uk@iterativ.ch", "Teilnehmer üK", "Iterativ", PASSWORD, "de" f"teilnehmer{n}.uk@iterativ.ch",
f"Teilnehmer{n} üK",
f"Iterativ{n}",
PASSWORD,
"de",
) )
for n in range(1, 10) for n in range(1, 10)
] ]
trainer = _create_or_update_user( trainer = _create_or_update_user(
"trainer1.uk@iterativ.ch", "Trainer üK", "Iterativ", PASSWORD, "de" "trainer1.uk@iterativ.ch", "Trainer1 üK", "Iterativ1", PASSWORD, "de"
) )
regionenleiter = _create_or_update_user( regionenleiter = _create_or_update_user(
"regionenleiter1.uk@iterativ.ch", "regionenleiter1.uk@iterativ.ch",
"Regionenleiter üK", "Regionenleiter1 üK",
"Iterativ", "Iterativ1",
PASSWORD, PASSWORD,
"de", "de",
) )
@ -200,21 +204,25 @@ def get_or_create_users_uk():
def get_or_create_users_vv(): def get_or_create_users_vv():
members = [ members = [
_create_or_update_user( _create_or_update_user(
f"teilnehmer{n}.vv@iterativ.ch", "Teilnehmer VV", "Iterativ", PASSWORD, "de" f"teilnehmer{n}.vv@iterativ.ch",
f"Teilnehmer{n} VV ",
f"Iterativ{n}",
PASSWORD,
"de",
) )
for n in range(1, 10) for n in range(1, 10)
] ]
member_with_mentor = _create_or_update_user( member_with_mentor = _create_or_update_user(
"teilnehmer1.vv.lb@iterativ.ch", "teilnehmer1.mitlb.vv@iterativ.ch",
"Teilnehmer VV mit LB", "Teilnehmer1 VV mit LB",
"Iterativ", "Iterativ1",
PASSWORD, PASSWORD,
"de", "de",
) )
mentor = _create_or_update_user( mentor = _create_or_update_user(
"lernbegleitung1.vv@iterativ.ch", "lernbegleitung1.vv@iterativ.ch",
"Lernbegleitung VV", "Lernbegleitung1 VV",
"Iterativ", "Iterativ1",
PASSWORD, PASSWORD,
"de", "de",
) )

View File

@ -1,8 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<g clip-path="url(#clip0_2_2021)"> <g clip-path="url(#clip0_2_2021)">
<circle cx="25.5" cy="18.5" r="5.5" fill="#54CE8B"/> <circle cx="25.5" cy="18.5" r="5.5" fill="#54CE8B"/>
<path d="M7.5 11.7762L11.5938 15.87L20.4638 7" stroke="#54CE8B" stroke-width="2"/> <path d="M7.5 11.7762L11.5938 15.87L20.4638 7" stroke="#54CE8B" stroke-width="2" fill="none"/>
<path d="M29 32C28.7614 32.0004 28.5305 31.9153 28.3493 31.76L25.5 29.3173L22.6507 31.7587C22.5055 31.8831 22.3277 31.9632 22.1384 31.9897C21.949 32.0161 21.7561 31.9878 21.5824 31.9079C21.4087 31.8281 21.2615 31.7001 21.1583 31.5392C21.0551 31.3783 21.0001 31.1912 21 31V27.8C20.4707 27.9147 19.804 28 19 28H7C6.73478 28 6.48043 27.8946 6.29289 27.7071C6.10536 27.5196 6 27.2652 6 27C6 26.7348 6.10536 26.4804 6.29289 26.2929C6.48043 26.1054 6.73478 26 7 26C8.10267 26 9 25.1027 9 24C9 23.6493 9.06 23.312 9.172 23H3C1.34667 23 0 21.6533 0 20V3C0 1.34667 1.34667 0 3 0H25C26.6533 0 28 1.34667 28 3V10H26V3C25.9996 2.73489 25.8942 2.48074 25.7067 2.29328C25.5193 2.10582 25.2651 2.00035 25 2H3C2.73489 2.00035 2.48074 2.10582 2.29328 2.29328C2.10582 2.48074 2.00035 2.73489 2 3V20C2 20.5507 2.44933 21 3 21H17C17.2652 21 17.5196 21.1054 17.7071 21.2929C17.8946 21.4804 18 21.7348 18 22C18 22.2652 17.8946 22.5196 17.7071 22.7071C17.5196 22.8946 17.2652 23 17 23H12C11.7349 23.0004 11.4807 23.1058 11.2933 23.2933C11.1058 23.4807 11.0004 23.7349 11 24C11 24.728 10.8053 25.412 10.464 26H19C20.6547 26 21.4973 25.548 21.5067 25.5427C21.6595 25.458 21.8318 25.4148 22.0064 25.4171C22.1811 25.4194 22.3522 25.4673 22.5027 25.556C22.6529 25.6434 22.7778 25.7684 22.865 25.9187C22.9522 26.0691 22.9988 26.2395 23 26.4133V28.8267L24.8493 27.24C25.0308 27.0852 25.2615 27.0002 25.5 27.0002C25.7385 27.0002 25.9692 27.0852 26.1507 27.24L28 28.8267L27.9987 25.1893C27.9413 25.096 27.8747 25.052 27.7667 25.0933C26.8053 25.4533 25.788 25.5733 24.7427 25.4573C21.8933 25.132 19.512 22.8467 19.0787 20.0213C18.9155 19.0074 18.9959 17.9691 19.3133 16.9923C19.6307 16.0156 20.176 15.1284 20.904 14.404C21.6281 13.6754 22.5152 13.1298 23.492 12.8121C24.4688 12.4945 25.5072 12.414 26.5213 12.5773C27.6114 12.7501 28.6395 13.1977 29.5087 13.8779C30.3779 14.5581 31.0595 15.4484 31.4894 16.4649C31.9192 17.4815 32.083 18.5907 31.9655 19.6881C31.8479 20.7855 31.4528 21.8349 30.8173 22.7373L29.1827 21.588C29.8454 20.6417 30.1212 19.4777 29.9533 18.3347C29.6827 16.4333 28.112 14.8453 26.2187 14.5547C25.5148 14.4421 24.7942 14.4979 24.116 14.7177C23.4379 14.9374 22.8215 15.3147 22.3173 15.8187C21.8128 16.3221 21.4352 16.9383 21.2157 17.6164C20.9961 18.2945 20.9409 19.0151 21.0547 19.7187C21.3547 21.668 22.9987 23.2453 24.968 23.4707C25.6785 23.5563 26.3992 23.4698 27.0693 23.2187C27.4025 23.0929 27.7612 23.0495 28.1148 23.0922C28.4683 23.1349 28.8064 23.2625 29.1 23.464C29.672 23.8613 29.9987 24.4907 29.9987 25.1893V31C29.998 31.265 29.8924 31.5189 29.705 31.7063C29.5176 31.8937 29.265 31.9993 29 32Z" fill="black"/> <path d="M29 32C28.7614 32.0004 28.5305 31.9153 28.3493 31.76L25.5 29.3173L22.6507 31.7587C22.5055 31.8831 22.3277 31.9632 22.1384 31.9897C21.949 32.0161 21.7561 31.9878 21.5824 31.9079C21.4087 31.8281 21.2615 31.7001 21.1583 31.5392C21.0551 31.3783 21.0001 31.1912 21 31V27.8C20.4707 27.9147 19.804 28 19 28H7C6.73478 28 6.48043 27.8946 6.29289 27.7071C6.10536 27.5196 6 27.2652 6 27C6 26.7348 6.10536 26.4804 6.29289 26.2929C6.48043 26.1054 6.73478 26 7 26C8.10267 26 9 25.1027 9 24C9 23.6493 9.06 23.312 9.172 23H3C1.34667 23 0 21.6533 0 20V3C0 1.34667 1.34667 0 3 0H25C26.6533 0 28 1.34667 28 3V10H26V3C25.9996 2.73489 25.8942 2.48074 25.7067 2.29328C25.5193 2.10582 25.2651 2.00035 25 2H3C2.73489 2.00035 2.48074 2.10582 2.29328 2.29328C2.10582 2.48074 2.00035 2.73489 2 3V20C2 20.5507 2.44933 21 3 21H17C17.2652 21 17.5196 21.1054 17.7071 21.2929C17.8946 21.4804 18 21.7348 18 22C18 22.2652 17.8946 22.5196 17.7071 22.7071C17.5196 22.8946 17.2652 23 17 23H12C11.7349 23.0004 11.4807 23.1058 11.2933 23.2933C11.1058 23.4807 11.0004 23.7349 11 24C11 24.728 10.8053 25.412 10.464 26H19C20.6547 26 21.4973 25.548 21.5067 25.5427C21.6595 25.458 21.8318 25.4148 22.0064 25.4171C22.1811 25.4194 22.3522 25.4673 22.5027 25.556C22.6529 25.6434 22.7778 25.7684 22.865 25.9187C22.9522 26.0691 22.9988 26.2395 23 26.4133V28.8267L24.8493 27.24C25.0308 27.0852 25.2615 27.0002 25.5 27.0002C25.7385 27.0002 25.9692 27.0852 26.1507 27.24L28 28.8267L27.9987 25.1893C27.9413 25.096 27.8747 25.052 27.7667 25.0933C26.8053 25.4533 25.788 25.5733 24.7427 25.4573C21.8933 25.132 19.512 22.8467 19.0787 20.0213C18.9155 19.0074 18.9959 17.9691 19.3133 16.9923C19.6307 16.0156 20.176 15.1284 20.904 14.404C21.6281 13.6754 22.5152 13.1298 23.492 12.8121C24.4688 12.4945 25.5072 12.414 26.5213 12.5773C27.6114 12.7501 28.6395 13.1977 29.5087 13.8779C30.3779 14.5581 31.0595 15.4484 31.4894 16.4649C31.9192 17.4815 32.083 18.5907 31.9655 19.6881C31.8479 20.7855 31.4528 21.8349 30.8173 22.7373L29.1827 21.588C29.8454 20.6417 30.1212 19.4777 29.9533 18.3347C29.6827 16.4333 28.112 14.8453 26.2187 14.5547C25.5148 14.4421 24.7942 14.4979 24.116 14.7177C23.4379 14.9374 22.8215 15.3147 22.3173 15.8187C21.8128 16.3221 21.4352 16.9383 21.2157 17.6164C20.9961 18.2945 20.9409 19.0151 21.0547 19.7187C21.3547 21.668 22.9987 23.2453 24.968 23.4707C25.6785 23.5563 26.3992 23.4698 27.0693 23.2187C27.4025 23.0929 27.7612 23.0495 28.1148 23.0922C28.4683 23.1349 28.8064 23.2625 29.1 23.464C29.672 23.8613 29.9987 24.4907 29.9987 25.1893V31C29.998 31.265 29.8924 31.5189 29.705 31.7063C29.5176 31.8937 29.265 31.9993 29 32Z"
fill="black"/>
</g> </g>
<defs> <defs>
<clipPath id="clip0_2_2021"> <clipPath id="clip0_2_2021">

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB