WIP: Adding courseId to statistics page

This commit is contained in:
Christian Cueni 2024-04-18 15:43:19 +02:00
parent 1e5db1f0f7
commit b58bceb14d
13 changed files with 169 additions and 61 deletions

View File

@ -6,6 +6,7 @@ import BaseBox from "@/components/dashboard/BaseBox.vue";
const props = defineProps<{ const props = defineProps<{
assignmentsCompleted: number; assignmentsCompleted: number;
avgPassed: number; avgPassed: number;
courseSlug: string;
}>(); }>();
const progress = computed(() => { const progress = computed(() => {
@ -19,7 +20,7 @@ const progress = computed(() => {
<template> <template>
<BaseBox <BaseBox
:details-link="'/statistic/assignment'" :details-link="`/statistic/${courseSlug}/assignment`"
data-cy="dashboard.stats.assignments" data-cy="dashboard.stats.assignments"
> >
<template #title>{{ $t("a.Kompetenznachweis-Elemente") }}</template> <template #title>{{ $t("a.Kompetenznachweis-Elemente") }}</template>

View File

@ -6,6 +6,7 @@ import BaseBox from "@/components/dashboard/BaseBox.vue";
const props = defineProps<{ const props = defineProps<{
daysCompleted: number; daysCompleted: number;
avgParticipantsPresent: number; avgParticipantsPresent: number;
courseSlug: string;
}>(); }>();
const progressRecord = computed(() => { const progressRecord = computed(() => {
@ -18,7 +19,10 @@ const progressRecord = computed(() => {
</script> </script>
<template> <template>
<BaseBox :details-link="'/statistic/attendance'" data-cy="dashboard.stats.attendance"> <BaseBox
:details-link="`/statistic/${props.courseSlug}/attendance`"
data-cy="dashboard.stats.attendance"
>
<template #title>{{ $t("a.Anwesenheit") }}</template> <template #title>{{ $t("a.Anwesenheit") }}</template>
<template #content> <template #content>
<div class="flex items-center"> <div class="flex items-center">

View File

@ -146,7 +146,7 @@ function hasActionButton(): boolean {
v-if="hasWidget('UKStatisticsWidget')" v-if="hasWidget('UKStatisticsWidget')"
class="flex flex-col flex-wrap gap-x-[60px] border-b border-gray-300 pb-8 last:border-0 md:flex-row" class="flex flex-col flex-wrap gap-x-[60px] border-b border-gray-300 pb-8 last:border-0 md:flex-row"
> >
<UkStatistics :course-id="courseConfig.course_id" /> <UkStatistics :course-slug="courseSlug" :course-id="courseConfig.course_id" />
</div> </div>
<div v-if="numberOfMentorWidgets > 0" class="flex flex-col flex-wrap md:flex-row"> <div v-if="numberOfMentorWidgets > 0" class="flex flex-col flex-wrap md:flex-row">
<MentorOpenTasksCount <MentorOpenTasksCount

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { useDashboardStore } from "@/stores/dashboard"; import { useDashboardStore } from "@/stores/dashboard";
import { computed, onMounted } from "vue"; import { computed, onMounted, ref } from "vue";
import AttendanceSummaryBox from "@/components/dashboard/AttendanceSummaryBox.vue"; import AttendanceSummaryBox from "@/components/dashboard/AttendanceSummaryBox.vue";
import type { CourseStatisticsType } from "@/gql/graphql"; import type { CourseStatisticsType } from "@/gql/graphql";
import AssignmentSummaryBox from "@/components/dashboard/AssignmentSummaryBox.vue"; import AssignmentSummaryBox from "@/components/dashboard/AssignmentSummaryBox.vue";
@ -9,12 +9,12 @@ import CompetenceSummaryBox from "@/components/dashboard/CompetenceSummaryBox.vu
const props = defineProps<{ const props = defineProps<{
courseId: string; courseId: string;
courseSlug: string;
}>(); }>();
const statistics = ref<CourseStatisticsType | null>(null);
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const statistics = computed(() => {
return dashboardStore.currentDashBoardData as CourseStatisticsType;
});
const attendanceDayPresences = computed(() => { const attendanceDayPresences = computed(() => {
return statistics.value.attendance_day_presences.summary; return statistics.value.attendance_day_presences.summary;
@ -33,7 +33,8 @@ const feebackSummary = computed(() => {
}); });
onMounted(async () => { onMounted(async () => {
await dashboardStore.loadStatisticsData(props.courseId); statistics.value = await dashboardStore.loadStatisticsDatav2(props.courseId);
//await dashboardStore.loadStatisticsData(props.courseId);
}); });
</script> </script>
@ -46,11 +47,13 @@ onMounted(async () => {
class="flex-grow" class="flex-grow"
:days-completed="attendanceDayPresences.days_completed" :days-completed="attendanceDayPresences.days_completed"
:avg-participants-present="attendanceDayPresences.participants_present" :avg-participants-present="attendanceDayPresences.participants_present"
:course-slug="props.courseSlug"
/> />
<AssignmentSummaryBox <AssignmentSummaryBox
class="flex-grow" class="flex-grow"
:assignments-completed="assigmentSummary.completed_count" :assignments-completed="assigmentSummary.completed_count"
:avg-passed="assigmentSummary.average_passed" :avg-passed="assigmentSummary.average_passed"
:course-slug="props.courseSlug"
/> />
</div> </div>
<div <div
@ -60,11 +63,13 @@ onMounted(async () => {
:feedback-count="feebackSummary.total_responses" :feedback-count="feebackSummary.total_responses"
:statisfaction-max="feebackSummary.satisfaction_max" :statisfaction-max="feebackSummary.satisfaction_max"
:statisfaction-avg="feebackSummary.satisfaction_average" :statisfaction-avg="feebackSummary.satisfaction_average"
:course-slug="props.courseSlug"
/> />
<CompetenceSummaryBox <CompetenceSummaryBox
:fail-count="competenceSummary.fail_total" :fail-count="competenceSummary.fail_total"
:success-count="competenceSummary.success_total" :success-count="competenceSummary.success_total"
details-link="/statistic/competence" :details-link="`/statistic/${courseSlug}/competence`"
:course-slug="props.courseSlug"
/> />
</div> </div>
</div> </div>

View File

@ -9,7 +9,7 @@ import {
someFinishedInLearningSequence, someFinishedInLearningSequence,
} from "@/services/circle"; } from "@/services/circle";
import type { DashboardPersonType } from "@/services/dashboard"; import type { DashboardPersonType } from "@/services/dashboard";
import { fetchDashboardPersons } from "@/services/dashboard"; import { fetchDashboardPersons, fetchStatisticData } from "@/services/dashboard";
import { presignUpload, uploadFile } from "@/services/files"; import { presignUpload, uploadFile } from "@/services/files";
import { useCompletionStore } from "@/stores/completion"; import { useCompletionStore } from "@/stores/completion";
import { useCourseSessionsStore } from "@/stores/courseSessions"; import { useCourseSessionsStore } from "@/stores/courseSessions";
@ -573,3 +573,24 @@ export function useCourseCircleProgress(circles: Ref<CircleType[] | undefined>)
return { inProgressCirclesCount, circlesCount }; return { inProgressCirclesCount, circlesCount };
} }
export function useCourseStatisticsv2(courseId: string) {
const courseStatistics = ref<CourseStatisticsType[] | null>(null);
const loading = ref(false);
const fetchData = async () => {
loading.value = true;
try {
courseStatistics.value = await fetchStatisticData(courseId);
} finally {
loading.value = false;
}
};
onMounted(fetchData);
return {
courseStatistics,
loading,
};
}

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Component, Ref } from "vue"; import type { Component } from "vue";
import { onMounted, ref } from "vue"; import { onMounted } from "vue";
import StatisticPage from "@/pages/dashboard/StatisticPage.vue"; import StatisticPage from "@/pages/dashboard/StatisticPage.vue";
import ProgressPage from "@/pages/dashboard/ProgressPage.vue"; import ProgressPage from "@/pages/dashboard/ProgressPage.vue";
import SimpleDates from "@/components/dashboard/SimpleDates.vue"; import SimpleDates from "@/components/dashboard/SimpleDates.vue";
@ -8,7 +8,6 @@ import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
import { useDashboardStore } from "@/stores/dashboard"; import { useDashboardStore } from "@/stores/dashboard";
import type { DashboardType } from "@/gql/graphql"; import type { DashboardType } from "@/gql/graphql";
import type { DashboardCourseConfigType } from "@/services/dashboard"; import type { DashboardCourseConfigType } from "@/services/dashboard";
import { fetchDashboardConfigv2 } from "@/services/dashboard";
import SimpleCoursePage from "@/pages/dashboard/SimpleCoursePage.vue"; import SimpleCoursePage from "@/pages/dashboard/SimpleCoursePage.vue";
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue"; import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
import CourseDetailDates from "@/components/dashboard/CourseDetailDates.vue"; import CourseDetailDates from "@/components/dashboard/CourseDetailDates.vue";
@ -31,15 +30,12 @@ const boards: Record<DashboardType, DashboardPage> = {
PRAXISBILDNER_DASHBOARD: { main: CoursePanel, aside: SimpleDates }, PRAXISBILDNER_DASHBOARD: { main: CoursePanel, aside: SimpleDates },
}; };
const dashboardConfigv2: Ref<DashboardCourseConfigType[]> = ref([]);
onMounted(async () => { onMounted(async () => {
dashboardConfigv2.value = await fetchDashboardConfigv2();
await dashboardStore.loadDashboardDetails(); await dashboardStore.loadDashboardDetails();
}); });
function newDashboardConfigForId(id: string): DashboardCourseConfigType | undefined { function newDashboardConfigForId(id: string): DashboardCourseConfigType | undefined {
return dashboardConfigv2.value.find((config) => config.course_id == id); return dashboardStore.dashboardConfigsv2.find((config) => config.course_id == id);
} }
</script> </script>
@ -64,8 +60,11 @@ function newDashboardConfigForId(id: string): DashboardCourseConfigType | undefi
</div> </div>
<!-- new way of dashboard --> <!-- new way of dashboard -->
<ul> <ul>
<li v-for="config in dashboardStore.dashboardConfigs" :key="config.id"> <li
<CoursePanel :course-config="newDashboardConfigForId(config.id)" /> v-for="config in dashboardStore.dashboardConfigsv2"
:key="config.course_id"
>
<CoursePanel :course-config="newDashboardConfigForId(config.course_id)" />
</li> </li>
</ul> </ul>
<!-- end of new way of dashboard --> <!-- end of new way of dashboard -->

View File

@ -1,23 +1,39 @@
<script setup lang="ts"> <script setup lang="ts">
import { useDashboardStore } from "@/stores/dashboard"; import { useDashboardStore } from "@/stores/dashboard";
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue"; import { onMounted, ref } from "vue";
import { computed } from "vue";
import type { import type {
AssignmentCompletionMetricsType, AssignmentCompletionMetricsType,
AssignmentStatisticsRecordType, AssignmentStatisticsRecordType,
CourseStatisticsType, CourseStatisticsType,
} from "@/gql/graphql"; } from "@/gql/graphql";
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue"; import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
import { useCourseStatistics } from "@/composables"; import { useCourseStatistics, useCourseStatisticsv2 } from "@/composables";
import ItProgress from "@/components/ui/ItProgress.vue";
import { getDateString } from "@/components/dueDates/dueDatesUtils"; import { getDateString } from "@/components/dueDates/dueDatesUtils";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { courseIdForCourseSlug } from "@/services/dashboard";
import ItProgress from "@/components/ui/ItProgress.vue";
const props = defineProps<{
courseSlug: string;
}>();
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const statistics = computed(() => { const statistics = ref<CourseStatisticsType | null>(null);
return dashboardStore.currentDashBoardData as CourseStatisticsType; // const statistics = reactive<CourseStatisticsType>({
}); // course_session_properties: {
// sessions: [],
// generations: [],
// circles: [],
// },
// assignments: { records: [] },
// });
const courseId = courseIdForCourseSlug(
dashboardStore.dashboardConfigsv2,
props.courseSlug
);
const { courseStatistics, loading } = useCourseStatisticsv2(courseId);
const { courseSessionName, circleMeta } = useCourseStatistics(); const { courseSessionName, circleMeta } = useCourseStatistics();
@ -40,23 +56,34 @@ const assignmentStats = (metrics: AssignmentCompletionMetricsType) => {
const total = (metrics: AssignmentCompletionMetricsType) => { const total = (metrics: AssignmentCompletionMetricsType) => {
return metrics.passed_count + metrics.failed_count + metrics.unranked_count; return metrics.passed_count + metrics.failed_count + metrics.unranked_count;
}; };
onMounted(async () => {
console.log("onMount AssignmentList", courseId, props.courseSlug, statistics);
if (!courseId) {
return;
}
// statistics.value = await dashboardStore.loadStatisticsDatav2(courseId);
// const some = await courseStatistics.loadStatisticsDatav2(courseId);
// console.log("some", some.course_session_properties);
// statistics.value = some;
// console.log("other", statistics.value.course_session_properties);
// statistics.course_session_properties = some.course_session_properties;
// statistics.assignments = some.assignments;
// statistics.console.log(statistics.value);
});
</script> </script>
<template> <template>
<main v-if="statistics"> {{ loading }}
<main v-if="!loading">
<div class="mb-10 flex items-center justify-between"> <div class="mb-10 flex items-center justify-between">
<h3>{{ $t("a.Kompetenznachweis-Elemente") }}</h3> <h3>{{ $t("a.Kompetenznachweis-Elemente") }}</h3>
<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> </div>
<div v-if="statistics.assignments.records" class="mt-8 bg-white"> <div v-if="courseStatistics?.assignments.records" class="mt-8 bg-white">
<StatisticFilterList <StatisticFilterList
:course-session-properties="statistics.course_session_properties" v-if="courseStatistics?.course_session_properties"
:items="statistics.assignments.records" :course-session-properties="courseStatistics.course_session_properties"
:items="courseStatistics.assignments.records"
> >
<template #default="{ item }"> <template #default="{ item }">
<div class="flex justify-between"> <div class="flex justify-between">

View File

@ -1,19 +1,26 @@
<script setup lang="ts"> <script setup lang="ts">
import { useDashboardStore } from "@/stores/dashboard"; import { useDashboardStore } from "@/stores/dashboard";
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue"; import { onMounted, ref } from "vue";
import { computed } from "vue";
import type { CourseStatisticsType, PresenceRecordStatisticsType } from "@/gql/graphql"; import type { CourseStatisticsType, PresenceRecordStatisticsType } from "@/gql/graphql";
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue"; import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
import ItProgress from "@/components/ui/ItProgress.vue"; import ItProgress from "@/components/ui/ItProgress.vue";
import { useCourseStatistics } from "@/composables"; import { useCourseStatistics } from "@/composables";
import { getDateString } from "@/components/dueDates/dueDatesUtils"; import { getDateString } from "@/components/dueDates/dueDatesUtils";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { courseIdForCourseSlug } from "@/services/dashboard";
const props = defineProps<{
courseSlug: string;
}>();
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const statistics = computed(() => { const statistics = ref<CourseStatisticsType | null>(null);
return dashboardStore.currentDashBoardData as CourseStatisticsType;
}); const courseId = courseIdForCourseSlug(
dashboardStore.dashboardConfigsv2,
props.courseSlug
);
const { courseSessionName, circleMeta } = useCourseStatistics(); const { courseSessionName, circleMeta } = useCourseStatistics();
@ -24,18 +31,17 @@ const attendanceStats = (present: number, total: number) => {
UNKNOWN: 0, UNKNOWN: 0,
}; };
}; };
onMounted(async () => {
console.log("onMount AttendanceList", courseId, props.courseSlug, statistics);
statistics.value = await dashboardStore.loadStatisticsDatav2(courseId);
});
</script> </script>
<template> <template>
<main v-if="statistics"> <main v-if="statistics">
<div class="mb-10 flex items-center justify-between"> <div class="mb-10 flex items-center justify-between">
<h3>{{ $t("Anwesenheit") }}</h3> <h3>{{ $t("Anwesenheit") }}</h3>
<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> </div>
<div v-if="statistics.attendance_day_presences.records" class="mt-8 bg-white"> <div v-if="statistics.attendance_day_presences.records" class="mt-8 bg-white">
<StatisticFilterList <StatisticFilterList

View File

@ -1,21 +1,30 @@
<script setup lang="ts"> <script setup lang="ts">
import { useDashboardStore } from "@/stores/dashboard"; import { useDashboardStore } from "@/stores/dashboard";
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue"; import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
import { computed } from "vue"; import { onMounted, ref } from "vue";
import type { import type { CompetenceRecordStatisticsType } from "@/gql/graphql";
CompetenceRecordStatisticsType, import { CourseStatisticsType } from "@/gql/graphql";
CourseStatisticsType,
} from "@/gql/graphql";
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue"; import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
import { useCourseStatistics } from "@/composables"; import { useCourseStatistics } from "@/composables";
import { courseIdForCourseSlug } from "@/services/dashboard";
const props = defineProps<{
courseSlug: string;
}>();
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const statistics = ref<CourseStatisticsType | null>(null);
const statistics = computed(() => { const courseId = courseIdForCourseSlug(
return dashboardStore.currentDashBoardData as CourseStatisticsType; dashboardStore.dashboardConfigsv2,
}); props.courseSlug
);
const { courseSessionName, circleMeta } = useCourseStatistics(); const { courseSessionName, circleMeta } = useCourseStatistics();
onMounted(async () => {
statistics.value = await dashboardStore.loadStatisticsDatav2(courseId);
});
</script> </script>
<template> <template>

View File

@ -1,10 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import { useDashboardStore } from "@/stores/dashboard"; import { useDashboardStore } from "@/stores/dashboard";
import { onMounted } from "vue"; import { computed, onMounted, watch } from "vue";
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue"; import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
onMounted(dashboardStore.loadDashboardDetails); dashboardStore.loading = true;
const some = computed(() => {
return dashboardStore.loading;
});
watch(some, () => {
console.log("watch loading", dashboardStore.loading);
});
onMounted(async () => {
console.log("onMount StatisticParentPage", dashboardStore.loading);
await dashboardStore.loadDashboardDetails();
});
</script> </script>
<template> <template>

View File

@ -269,7 +269,7 @@ const router = createRouter({
], ],
}, },
{ {
path: "/statistic", path: "/statistic/:courseSlug",
props: true, props: true,
component: () => import("@/pages/dashboard/statistic/StatisticParentPage.vue"), component: () => import("@/pages/dashboard/statistic/StatisticParentPage.vue"),
children: [ children: [

View File

@ -159,3 +159,11 @@ export async function fetchOpenTasksCount(courseId: string) {
`/api/dashboard/course/${courseId}/open_tasks/` `/api/dashboard/course/${courseId}/open_tasks/`
); );
} }
export function courseIdForCourseSlug(
dashboardConfigs: DashboardCourseConfigType[],
courseSlug: string
) {
const config = dashboardConfigs.find((config) => config.course_slug === courseSlug);
return config?.course_id;
}

View File

@ -4,8 +4,10 @@ import type {
DashboardConfigType, DashboardConfigType,
DashboardType, DashboardType,
} from "@/gql/graphql"; } from "@/gql/graphql";
import type { DashboardCourseConfigType } from "@/services/dashboard";
import { import {
fetchDashboardConfig, fetchDashboardConfig,
fetchDashboardConfigv2,
fetchProgressData, fetchProgressData,
fetchStatisticData, fetchStatisticData,
} from "@/services/dashboard"; } from "@/services/dashboard";
@ -15,6 +17,7 @@ import { ref } from "vue";
export const useDashboardStore = defineStore("dashboard", () => { export const useDashboardStore = defineStore("dashboard", () => {
const dashboardConfigs: Ref<DashboardConfigType[]> = ref([]); const dashboardConfigs: Ref<DashboardConfigType[]> = ref([]);
const dashboardConfigsv2: Ref<DashboardCourseConfigType[]> = ref([]);
const currentDashboardConfig: Ref<DashboardConfigType | undefined> = ref(); const currentDashboardConfig: Ref<DashboardConfigType | undefined> = ref();
const dashBoardDataCache: Record< const dashBoardDataCache: Record<
string, string,
@ -56,6 +59,8 @@ export const useDashboardStore = defineStore("dashboard", () => {
const loadDashboardDetails = async () => { const loadDashboardDetails = async () => {
loading.value = true; loading.value = true;
dashboardConfigsv2.value = await fetchDashboardConfigv2();
console.log("got dashboard config v2: ", dashboardConfigsv2.value);
try { try {
if (!currentDashboardConfig.value) { if (!currentDashboardConfig.value) {
await loadDashboardConfig(); await loadDashboardConfig();
@ -66,8 +71,9 @@ export const useDashboardStore = defineStore("dashboard", () => {
currentDashBoardData.value = dashBoardDataCache[id]; currentDashBoardData.value = dashBoardDataCache[id];
return; return;
} }
await loadDashboardData(dashboard_type, id); // await loadDashboardData(dashboard_type, id);
} finally { } finally {
console.log("done loading dashboard details");
loading.value = false; loading.value = false;
} }
}; };
@ -78,8 +84,16 @@ export const useDashboardStore = defineStore("dashboard", () => {
currentDashBoardData.value = data; currentDashBoardData.value = data;
}; };
const loadStatisticsDatav2 = async (id: string) => {
console.log("fetching statistics v2 for course ID: ", id);
const data = await fetchStatisticData(id);
dashBoardDataCache[id] = data;
return data;
};
return { return {
dashboardConfigs, dashboardConfigs,
dashboardConfigsv2,
currentDashboardConfig, currentDashboardConfig,
switchAndLoadDashboardConfig, switchAndLoadDashboardConfig,
loadDashboardConfig, loadDashboardConfig,
@ -87,5 +101,6 @@ export const useDashboardStore = defineStore("dashboard", () => {
currentDashBoardData, currentDashBoardData,
loading, loading,
loadStatisticsData, loadStatisticsData,
loadStatisticsDatav2,
}; };
}); });