Changes after code review

This commit is contained in:
Daniel Egger 2023-04-12 15:08:10 +02:00
parent 27ebe8a69f
commit d4c846a1b9
14 changed files with 81 additions and 75 deletions

View File

@ -1,6 +1,6 @@
<template> <template>
<AccountMenuContent <AccountMenuContent
:course-sessions="courseSessionsStore.currentCourseSessions" :course-sessions="courseSessionsStore.allCurrentCourseSessions"
:selected-course-session="courseSessionsStore.currentCourseSession?.id" :selected-course-session="courseSessionsStore.currentCourseSession?.id"
:user="userStore" :user="userStore"
@logout="logout" @logout="logout"

View File

@ -1,5 +1,6 @@
import avatar from "../../../.storybook/uk1.patrizia.huggel.jpg"; import avatar from "../../../.storybook/uk1.patrizia.huggel.jpg";
import AccountMenuContent from "./AccountMenuContent.vue"; import AccountMenuContent from "./AccountMenuContent.vue";
const bern = { const bern = {
id: 1, id: 1,
title: "Bern 2023 a", title: "Bern 2023 a",
@ -31,7 +32,7 @@ describe("<AccountMenuContent />", () => {
cy.mount(AccountMenuContent, { cy.mount(AccountMenuContent, {
props: { props: {
user, user,
courseSessions, allCourseSessions: courseSessions,
selectedCourseSession: selectedSession, selectedCourseSession: selectedSession,
onSelectCourseSession: selectCourseSession, onSelectCourseSession: selectCourseSession,
}, },

View File

@ -42,14 +42,14 @@ const loggedInUser = {
export const LoggedInUser: Story = { export const LoggedInUser: Story = {
args: { args: {
courseSessions, allCourseSessions: courseSessions,
user: loggedInUser, user: loggedInUser,
}, },
}; };
export const NoUser: Story = { export const NoUser: Story = {
args: { args: {
courseSessions, allCourseSessions: courseSessions,
user: { user: {
loggedIn: false, loggedIn: false,
}, },

View File

@ -33,8 +33,7 @@ function userCountStatusForCircle(userId: number, translationKey: string) {
); );
const grouped = groupBy(criteria, "circle.translation_key"); const grouped = groupBy(criteria, "circle.translation_key");
// @ts-ignore return competenceStore.calcStatusCount(grouped[translationKey] as []);
return competenceStore.calcStatusCount(grouped[translationKey]);
} }
const circles = computed(() => { const circles = computed(() => {

View File

@ -24,7 +24,7 @@ const attendanceDay = computed(() => {
<div class="lg:mt-8"> <div class="lg:mt-8">
<div class="text-large my-4"> <div class="text-large my-4">
<div v-if="attendanceDay">{{ attendanceDay }}</div> <div v-if="attendanceDay">{{ attendanceDay }}</div>
<div v-else>Keine Präsenztagdaten erfasst für diese Durchführung</div> <div v-else>Für diese Durchführung existieren noch keine Details</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -171,9 +171,9 @@ router.beforeEach(redirectToLoginIfRequired);
router.beforeEach((to) => { router.beforeEach((to) => {
const courseSessionStore = useCourseSessionsStore(); const courseSessionStore = useCourseSessionsStore();
if (to.params.courseSlug) { if (to.params.courseSlug) {
courseSessionStore.currentCourseSlug = to.params.courseSlug as string; courseSessionStore._currentCourseSlug = to.params.courseSlug as string;
} else { } else {
courseSessionStore.currentCourseSlug = ""; courseSessionStore._currentCourseSlug = "";
} }
}); });

View File

@ -108,7 +108,7 @@ export class LearningPath implements WagtailLearningPath {
public async reloadCompletionData() { public async reloadCompletionData() {
const learningPathStore = useLearningPathStore(); const learningPathStore = useLearningPathStore();
const completionData = await learningPathStore.loadCompletionData( const completionData = await learningPathStore.loadCourseSessionCompletionData(
this.course.slug, this.course.slug,
this.userId this.userId
); );

View File

@ -63,8 +63,8 @@ describe("CourseSession Store", () => {
userStore.$patch(user); userStore.$patch(user);
const courseSessionsStore = useCourseSessionsStore(); const courseSessionsStore = useCourseSessionsStore();
courseSessionsStore.currentCourseSlug = "test-course"; courseSessionsStore._currentCourseSlug = "test-course";
courseSessionsStore.courseSessions = courseSessions; courseSessionsStore.allCourseSessions = courseSessions;
expect(courseSessionsStore.hasCockpit).toBeFalsy(); expect(courseSessionsStore.hasCockpit).toBeFalsy();
}); });
@ -74,8 +74,8 @@ describe("CourseSession Store", () => {
userStore.$patch(Object.assign(user, { is_superuser: true })); userStore.$patch(Object.assign(user, { is_superuser: true }));
const courseSessionsStore = useCourseSessionsStore(); const courseSessionsStore = useCourseSessionsStore();
courseSessionsStore.currentCourseSlug = "test-course"; courseSessionsStore._currentCourseSlug = "test-course";
courseSessionsStore.courseSessions = courseSessions; courseSessionsStore.allCourseSessions = courseSessions;
expect(courseSessionsStore.hasCockpit).toBeTruthy(); expect(courseSessionsStore.hasCockpit).toBeTruthy();
}); });
@ -87,8 +87,8 @@ describe("CourseSession Store", () => {
); );
const courseSessionsStore = useCourseSessionsStore(); const courseSessionsStore = useCourseSessionsStore();
courseSessionsStore.currentCourseSlug = "test-course"; courseSessionsStore._currentCourseSlug = "test-course";
courseSessionsStore.courseSessions = courseSessions; courseSessionsStore.allCourseSessions = courseSessions;
expect(courseSessionsStore.hasCockpit).toBeTruthy(); expect(courseSessionsStore.hasCockpit).toBeTruthy();
}); });

View File

@ -169,7 +169,7 @@ export const useCompetenceStore = defineStore({
const courseSessionsStore = useCourseSessionsStore(); const courseSessionsStore = useCourseSessionsStore();
const courseSession = courseSessionsStore.currentCourseSession; const courseSession = courseSessionsStore.currentCourseSession;
if (courseSession) { if (courseSession) {
const completionData = await completionStore.loadCompletionData( const completionData = await completionStore.loadCourseSessionCompletionData(
courseSession.id, courseSession.id,
userId userId
); );

View File

@ -11,7 +11,11 @@ export const useCompletionStore = defineStore({
}, },
getters: {}, getters: {},
actions: { actions: {
async loadCompletionData(courseSessionId: number, userId: number, reload = false) { async loadCourseSessionCompletionData(
courseSessionId: number,
userId: number,
reload = false
) {
const userCompletionData = (await itGetCached( const userCompletionData = (await itGetCached(
`/api/course/completion/${courseSessionId}/${userId}/`, `/api/course/completion/${courseSessionId}/${userId}/`,
{ {

View File

@ -57,7 +57,7 @@ function loadCourseSessionsData(reload = false) {
loadAndUpdate(); // this will be called asynchronously, but does not block loadAndUpdate(); // this will be called asynchronously, but does not block
// returns the empty sessions array at first, then after loading populates the ref // returns the empty sessions array at first, then after loading populates the ref
return { courseSessions }; return { allCourseSessions: courseSessions };
} }
export const useCourseSessionsStore = defineStore("courseSessions", () => { export const useCourseSessionsStore = defineStore("courseSessions", () => {
@ -67,26 +67,33 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
// store should do own setup, we don't want to have each component initialize it // store should do own setup, we don't want to have each component initialize it
// that's why we call the load function in here // that's why we call the load function in here
const { courseSessions } = loadCourseSessionsData(); const { allCourseSessions } = loadCourseSessionsData();
const selectedCourseSessionMap = useLocalStorage( const selectedCourseSessionMap = useLocalStorage(
SELECTED_COURSE_SESSIONS_KEY, SELECTED_COURSE_SESSIONS_KEY,
new Map<string, number>() new Map<string, number>()
); );
const currentCourseSlug = ref(""); const _currentCourseSlug = ref("");
// these will become getters
const uniqueCourseSessionsByCourse = computed(() => const uniqueCourseSessionsByCourse = computed(() =>
// TODO: refactor after implementing of Klassenkonzept // Im Dashboard wird aktuell ein Widget pro Kurs dargestellt
// mit dem Fortschritt jeweils einer Durchführung.
// Dieser Getter wird nur dort benutzt.
// TODO: Das Dashboard verschwindet evtl. in Zukunft, dann kann dieser Getter weg.
// @ts-ignore // @ts-ignore
uniqBy(courseSessions.value, "course.id") uniqBy(allCourseSessions.value, "course.id")
); );
function selectedCourseSessionForCourse(courseSlug: string) { function selectedCourseSessionForCourse(courseSlug: string) {
// Wir wollen pro Kurs wissen, welche Durchführung der User zuletzt ausgewählt hat.
// Die letzte Durchführung wird im localStorage via `selectedCoruseSessionMap`
// gespeichert und hier geladen.
// Wenn noch keine Durchführung ausgewählt wurde, wird die erste Durchführung
// in `courseSessionForCourse` zurückgegeben.
try { try {
const courseSessionId = selectedCourseSessionMap.value.get(courseSlug); const courseSessionId = selectedCourseSessionMap.value.get(courseSlug);
if (courseSessionId) { if (courseSessionId) {
return courseSessions.value.find((cs) => { return allCourseSessions.value.find((cs) => {
return cs.id === courseSessionId; return cs.id === courseSessionId;
}); });
} }
@ -94,6 +101,7 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
log.error("Error while parsing courseSessions from localStorage", e); log.error("Error while parsing courseSessions from localStorage", e);
} }
// Keine Durchführung ausgewählt im localStorage
return undefined; return undefined;
} }
@ -111,7 +119,7 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
return courseSession; return courseSession;
} else { } else {
// return first if there is no selected courseSession // return first if there is no selected courseSession
return courseSessions.value.find((cs) => { return allCourseSessions.value.find((cs) => {
return cs.course.slug === courseSlug; return cs.course.slug === courseSlug;
}); });
} }
@ -120,18 +128,18 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
return undefined; return undefined;
} }
function courseSessionsForCourse(courseSlug: string) { function allCourseSessionsForCourse(courseSlug: string) {
return courseSessions.value.filter((cs) => { return allCourseSessions.value.filter((cs) => {
return cs.course.slug === courseSlug; return cs.course.slug === courseSlug;
}); });
} }
const currentCourseSession = computed(() => { const currentCourseSession = computed(() => {
return courseSessionForCourse(currentCourseSlug.value); return courseSessionForCourse(_currentCourseSlug.value);
}); });
const currentCourseSessions = computed(() => { const allCurrentCourseSessions = computed(() => {
return courseSessionsForCourse(currentCourseSlug.value); return allCourseSessionsForCourse(_currentCourseSlug.value);
}); });
const hasCockpit = computed(() => { const hasCockpit = computed(() => {
@ -196,7 +204,7 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
async function startUpload() { async function startUpload() {
log.debug("loadCourseSessionsData called"); log.debug("loadCourseSessionsData called");
courseSessions.value = await itPost(`/api/core/file/start`, { allCourseSessions.value = await itPost(`/api/core/file/start`, {
file_type: "image/png", file_type: "image/png",
file_name: "test.png", file_name: "test.png",
}); });
@ -227,7 +235,7 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
return { return {
uniqueCourseSessionsByCourse, uniqueCourseSessionsByCourse,
currentCourseSession, currentCourseSession,
currentCourseSessions, allCurrentCourseSessions,
courseSessionForCourse, courseSessionForCourse,
switchCourseSession, switchCourseSession,
hasCockpit, hasCockpit,
@ -239,10 +247,10 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
removeDocument, removeDocument,
findAttendanceDay, findAttendanceDay,
// TODO: only used to be changed by router.afterEach // only used so that `router.afterEach` can switch it
currentCourseSlug, _currentCourseSlug,
// TODO: only used for unit testing // only used for unit testing
courseSessions, allCourseSessions,
}; };
}); });

View File

@ -41,10 +41,11 @@ export const useLearningPathStore = defineStore("learningPath", () => {
}; };
}); });
async function loadCompletionData( async function loadCourseSessionCompletionData(
courseSlug: string, courseSlug: string,
userId: number | undefined = undefined userId: number | undefined = undefined
) { ) {
// FIXME: should not be here anymore with VBV-305
const completionStore = useCompletionStore(); const completionStore = useCompletionStore();
let completionData: CourseCompletion[] = []; let completionData: CourseCompletion[] = [];
@ -52,7 +53,7 @@ export const useLearningPathStore = defineStore("learningPath", () => {
const courseSessionsStore = useCourseSessionsStore(); const courseSessionsStore = useCourseSessionsStore();
const courseSession = courseSessionsStore.courseSessionForCourse(courseSlug); const courseSession = courseSessionsStore.courseSessionForCourse(courseSlug);
if (courseSession) { if (courseSession) {
completionData = await completionStore.loadCompletionData( completionData = await completionStore.loadCourseSessionCompletionData(
courseSession.id, courseSession.id,
userId userId
); );
@ -85,7 +86,7 @@ export const useLearningPathStore = defineStore("learningPath", () => {
throw `No learning path found with: ${slug}`; throw `No learning path found with: ${slug}`;
} }
const completionData = await loadCompletionData( const completionData = await loadCourseSessionCompletionData(
learningPathData.course.slug, learningPathData.course.slug,
userId userId
); );
@ -99,19 +100,22 @@ export const useLearningPathStore = defineStore("learningPath", () => {
return learningPath; return learningPath;
} }
function reloadCompletionData() { eventBus.on("switchedCourseSession", (courseSession) => {
log.debug("handle switchedCourseSession", courseSession);
// FIXME: clean up with VBV-305
// Die Completion Daten werden nur für die aktuelle Durchführung geladen.
// Deshalb müssen die hier neu geladen werden...
state.learningPaths.forEach((lp) => { state.learningPaths.forEach((lp) => {
if (lp.userId) { if (lp.userId) {
lp.reloadCompletionData(); lp.reloadCompletionData();
} }
}); });
}
eventBus.on("switchedCourseSession", (courseSession) => {
log.debug("handle switchedCourseSession", courseSession);
// FIXME: clean up with VBV-305
reloadCompletionData();
}); });
return { state, learningPathForUser, loadCompletionData, loadLearningPath }; return {
state,
learningPathForUser,
loadCourseSessionCompletionData,
loadLearningPath,
};
}); });

View File

@ -5,49 +5,38 @@ describe("Competence", () => {
cy.manageCommand("cypress_reset"); cy.manageCommand("cypress_reset");
login("admin", "test"); login("admin", "test");
cy.visit("/course/versicherungsvermittler-in/learn/fahrzeug");
// test-lehrgang-lp-circle-analyse-lu-fahrzeug ist eine Selbstevaluation
// mit mehreren Schritten
cy.visit("/course/test-lehrgang/learn/analyse");
}); });
it("self evaluation should be neutral", () => { it("self evaluation should be neutral", () => {
cy.get( cy.get('[data-cy="test-lehrgang-lp-circle-analyse-lu-fahrzeug"]')
'[data-cy="versicherungsvermittler-in-lp-circle-fahrzeug-lu-fahrzeug-1"]'
)
.find('[data-cy="unknown"]') .find('[data-cy="unknown"]')
.should("exist"); .should("exist");
}); });
it("should be able to make a happy self evaluation", () => { it("should be able to make a happy self evaluation", () => {
cy.get( cy.get('[data-cy="test-lehrgang-lp-circle-analyse-lu-fahrzeug"]').click();
'[data-cy="versicherungsvermittler-in-lp-circle-fahrzeug-lu-fahrzeug-1"]' cy.makeSelfEvaluation([true, true]);
).click(); cy.get('[data-cy="test-lehrgang-lp-circle-analyse-lu-fahrzeug"]')
cy.makeSelfEvaluation([true]);
cy.get(
'[data-cy="versicherungsvermittler-in-lp-circle-fahrzeug-lu-fahrzeug-1"]'
)
.find('[data-cy="success"]') .find('[data-cy="success"]')
.should("exist"); .should("exist");
}); });
it("should be able to make a fail self evaluation", () => { it("should be able to make a fail self evaluation", () => {
cy.get( cy.get('[data-cy="test-lehrgang-lp-circle-analyse-lu-fahrzeug"]').click();
'[data-cy="versicherungsvermittler-in-lp-circle-fahrzeug-lu-fahrzeug-1"]' cy.makeSelfEvaluation([false, false]);
).click(); cy.get('[data-cy="test-lehrgang-lp-circle-analyse-lu-fahrzeug"]')
cy.makeSelfEvaluation([false]);
cy.get(
'[data-cy="versicherungsvermittler-in-lp-circle-fahrzeug-lu-fahrzeug-1"]'
)
.find('[data-cy="fail"]') .find('[data-cy="fail"]')
.should("exist"); .should("exist");
}); });
it.skip("should be able to make a mixed self evaluation", () => { it("should be able to make a mixed self evaluation", () => {
cy.get( cy.get('[data-cy="test-lehrgang-lp-circle-analyse-lu-fahrzeug"]').click();
'[data-cy="versicherungsvermittler-in-alt-lp-circle-analyse-lu-fahrzeug"]' cy.makeSelfEvaluation([false, true]);
).click(); cy.get('[data-cy="test-lehrgang-lp-circle-analyse-lu-fahrzeug"]')
cy.makeSelfEvaluation([false, true, true]);
cy.get(
'[data-cy="versicherungsvermittler-in-alt-lp-circle-analyse-lu-fahrzeug"]'
)
.find('[data-cy="fail"]') .find('[data-cy="fail"]')
.should("exist"); .should("exist");
}); });

View File

@ -7,8 +7,9 @@ describe("MediaLibrary", () => {
login("admin", "test"); login("admin", "test");
}); });
// skipped as long the MainNavigation is not finished
it.skip("should be accessible via link in header", () => { it.skip("should be accessible via link in header", () => {
// der Link zur Mediathek fehlt aktuell im Header (auch im Design)
// das ganze Konzept der Mediathek wird noch überdacht -> skip
cy.visit("/course/test-lehrgang"); cy.visit("/course/test-lehrgang");
cy.get('[data-cy="medialibrary-link"]').click(); cy.get('[data-cy="medialibrary-link"]').click();
cy.get('[data-cy="Handlungsfelder-link"]').click(); cy.get('[data-cy="Handlungsfelder-link"]').click();