feat: add feedback list

This commit is contained in:
Reto Aebersold 2023-10-30 11:50:46 +01:00
parent 62c3aaf849
commit e2a346caed
8 changed files with 130 additions and 28 deletions

View File

@ -1,8 +1,11 @@
<script setup lang="ts">
import { computed, ref, watch } from "vue";
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
import { useTranslation } from "i18next-vue";
import type { StatisticsCourseSessionPropertiesType } from "@/gql/graphql";
const { t } = useTranslation();
interface Item {
_id: string;
course_session_id: string;
@ -17,18 +20,18 @@ const props = defineProps<{
const sessionFilter = computed(() => {
const f = props.courseSessionProperties.sessions.map((session) => ({
name: `Durchführung: ${session.name}`,
name: `${t("a.Durchfuehrung")}: ${session.name}`,
id: session.id,
}));
return [{ name: "Durchführung: Alle", id: "_all" }, ...f];
return [{ name: t("a.AlleDurchführungen"), id: "_all" }, ...f];
});
const generationFilter = computed(() => {
const f = props.courseSessionProperties.generations.map((generation) => ({
name: `Generation: ${generation}`,
name: `${t("a.Generation")}: ${generation}`,
id: generation,
}));
return [{ name: "Generation: Alle", id: "_all" }, ...f];
return [{ name: t("a.AlleGenerationen"), id: "_all" }, ...f];
});
const circleFilter = computed(() => {
@ -36,7 +39,7 @@ const circleFilter = computed(() => {
name: `Circle: ${circle.name}`,
id: circle.id,
}));
return [{ name: "Circle: Alle", id: "_all" }, ...f];
return [{ name: t("a.AlleCircle"), id: "_all" }, ...f];
});
const sessionFilterValue = ref(sessionFilter.value[0]);

View File

@ -1,3 +1,4 @@
import type { CourseStatisticsType } from "@/gql/graphql";
import { graphqlClient } from "@/graphql/client";
import { COURSE_QUERY, COURSE_SESSION_DETAIL_QUERY } from "@/graphql/queries";
import {
@ -7,6 +8,7 @@ import {
} from "@/services/circle";
import { useCompletionStore } from "@/stores/completion";
import { useCourseSessionsStore } from "@/stores/courseSessions";
import { useDashboardStore } from "@/stores/dashboard";
import { useUserStore } from "@/stores/user";
import type {
ActionCompetence,
@ -411,3 +413,25 @@ export function useCourseDataWithCompletion(
nextLearningContent,
};
}
export function useCourseStatistics() {
const dashboardStore = useDashboardStore();
const statistics = computed(() => {
return dashboardStore.currentDashBoardData as CourseStatisticsType;
});
const courseSessionName = (courseSessionId: string) => {
return statistics.value.course_session_properties.sessions.find(
(session) => session.id === courseSessionId
)?.name;
};
const circleMeta = (circleId: string) => {
return statistics.value.course_session_properties.circles.find(
(circle) => circle.id === circleId
);
};
return { courseSessionName, circleMeta };
}

View File

@ -493,7 +493,7 @@ export type FeedbackStatisticsRecordType = {
export type FeedbackStatisticsResponsesType = {
__typename?: 'FeedbackStatisticsResponsesType';
_id: Scalars['ID']['output'];
records: Array<Maybe<FeedbackStatisticsRecordType>>;
records: Array<FeedbackStatisticsRecordType>;
summary: FeedbackStatisticsSummaryType;
};
@ -1203,7 +1203,7 @@ export type CourseStatisticsQueryVariables = Exact<{
}>;
export type CourseStatisticsQuery = { __typename?: 'Query', course_statistics?: { __typename?: 'CourseStatisticsType', _id: string, course_id: string, course_title: string, course_slug: string, course_session_selection_ids: Array<string | null>, course_session_properties: { __typename?: 'StatisticsCourseSessionPropertiesType', _id: string, generations: Array<string>, sessions: Array<{ __typename?: 'StatisticsCourseSessionDataType', id: string, name: string }>, circles: Array<{ __typename?: 'StatisticsCircleDataType', id: string, name: string, experts: Array<string | null> }> }, course_session_selection_metrics: { __typename?: 'StatisticsCourseSessionsSelectionMetricType', _id: string, session_count: number, participant_count: number, expert_count: number }, attendance_day_presences: { __typename?: 'AttendanceDayPresencesStatisticsType', _id: string, records: Array<{ __typename?: 'PresenceRecordStatisticsType', _id: string, course_session_id: string, generation: string, circle_id: string, due_date: string, participants_present: number, participants_total: number, details_url: string }>, summary: { __typename?: 'AttendanceSummaryStatisticsType', _id: string, days_completed: number, participants_present: number } }, feedback_responses: { __typename?: 'FeedbackStatisticsResponsesType', _id: string, records: Array<{ __typename?: 'FeedbackStatisticsRecordType', _id: string, course_session_id: string, generation: string, circle_id: string, satisfaction_average: number, satisfaction_max: number, details_url: string } | null>, summary: { __typename?: 'FeedbackStatisticsSummaryType', _id: string, satisfaction_average: number, satisfaction_max: number, total_responses: number } }, assignments: { __typename?: 'AssignmentsStatisticsType', _id: string, summary: { __typename?: 'AssignmentStatisticsSummaryType', _id: string, completed_count: number, average_passed: number }, records: Array<{ __typename?: 'AssignmentStatisticsRecordType', _id: string, course_session_id: string, course_session_assignment_id: string, circle_id: string, generation: string, assignment_title: string, assignment_type_translation_key: string, details_url: string, deadline: string, metrics: { __typename?: 'AssignmentCompletionMetricsType', _id: string, passed_count: number, failed_count: number, unranked_count: number, ranking_completed: boolean, average_passed: number } } | null> }, competences: { __typename?: 'CompetencesStatisticsType', _id: string, summary: { __typename?: 'CompetencePerformanceStatisticsSummaryType', _id: string, success_total: number, fail_total: number }, performances: Array<{ __typename?: 'CompetencePerformanceStatisticsType', _id: string, course_session_id: string, generation: string, circle_id: string, success_count: number, fail_count: number, details_url: string } | null> } } | null };
export type CourseStatisticsQuery = { __typename?: 'Query', course_statistics?: { __typename?: 'CourseStatisticsType', _id: string, course_id: string, course_title: string, course_slug: string, course_session_selection_ids: Array<string | null>, course_session_properties: { __typename?: 'StatisticsCourseSessionPropertiesType', _id: string, generations: Array<string>, sessions: Array<{ __typename?: 'StatisticsCourseSessionDataType', id: string, name: string }>, circles: Array<{ __typename?: 'StatisticsCircleDataType', id: string, name: string, experts: Array<string | null> }> }, course_session_selection_metrics: { __typename?: 'StatisticsCourseSessionsSelectionMetricType', _id: string, session_count: number, participant_count: number, expert_count: number }, attendance_day_presences: { __typename?: 'AttendanceDayPresencesStatisticsType', _id: string, records: Array<{ __typename?: 'PresenceRecordStatisticsType', _id: string, course_session_id: string, generation: string, circle_id: string, due_date: string, participants_present: number, participants_total: number, details_url: string }>, summary: { __typename?: 'AttendanceSummaryStatisticsType', _id: string, days_completed: number, participants_present: number } }, feedback_responses: { __typename?: 'FeedbackStatisticsResponsesType', _id: string, records: Array<{ __typename?: 'FeedbackStatisticsRecordType', _id: string, course_session_id: string, generation: string, circle_id: string, satisfaction_average: number, satisfaction_max: number, details_url: string }>, summary: { __typename?: 'FeedbackStatisticsSummaryType', _id: string, satisfaction_average: number, satisfaction_max: number, total_responses: number } }, assignments: { __typename?: 'AssignmentsStatisticsType', _id: string, summary: { __typename?: 'AssignmentStatisticsSummaryType', _id: string, completed_count: number, average_passed: number }, records: Array<{ __typename?: 'AssignmentStatisticsRecordType', _id: string, course_session_id: string, course_session_assignment_id: string, circle_id: string, generation: string, assignment_title: string, assignment_type_translation_key: string, details_url: string, deadline: string, metrics: { __typename?: 'AssignmentCompletionMetricsType', _id: string, passed_count: number, failed_count: number, unranked_count: number, ranking_completed: boolean, average_passed: number } } | null> }, competences: { __typename?: 'CompetencesStatisticsType', _id: string, summary: { __typename?: 'CompetencePerformanceStatisticsSummaryType', _id: string, success_total: number, fail_total: number }, performances: Array<{ __typename?: 'CompetencePerformanceStatisticsType', _id: string, course_session_id: string, generation: string, circle_id: string, success_count: number, fail_count: number, details_url: string } | null> } } | null };
export type SendFeedbackMutationMutationVariables = Exact<{
courseSessionId: Scalars['ID']['input'];

View File

@ -86,7 +86,7 @@ type AttendanceSummaryStatisticsType {
type FeedbackStatisticsResponsesType {
_id: ID!
records: [FeedbackStatisticsRecordType]!
records: [FeedbackStatisticsRecordType!]!
summary: FeedbackStatisticsSummaryType!
}

View File

@ -5,6 +5,7 @@ import { computed } from "vue";
import type { CourseStatisticsType, PresenceRecordStatisticsType } from "@/gql/graphql";
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
import ItProgress from "@/components/ui/ItProgress.vue";
import { useCourseStatistics } from "@/composables";
const dashboardStore = useDashboardStore();
@ -12,17 +13,7 @@ const statistics = computed(() => {
return dashboardStore.currentDashBoardData as CourseStatisticsType;
});
const courseSessionName = (courseSessionId: string) => {
return statistics.value.course_session_properties.sessions.find(
(session) => session.id === courseSessionId
)?.name;
};
const circleMeta = (circleId: string) => {
return statistics.value.course_session_properties.circles.find(
(circle) => circle.id === circleId
);
};
const { courseSessionName, circleMeta } = useCourseStatistics();
const attendanceStats = (present: number, total: number) => {
return {

View File

@ -0,0 +1,86 @@
<script setup lang="ts">
import { useDashboardStore } from "@/stores/dashboard";
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
import { computed } from "vue";
import type {
CourseStatisticsType,
FeedbackStatisticsRecordType,
PresenceRecordStatisticsType,
} from "@/gql/graphql";
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
import { useCourseStatistics } from "@/composables";
const dashboardStore = useDashboardStore();
const statistics = computed(() => {
return dashboardStore.currentDashBoardData as CourseStatisticsType;
});
const { courseSessionName, circleMeta } = useCourseStatistics();
const attendanceStats = (present: number, total: number) => {
return {
SUCCESS: present,
FAIL: total - present,
UNKNOWN: 0,
};
};
</script>
<template>
<main v-if="statistics">
<div class="mb-10 flex items-center justify-between">
<h3>{{ $t("a.Feedback Teilnehmer") }}</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 v-if="statistics.feedback_responses.records" class="mt-8 bg-white">
<StatisticFilterList
:course-session-properties="statistics.course_session_properties"
:items="statistics.feedback_responses.records"
>
<template #default="{ item }">
<div class="flex justify-between">
<div>
<h4 class="font-bold">
Feedback: Circle «{{ circleMeta(item.circle_id)?.name }}»
</h4>
<div>
Durchführung «{{ courseSessionName(item.course_session_id) }}» -
Trainer: {{ circleMeta(item.circle_id)?.experts[0] }}
</div>
</div>
<div>
<div class="mb-4 flex items-center space-x-2">
<div
class="flex items-center justify-center rounded bg-green-500 px-2 py-1"
>
<span class="font-bold">
{{ (item as FeedbackStatisticsRecordType).satisfaction_average }}
</span>
&nbsp;von&nbsp;
<span>
{{ (item as FeedbackStatisticsRecordType).satisfaction_max }}
</span>
</div>
<span>
{{ $t("a.Allgemeine Zufriedenheit") }}
</span>
</div>
<router-link
class="underline"
:to="(item as PresenceRecordStatisticsType).details_url"
>
{{ $t("a.Details anschauen") }}
</router-link>
</div>
</div>
</template>
</StatisticFilterList>
</div>
</main>
</template>

View File

@ -181,26 +181,22 @@ const router = createRouter({
{
path: "attendance",
props: true,
component: () =>
import("@/pages/dashboard/statistic/AttendanceDayPresences.vue"),
component: () => import("@/pages/dashboard/statistic/AttendanceList.vue"),
},
{
path: "assignment",
props: true,
component: () =>
import("@/pages/dashboard/statistic/AttendanceDayPresences.vue"),
component: () => import("@/pages/dashboard/statistic/AttendanceList.vue"),
},
{
path: "competence",
props: true,
component: () =>
import("@/pages/dashboard/statistic/AttendanceDayPresences.vue"),
component: () => import("@/pages/dashboard/statistic/AttendanceList.vue"),
},
{
path: "feedback",
props: true,
component: () =>
import("@/pages/dashboard/statistic/AttendanceDayPresences.vue"),
component: () => import("@/pages/dashboard/statistic/FeedbackList.vue"),
},
],
},

View File

@ -26,7 +26,9 @@ class FeedbackStatisticsRecordType(graphene.ObjectType):
class FeedbackStatisticsResponsesType(graphene.ObjectType):
_id = graphene.ID(required=True)
records = graphene.List(FeedbackStatisticsRecordType, required=True)
records = graphene.List(
graphene.NonNull(FeedbackStatisticsRecordType), required=True
)
summary = graphene.Field(FeedbackStatisticsSummaryType, required=True)