Replace dueDate code
This commit is contained in:
parent
41b0d3ae4d
commit
8cb00b0976
|
|
@ -1,50 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { useCurrentCourseSession, useDashboardPersons } from "@/composables";
|
||||
import DueDateSingle from "@/components/dueDates/DueDateSingle.vue";
|
||||
import { computed } from "vue";
|
||||
import { useExpertCockpitStore } from "@/stores/expertCockpit";
|
||||
import _ from "lodash";
|
||||
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
||||
|
||||
const expertCockpitStore = useExpertCockpitStore();
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
const courseSessionId = courseSession.value.id;
|
||||
|
||||
const { loading: loadingDates, currentDueDates } = useDashboardPersons();
|
||||
|
||||
const circleDates = computed(() => {
|
||||
const courseSessionId = courseSession.value.id;
|
||||
const circleId = expertCockpitStore.currentCircle?.id ?? "0";
|
||||
const dueDates = currentDueDates.value.filter((dueDate) => {
|
||||
return (
|
||||
dueDate.course_session_id === courseSessionId && dueDate.circle?.id === circleId
|
||||
);
|
||||
});
|
||||
return _.take(dueDates, 3);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="loadingDates" class="m-8 flex justify-center">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
<div v-else class="flex flex-col space-y-2">
|
||||
<h3 class="heading-3">{{ $t("Nächste Termine") }}</h3>
|
||||
<div
|
||||
v-for="dueDate in circleDates"
|
||||
:key="dueDate.id"
|
||||
class="border-t border-gray-500 pt-2"
|
||||
>
|
||||
<DueDateSingle :due-date="dueDate"></DueDateSingle>
|
||||
</div>
|
||||
<div v-if="circleDates.length === 0">{{ $t("dueDates.noDueDatesAvailable") }}</div>
|
||||
</div>
|
||||
|
||||
<router-link
|
||||
class="btn-secondary mt-4"
|
||||
:to="`/dashboard/due-dates?session=${courseSessionId}`"
|
||||
>
|
||||
{{ $t("a.Alle Termine anzeigen") }}
|
||||
</router-link>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<script lang="ts" setup>
|
||||
import { useDashboardPersons } from "@/composables";
|
||||
import { computed } from "vue";
|
||||
import _ from "lodash";
|
||||
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
||||
import DueDateSingle from "@/components/dueDates/DueDateSingle.vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
courseSessionId: string;
|
||||
circleId?: string;
|
||||
maxCount?: number;
|
||||
showAllButton?: boolean;
|
||||
}>(),
|
||||
{
|
||||
maxCount: 3,
|
||||
circleId: undefined,
|
||||
showAllButton: false,
|
||||
}
|
||||
);
|
||||
|
||||
const { loading, currentDueDates } = useDashboardPersons();
|
||||
|
||||
const filteredDueDates = computed(() => {
|
||||
let dueDates = currentDueDates.value.filter(
|
||||
(dueDate) => dueDate.course_session_id === props.courseSessionId
|
||||
);
|
||||
|
||||
if (props.circleId) {
|
||||
dueDates = dueDates.filter((dueDate) => dueDate.circle?.id === props.circleId);
|
||||
}
|
||||
return _.take(dueDates, props.maxCount);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="loading" class="m-8 flex justify-center">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
<div v-else class="flex flex-col space-y-2">
|
||||
<h3 class="heading-3">{{ $t("Nächste Termine") }}</h3>
|
||||
<div
|
||||
v-for="dueDate in filteredDueDates"
|
||||
:key="dueDate.id"
|
||||
class="border-t border-gray-500 pt-2"
|
||||
>
|
||||
<DueDateSingle :due-date="dueDate"></DueDateSingle>
|
||||
</div>
|
||||
<div v-if="filteredDueDates.length === 0">
|
||||
{{ $t("dueDates.noDueDatesAvailable") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<router-link
|
||||
v-if="showAllButton"
|
||||
class="btn-secondary mt-4"
|
||||
:to="`/dashboard/due-dates?session=${courseSessionId}`"
|
||||
>
|
||||
{{ $t("a.Alle Termine anzeigen") }}
|
||||
</router-link>
|
||||
</template>
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
<script lang="ts" setup>
|
||||
import DueDateSingle from "@/components/dueDates/DueDateSingle.vue";
|
||||
import { computed } from "vue";
|
||||
import type { DashboardDueDate } from "@/services/dashboard";
|
||||
|
||||
const props = defineProps<{
|
||||
maxCount: number;
|
||||
dueDates: DashboardDueDate[];
|
||||
showTopBorder: boolean;
|
||||
showBottomBorder: boolean;
|
||||
showAllDueDatesLink: boolean;
|
||||
showCourseSession: boolean;
|
||||
}>();
|
||||
|
||||
const allDueDates = computed(() => {
|
||||
return props.dueDates;
|
||||
});
|
||||
|
||||
const dueDatesDisplayed = computed(() => {
|
||||
return props.dueDates.slice(0, props.maxCount);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<ul :class="showBottomBorder ? '' : 'no-border-last'">
|
||||
<li
|
||||
v-for="dueDate in dueDatesDisplayed"
|
||||
:key="dueDate.id"
|
||||
class="cy-single-due-date"
|
||||
:class="{ 'first:border-t': props.showTopBorder, 'border-b': true }"
|
||||
>
|
||||
<DueDateSingle
|
||||
:due-date="dueDate"
|
||||
:show-course-session="props.showCourseSession"
|
||||
></DueDateSingle>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="allDueDates.length === 0">{{ $t("dueDates.noDueDatesAvailable") }}</div>
|
||||
<div
|
||||
v-if="showAllDueDatesLink && allDueDates.length > 0"
|
||||
class="flex items-center pt-6"
|
||||
>
|
||||
<a href="/appointments">{{ $t("dueDates.showAllDueDates") }}</a>
|
||||
<it-icon-arrow-right />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.no-border-last li:last-child {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<DueDatesList
|
||||
:due-dates="allDueDates"
|
||||
:max-count="props.maxCount"
|
||||
:show-top-border="props.showTopBorder"
|
||||
show-all-due-dates-link
|
||||
show-bottom-border
|
||||
:show-course-session="false"
|
||||
></DueDatesList>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import DueDatesList from "@/components/dueDates/DueDatesList.vue";
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
|
||||
const props = defineProps<{
|
||||
maxCount: number;
|
||||
showTopBorder: boolean;
|
||||
}>();
|
||||
|
||||
const courseSession = useCurrentCourseSession();
|
||||
const allDueDates = courseSession.value.due_dates;
|
||||
</script>
|
||||
|
|
@ -60,9 +60,9 @@ const selectedCourseSessionTitle = computed(() => {
|
|||
const appointmentsUrl = computed(() => {
|
||||
const currentCourseSession = courseSessionsStore.currentCourseSession;
|
||||
if (currentCourseSession) {
|
||||
return `/course/dashboard/due-dates`;
|
||||
return `/dashboard/due-dates?session=${currentCourseSession.id}`;
|
||||
} else {
|
||||
return `/appointments`;
|
||||
return `/dashboard/due-dates`;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,179 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import { useTranslation } from "i18next-vue";
|
||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||
import type { DueDate } from "@/types";
|
||||
import DueDatesList from "@/components/dueDates/DueDatesList.vue";
|
||||
import { useCourseData } from "@/composables";
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const UNFILTERED = Number.MAX_SAFE_INTEGER.toString();
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
|
||||
type Item = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
type CourseItem = Item & {
|
||||
slug: string;
|
||||
};
|
||||
|
||||
const courses: CourseItem[] = courseSessionsStore.uniqueCourseSessionsByCourse.map(
|
||||
(cs) => ({
|
||||
id: cs.course.id,
|
||||
name: cs.course.title,
|
||||
slug: cs.course.slug,
|
||||
})
|
||||
);
|
||||
const selectedCourse = ref<CourseItem>(courses[0]);
|
||||
|
||||
const courseSessions = computed(() => {
|
||||
return [
|
||||
{
|
||||
id: UNFILTERED,
|
||||
name: t("a.AlleDurchführungen"),
|
||||
},
|
||||
...courseSessionsStore.allCourseSessions
|
||||
.filter((cs) => cs.course.id === selectedCourse.value.id)
|
||||
.map((cs) => ({ id: cs.id, name: cs.title })),
|
||||
];
|
||||
});
|
||||
const selectedSession = ref<Item>(courseSessions.value[0]);
|
||||
|
||||
// pre-select course and session if we are in a course session
|
||||
if (courseSessionsStore.currentCourseSession) {
|
||||
const session = courseSessionsStore.currentCourseSession;
|
||||
const { id: courseId, title: courseName, slug: courseSlug } = session.course;
|
||||
selectedCourse.value = { id: courseId, name: courseName, slug: courseSlug };
|
||||
const { id: sessionId, title: sessionName } = session;
|
||||
selectedSession.value = { id: sessionId, name: sessionName };
|
||||
}
|
||||
|
||||
const initialItemCircle: Item = {
|
||||
id: UNFILTERED,
|
||||
name: t("a.AlleCircle"),
|
||||
};
|
||||
const circles = ref<Item[]>([initialItemCircle]);
|
||||
const selectedCircle = ref<Item>(circles.value[0]);
|
||||
|
||||
async function loadCircleValues() {
|
||||
if (selectedCourse.value) {
|
||||
const learningPathQuery = useCourseData(selectedCourse.value.slug);
|
||||
await learningPathQuery.resultPromise;
|
||||
circles.value = [
|
||||
initialItemCircle,
|
||||
...(learningPathQuery.circles.value ?? []).map((circle) => ({
|
||||
id: circle.id,
|
||||
name: circle.title,
|
||||
})),
|
||||
];
|
||||
} else {
|
||||
circles.value = [initialItemCircle];
|
||||
}
|
||||
|
||||
selectedCircle.value = circles.value[0];
|
||||
}
|
||||
|
||||
watch(selectedCourse, async () => {
|
||||
selectedSession.value = courseSessions.value[0];
|
||||
await loadCircleValues();
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
await loadCircleValues();
|
||||
});
|
||||
|
||||
const appointments = computed(() => {
|
||||
return courseSessionsStore
|
||||
.allDueDates()
|
||||
.filter(
|
||||
(dueDate) =>
|
||||
isMatchingCourse(dueDate) &&
|
||||
isMatchingSession(dueDate) &&
|
||||
isMatchingCircle(dueDate)
|
||||
);
|
||||
});
|
||||
|
||||
const isMatchingSession = (dueDate: DueDate) =>
|
||||
selectedSession.value.id === UNFILTERED ||
|
||||
dueDate.course_session_id === selectedSession.value.id;
|
||||
|
||||
const isMatchingCircle = (dueDate: DueDate) =>
|
||||
selectedCircle.value.id === UNFILTERED ||
|
||||
dueDate.circle?.id === selectedCircle.value.id;
|
||||
|
||||
const isMatchingCourse = (dueDate: DueDate) =>
|
||||
courseSessions.value.map((cs) => cs.id).includes(dueDate.course_session_id);
|
||||
|
||||
const numAppointmentsToShow = ref(7);
|
||||
const canLoadMore = computed(() => {
|
||||
return numAppointmentsToShow.value < appointments.value.length;
|
||||
});
|
||||
|
||||
async function loadAdditionalAppointments() {
|
||||
numAppointmentsToShow.value *= 2;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-gray-200">
|
||||
<div class="container-large px-8 py-8">
|
||||
<header class="mb-6 flex flex-col lg:flex-row lg:items-center lg:justify-between">
|
||||
<h1>{{ $t("a.AlleTermine") }}</h1>
|
||||
<div>
|
||||
<ItDropdownSelect
|
||||
v-model="selectedCourse"
|
||||
data-cy="appointments-course-select"
|
||||
:items="courses"
|
||||
></ItDropdownSelect>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<div class="flex flex-col space-x-0 bg-white lg:flex-row lg:space-x-3">
|
||||
<ItDropdownSelect
|
||||
v-model="selectedSession"
|
||||
data-cy="appointments-session-select"
|
||||
:items="courseSessions"
|
||||
borderless
|
||||
></ItDropdownSelect>
|
||||
<ItDropdownSelect
|
||||
v-model="selectedCircle"
|
||||
data-cy="appointments-circle-select"
|
||||
:items="circles"
|
||||
borderless
|
||||
></ItDropdownSelect>
|
||||
</div>
|
||||
<div class="bg-white px-5">
|
||||
<DueDatesList
|
||||
:show-top-border="false"
|
||||
:show-bottom-border="canLoadMore"
|
||||
:due-dates="appointments"
|
||||
:show-all-due-dates-link="false"
|
||||
:max-count="numAppointmentsToShow"
|
||||
data-cy="appointments-list"
|
||||
:show-course-session="true"
|
||||
/>
|
||||
<button
|
||||
v-if="canLoadMore"
|
||||
class="py-4 underline"
|
||||
data-cy="load-more-notifications"
|
||||
@click="loadAdditionalAppointments()"
|
||||
>
|
||||
{{ $t("notifications.load_more") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.no-border-last li:last-child {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -41,8 +41,8 @@ const userStore = useUserStore();
|
|||
class="bg-white p-4 lg:p-8"
|
||||
@submit.prevent="
|
||||
userStore.handleLogin(
|
||||
state.username,
|
||||
state.password,
|
||||
state.username.trim(),
|
||||
state.password.trim(),
|
||||
route.query.next as string
|
||||
)
|
||||
"
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import { useCourseSessionDetailQuery, useCurrentCourseSession } from "@/composab
|
|||
import SubmissionsOverview from "@/components/cockpit/SubmissionsOverview.vue";
|
||||
import { useExpertCockpitStore } from "@/stores/expertCockpit";
|
||||
import log from "loglevel";
|
||||
import CockpitDates from "@/components/cockpit/CockpitDates.vue";
|
||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||
import UserStatusCount from "@/components/cockpit/UserStatusCount.vue";
|
||||
import { useExpertCockpitPageData } from "@/pages/cockpit/cockpitPage/composables";
|
||||
import CourseSessionDueDatesList from "@/components/dueDates/CourseSessionDueDatesList.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
|
|
@ -105,7 +105,11 @@ const courseSessionDetailResult = useCourseSessionDetailQuery();
|
|||
</div>
|
||||
|
||||
<div class="mb-4 bg-white p-6">
|
||||
<CockpitDates></CockpitDates>
|
||||
<CourseSessionDueDatesList
|
||||
:course-session-id="courseSession.id"
|
||||
:circle-id="expertCockpitStore.currentCircle.id"
|
||||
:max-count="4"
|
||||
></CourseSessionDueDatesList>
|
||||
</div>
|
||||
<SubmissionsOverview
|
||||
:course-session="courseSession"
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ const selectedSessionRouteQuery = useRouteQuery("session", UNFILTERED, {
|
|||
const selectedSession = ref<DropboxItem>(courseSessions.value[0]);
|
||||
|
||||
watch(selectedSession, () => {
|
||||
// @ts-ignore
|
||||
selectedSessionRouteQuery.value = selectedSession.value.id;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { onMounted } from "vue";
|
||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||
import { useDashboardStore } from "@/stores/dashboard";
|
||||
import type { DashboardCourseConfigType } from "@/services/dashboard";
|
||||
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
||||
|
|
@ -24,20 +23,11 @@ function newDashboardConfigForId(id: string): DashboardCourseConfigType | undefi
|
|||
<LoadingSpinner />
|
||||
</div>
|
||||
<div
|
||||
v-else-if="dashboardStore.currentDashboardConfig"
|
||||
v-else-if="dashboardStore.dashboardConfigsv2.length"
|
||||
class="flex flex-col lg:flex-row"
|
||||
>
|
||||
<main class="grow bg-gray-200 lg:order-2">
|
||||
<div class="m-8">
|
||||
<div class="mb-10 flex items-center justify-between">
|
||||
<h1 data-cy="dashboard-title">Dashboard</h1>
|
||||
<ItDropdownSelect
|
||||
:model-value="dashboardStore.currentDashboardConfig"
|
||||
class="mt-4 w-full lg:mt-0 lg:w-96"
|
||||
:items="dashboardStore.dashboardConfigs"
|
||||
@update:model-value="dashboardStore.switchAndLoadDashboardConfig"
|
||||
></ItDropdownSelect>
|
||||
</div>
|
||||
<!-- new way of dashboard -->
|
||||
<ul>
|
||||
<li
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
<script setup lang="ts">
|
||||
import DueDatesShortList from "@/components/dueDates/DueDatesShortList.vue";
|
||||
import LearningPathListView from "@/pages/learningPath/learningPathPage/LearningPathListView.vue";
|
||||
import LearningPathPathView from "@/pages/learningPath/learningPathPage/LearningPathPathView.vue";
|
||||
import CircleProgress from "@/pages/learningPath/learningPathPage/LearningPathProgress.vue";
|
||||
|
|
@ -8,7 +7,12 @@ import type { ViewType } from "@/pages/learningPath/learningPathPage/LearningPat
|
|||
import LearningPathViewSwitch from "@/pages/learningPath/learningPathPage/LearningPathViewSwitch.vue";
|
||||
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core";
|
||||
import { computed, ref } from "vue";
|
||||
import { useCourseCircleProgress, useCourseDataWithCompletion } from "@/composables";
|
||||
import {
|
||||
useCourseCircleProgress,
|
||||
useCourseDataWithCompletion,
|
||||
useCurrentCourseSession,
|
||||
} from "@/composables";
|
||||
import CourseSessionDueDatesList from "@/components/dueDates/CourseSessionDueDatesList.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
|
|
@ -27,6 +31,8 @@ const lpQueryResult = useCourseDataWithCompletion(props.courseSlug);
|
|||
const learningPath = computed(() => lpQueryResult.learningPath.value);
|
||||
const course = computed(() => lpQueryResult.course.value);
|
||||
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
const { inProgressCirclesCount, circlesCount } = useCourseCircleProgress(
|
||||
lpQueryResult.circles
|
||||
);
|
||||
|
|
@ -60,10 +66,10 @@ const changeViewType = (viewType: ViewType) => {
|
|||
|
||||
<!-- Right -->
|
||||
<div v-if="!useMobileLayout" class="flex-grow">
|
||||
<div class="text-bold pb-3">
|
||||
{{ $t("a.Nächste Termine") }}
|
||||
</div>
|
||||
<DueDatesShortList :max-count="2" :show-top-border="true"></DueDatesShortList>
|
||||
<CourseSessionDueDatesList
|
||||
:course-session-id="courseSession.id"
|
||||
:max-count="2"
|
||||
></CourseSessionDueDatesList>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,18 +7,15 @@ import type {
|
|||
} from "vue-router";
|
||||
|
||||
const routeHistory: RouteLocationNormalized[] = [];
|
||||
const MAX_HISTORY = 10; // for example, store the last 10 visited routes
|
||||
const MAX_HISTORY = 10;
|
||||
let isFirstNavigation = true;
|
||||
|
||||
// Variable to track the type of the last navigation
|
||||
let lastNavigationWasPush = false;
|
||||
|
||||
// Function to set the state
|
||||
export function setLastNavigationWasPush(value: boolean) {
|
||||
lastNavigationWasPush = value;
|
||||
}
|
||||
|
||||
// Function to get the current state
|
||||
export function getLastNavigationWasPush() {
|
||||
return lastNavigationWasPush;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -324,14 +324,6 @@ const router = createRouter({
|
|||
path: "/notifications",
|
||||
component: () => import("@/pages/NotificationsPage.vue"),
|
||||
},
|
||||
{
|
||||
path: "/appointments",
|
||||
component: () => import("@/pages/AppointmentsPage.vue"),
|
||||
},
|
||||
{
|
||||
path: "/course/:courseSlug/appointments",
|
||||
component: () => import("@/pages/AppointmentsPage.vue"),
|
||||
},
|
||||
{
|
||||
path: "/onboarding/:courseType",
|
||||
props: true,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { itGetCached } from "@/fetchHelpers";
|
||||
import type { CourseSession, DueDate } from "@/types";
|
||||
import type { CourseSession } from "@/types";
|
||||
import eventBus from "@/utils/eventBus";
|
||||
import { useRouteLookups } from "@/utils/route";
|
||||
import { useLocalStorage } from "@vueuse/core";
|
||||
import dayjs from "dayjs";
|
||||
import uniqBy from "lodash/uniqBy";
|
||||
import log from "loglevel";
|
||||
import { defineStore } from "pinia";
|
||||
|
|
@ -25,13 +24,6 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
|||
|
||||
const userStore = useUserStore();
|
||||
if (userStore.loggedIn) {
|
||||
// TODO: refactor after implementing of Klassenkonzept
|
||||
await Promise.all(
|
||||
allCourseSessions.value.map(async (cs) => {
|
||||
sortDueDates(cs.due_dates);
|
||||
})
|
||||
);
|
||||
|
||||
if (!allCourseSessions.value) {
|
||||
throw `No courseSessionData found for user`;
|
||||
}
|
||||
|
|
@ -137,37 +129,12 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
|||
return Boolean(hasPreview && (inLearningPath() || inCompetenceProfile()));
|
||||
});
|
||||
|
||||
function allDueDates() {
|
||||
const allDueDatesReturn: DueDate[] = [];
|
||||
|
||||
allCourseSessions.value?.forEach((cs) => {
|
||||
allDueDatesReturn.push(...cs.due_dates);
|
||||
});
|
||||
|
||||
sortDueDates(allDueDatesReturn);
|
||||
return allDueDatesReturn;
|
||||
}
|
||||
|
||||
function sortDueDates(dueDates: DueDate[]) {
|
||||
dueDates.sort((a, b) => {
|
||||
const dateA = dayjs(a.start);
|
||||
const dateB = dayjs(b.start);
|
||||
|
||||
if (!dateA.isValid() && !dateB.isValid()) return 0; // If both are invalid, they are equal
|
||||
if (!dateA.isValid()) return 1; // If dateA is invalid, it goes after dateB
|
||||
if (!dateB.isValid()) return -1; // If dateB is invalid, it goes after dateA
|
||||
|
||||
return dateA.diff(dateB); // sort by `start`
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
uniqueCourseSessionsByCourse,
|
||||
allCurrentCourseSessions,
|
||||
getCourseSessionById,
|
||||
switchCourseSessionById,
|
||||
isCourseSessionPreviewActive,
|
||||
allDueDates,
|
||||
|
||||
// use `useCurrentCourseSession` whenever possible
|
||||
currentCourseSession,
|
||||
|
|
|
|||
|
|
@ -62,16 +62,16 @@ export const useDashboardStore = defineStore("dashboard", () => {
|
|||
dashboardConfigsv2.value = await fetchDashboardConfigv2();
|
||||
console.log("got dashboard config v2: ", dashboardConfigsv2.value);
|
||||
try {
|
||||
if (!currentDashboardConfig.value) {
|
||||
await loadDashboardConfig();
|
||||
return;
|
||||
}
|
||||
const { id, dashboard_type } = currentDashboardConfig.value;
|
||||
if (dashBoardDataCache[id]) {
|
||||
currentDashBoardData.value = dashBoardDataCache[id];
|
||||
return;
|
||||
}
|
||||
// await loadDashboardData(dashboard_type, id);
|
||||
// if (!currentDashboardConfig.value) {
|
||||
// await loadDashboardConfig();
|
||||
// return;
|
||||
// }
|
||||
// const { id, dashboard_type } = currentDashboardConfig.value;
|
||||
// if (dashBoardDataCache[id]) {
|
||||
// currentDashBoardData.value = dashBoardDataCache[id];
|
||||
// return;
|
||||
// }
|
||||
// // await loadDashboardData(dashboard_type, id);
|
||||
} finally {
|
||||
console.log("done loading dashboard details");
|
||||
loading.value = false;
|
||||
|
|
|
|||
|
|
@ -452,7 +452,6 @@ export interface CourseSession {
|
|||
title: string;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
due_dates: DueDate[];
|
||||
actions: string[];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
from django.db.models import Q
|
||||
from rest_framework import serializers
|
||||
|
||||
from vbv_lernwelt.core.utils import StringIDField
|
||||
|
|
@ -10,8 +9,6 @@ from vbv_lernwelt.course.models import (
|
|||
CourseConfiguration,
|
||||
CourseSession,
|
||||
)
|
||||
from vbv_lernwelt.duedate.models import DueDate
|
||||
from vbv_lernwelt.duedate.serializers import DueDateSerializer
|
||||
from vbv_lernwelt.iam.permissions import course_session_permissions
|
||||
|
||||
|
||||
|
|
@ -72,19 +69,12 @@ class CourseSessionSerializer(serializers.ModelSerializer):
|
|||
id = StringIDField()
|
||||
|
||||
course = serializers.SerializerMethodField()
|
||||
due_dates = serializers.SerializerMethodField()
|
||||
actions = serializers.SerializerMethodField()
|
||||
user_roles = serializers.SerializerMethodField()
|
||||
|
||||
def get_course(self, obj):
|
||||
return CourseSerializer(obj.course).data
|
||||
|
||||
def get_due_dates(self, obj):
|
||||
due_dates = DueDate.objects.filter(
|
||||
Q(start__isnull=False) | Q(end__isnull=False), course_session_id=obj.id
|
||||
)
|
||||
return DueDateSerializer(due_dates, many=True).data
|
||||
|
||||
def get_user_roles(self, obj):
|
||||
if hasattr(obj, "roles"):
|
||||
return list(obj.roles)
|
||||
|
|
@ -100,7 +90,6 @@ class CourseSessionSerializer(serializers.ModelSerializer):
|
|||
"title",
|
||||
"start_date",
|
||||
"end_date",
|
||||
"due_dates",
|
||||
"actions",
|
||||
"user_roles",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import json
|
||||
from datetime import datetime
|
||||
|
||||
from django.utils import timezone
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from vbv_lernwelt.core.create_default_users import create_default_users
|
||||
|
|
@ -9,9 +7,6 @@ from vbv_lernwelt.core.models import User
|
|||
from vbv_lernwelt.course.consts import COURSE_TEST_ID
|
||||
from vbv_lernwelt.course.creators.test_course import create_test_course
|
||||
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||
from vbv_lernwelt.course_session.models import CourseSessionAssignment
|
||||
from vbv_lernwelt.duedate.factories import DueDateFactory
|
||||
from vbv_lernwelt.learnpath.models import LearningContentAssignment
|
||||
|
||||
|
||||
class CourseCompletionApiTestCase(APITestCase):
|
||||
|
|
@ -56,41 +51,3 @@ class CourseCompletionApiTestCase(APITestCase):
|
|||
|
||||
print(json.dumps(response.json(), indent=4))
|
||||
self.assertEqual(response.json()[0]["id"], str(self.course_session.id))
|
||||
|
||||
def test_api_hasNoDueDates(self):
|
||||
self.client.login(username="admin", password="test")
|
||||
response = self.client.get(f"/api/course/sessions/")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.json()), 1)
|
||||
|
||||
print(json.dumps(response.json(), indent=4))
|
||||
self.assertEqual(response.json()[0]["due_dates"], [])
|
||||
|
||||
def test_api_hasDueDates(self):
|
||||
cs = CourseSession.objects.first()
|
||||
|
||||
due_date = DueDateFactory(
|
||||
start=timezone.make_aware(datetime.now()),
|
||||
course_session=cs,
|
||||
title="Test Due Date",
|
||||
)
|
||||
csa = CourseSessionAssignment.objects.create(
|
||||
course_session=cs,
|
||||
learning_content=LearningContentAssignment.objects.get(
|
||||
slug=f"test-lehrgang-lp-circle-reisen-lc-mein-kundenstamm"
|
||||
),
|
||||
)
|
||||
|
||||
csa.submission_deadline = due_date
|
||||
|
||||
csa.save()
|
||||
self.client.login(username="admin", password="test")
|
||||
response = self.client.get(f"/api/course/sessions/")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
print(json.dumps(response.json(), indent=4))
|
||||
self.assertEqual(len(response.json()), 1)
|
||||
|
||||
self.assertEqual(len(response.json()[0]["due_dates"]), 1)
|
||||
self.assertEqual(response.json()[0]["due_dates"][0]["title"], "Test Due Date")
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ class DashboardQuery(graphene.ObjectType):
|
|||
|
||||
course_session_ids = set()
|
||||
|
||||
# supervisors
|
||||
for group in CourseSessionGroup.objects.filter(course=course):
|
||||
if can_view_course_session_group_statistics(user=user, group=group):
|
||||
course_session_ids.update(
|
||||
|
|
|
|||
Loading…
Reference in New Issue