feat: add feedback list
This commit is contained in:
parent
62c3aaf849
commit
e2a346caed
|
|
@ -1,8 +1,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from "vue";
|
import { computed, ref, watch } from "vue";
|
||||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||||
|
import { useTranslation } from "i18next-vue";
|
||||||
import type { StatisticsCourseSessionPropertiesType } from "@/gql/graphql";
|
import type { StatisticsCourseSessionPropertiesType } from "@/gql/graphql";
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
interface Item {
|
interface Item {
|
||||||
_id: string;
|
_id: string;
|
||||||
course_session_id: string;
|
course_session_id: string;
|
||||||
|
|
@ -17,18 +20,18 @@ const props = defineProps<{
|
||||||
|
|
||||||
const sessionFilter = computed(() => {
|
const sessionFilter = computed(() => {
|
||||||
const f = props.courseSessionProperties.sessions.map((session) => ({
|
const f = props.courseSessionProperties.sessions.map((session) => ({
|
||||||
name: `Durchführung: ${session.name}`,
|
name: `${t("a.Durchfuehrung")}: ${session.name}`,
|
||||||
id: session.id,
|
id: session.id,
|
||||||
}));
|
}));
|
||||||
return [{ name: "Durchführung: Alle", id: "_all" }, ...f];
|
return [{ name: t("a.AlleDurchführungen"), id: "_all" }, ...f];
|
||||||
});
|
});
|
||||||
|
|
||||||
const generationFilter = computed(() => {
|
const generationFilter = computed(() => {
|
||||||
const f = props.courseSessionProperties.generations.map((generation) => ({
|
const f = props.courseSessionProperties.generations.map((generation) => ({
|
||||||
name: `Generation: ${generation}`,
|
name: `${t("a.Generation")}: ${generation}`,
|
||||||
id: generation,
|
id: generation,
|
||||||
}));
|
}));
|
||||||
return [{ name: "Generation: Alle", id: "_all" }, ...f];
|
return [{ name: t("a.AlleGenerationen"), id: "_all" }, ...f];
|
||||||
});
|
});
|
||||||
|
|
||||||
const circleFilter = computed(() => {
|
const circleFilter = computed(() => {
|
||||||
|
|
@ -36,7 +39,7 @@ const circleFilter = computed(() => {
|
||||||
name: `Circle: ${circle.name}`,
|
name: `Circle: ${circle.name}`,
|
||||||
id: circle.id,
|
id: circle.id,
|
||||||
}));
|
}));
|
||||||
return [{ name: "Circle: Alle", id: "_all" }, ...f];
|
return [{ name: t("a.AlleCircle"), id: "_all" }, ...f];
|
||||||
});
|
});
|
||||||
|
|
||||||
const sessionFilterValue = ref(sessionFilter.value[0]);
|
const sessionFilterValue = ref(sessionFilter.value[0]);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { CourseStatisticsType } from "@/gql/graphql";
|
||||||
import { graphqlClient } from "@/graphql/client";
|
import { graphqlClient } from "@/graphql/client";
|
||||||
import { COURSE_QUERY, COURSE_SESSION_DETAIL_QUERY } from "@/graphql/queries";
|
import { COURSE_QUERY, COURSE_SESSION_DETAIL_QUERY } from "@/graphql/queries";
|
||||||
import {
|
import {
|
||||||
|
|
@ -7,6 +8,7 @@ import {
|
||||||
} from "@/services/circle";
|
} from "@/services/circle";
|
||||||
import { useCompletionStore } from "@/stores/completion";
|
import { useCompletionStore } from "@/stores/completion";
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||||
|
import { useDashboardStore } from "@/stores/dashboard";
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
import type {
|
import type {
|
||||||
ActionCompetence,
|
ActionCompetence,
|
||||||
|
|
@ -411,3 +413,25 @@ export function useCourseDataWithCompletion(
|
||||||
nextLearningContent,
|
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 };
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -493,7 +493,7 @@ export type FeedbackStatisticsRecordType = {
|
||||||
export type FeedbackStatisticsResponsesType = {
|
export type FeedbackStatisticsResponsesType = {
|
||||||
__typename?: 'FeedbackStatisticsResponsesType';
|
__typename?: 'FeedbackStatisticsResponsesType';
|
||||||
_id: Scalars['ID']['output'];
|
_id: Scalars['ID']['output'];
|
||||||
records: Array<Maybe<FeedbackStatisticsRecordType>>;
|
records: Array<FeedbackStatisticsRecordType>;
|
||||||
summary: FeedbackStatisticsSummaryType;
|
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<{
|
export type SendFeedbackMutationMutationVariables = Exact<{
|
||||||
courseSessionId: Scalars['ID']['input'];
|
courseSessionId: Scalars['ID']['input'];
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ type AttendanceSummaryStatisticsType {
|
||||||
|
|
||||||
type FeedbackStatisticsResponsesType {
|
type FeedbackStatisticsResponsesType {
|
||||||
_id: ID!
|
_id: ID!
|
||||||
records: [FeedbackStatisticsRecordType]!
|
records: [FeedbackStatisticsRecordType!]!
|
||||||
summary: FeedbackStatisticsSummaryType!
|
summary: FeedbackStatisticsSummaryType!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ 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";
|
||||||
|
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
|
|
||||||
|
|
@ -12,17 +13,7 @@ const statistics = computed(() => {
|
||||||
return dashboardStore.currentDashBoardData as CourseStatisticsType;
|
return dashboardStore.currentDashBoardData as CourseStatisticsType;
|
||||||
});
|
});
|
||||||
|
|
||||||
const courseSessionName = (courseSessionId: string) => {
|
const { courseSessionName, circleMeta } = useCourseStatistics();
|
||||||
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 attendanceStats = (present: number, total: number) => {
|
const attendanceStats = (present: number, total: number) => {
|
||||||
return {
|
return {
|
||||||
|
|
@ -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>
|
||||||
|
von
|
||||||
|
<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>
|
||||||
|
|
@ -181,26 +181,22 @@ const router = createRouter({
|
||||||
{
|
{
|
||||||
path: "attendance",
|
path: "attendance",
|
||||||
props: true,
|
props: true,
|
||||||
component: () =>
|
component: () => import("@/pages/dashboard/statistic/AttendanceList.vue"),
|
||||||
import("@/pages/dashboard/statistic/AttendanceDayPresences.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "assignment",
|
path: "assignment",
|
||||||
props: true,
|
props: true,
|
||||||
component: () =>
|
component: () => import("@/pages/dashboard/statistic/AttendanceList.vue"),
|
||||||
import("@/pages/dashboard/statistic/AttendanceDayPresences.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "competence",
|
path: "competence",
|
||||||
props: true,
|
props: true,
|
||||||
component: () =>
|
component: () => import("@/pages/dashboard/statistic/AttendanceList.vue"),
|
||||||
import("@/pages/dashboard/statistic/AttendanceDayPresences.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "feedback",
|
path: "feedback",
|
||||||
props: true,
|
props: true,
|
||||||
component: () =>
|
component: () => import("@/pages/dashboard/statistic/FeedbackList.vue"),
|
||||||
import("@/pages/dashboard/statistic/AttendanceDayPresences.vue"),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@ class FeedbackStatisticsRecordType(graphene.ObjectType):
|
||||||
|
|
||||||
class FeedbackStatisticsResponsesType(graphene.ObjectType):
|
class FeedbackStatisticsResponsesType(graphene.ObjectType):
|
||||||
_id = graphene.ID(required=True)
|
_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)
|
summary = graphene.Field(FeedbackStatisticsSummaryType, required=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue