Add competence detail page
This commit is contained in:
parent
93bec05abc
commit
24511df01e
|
|
@ -4,9 +4,14 @@ import * as log from "loglevel";
|
|||
|
||||
log.debug("CompetenceAssignmentRow setup");
|
||||
|
||||
const props = defineProps<{
|
||||
export interface Props {
|
||||
assignment: CompetenceCertificateAssignment;
|
||||
}>();
|
||||
addBorderBottom?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
addBorderBottom: false,
|
||||
});
|
||||
|
||||
const getIconName = () => {
|
||||
if (props.assignment.assignment_type === "EDONIQ_TEST") {
|
||||
|
|
@ -17,7 +22,7 @@ const getIconName = () => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center border-b py-8">
|
||||
<div class="flex items-center py-8" :class="{ 'border-b': props.addBorderBottom }">
|
||||
<component :is="getIconName()" class="mr-4 h-9 w-9"></component>
|
||||
<div class="flex w-[420px] flex-col">
|
||||
<h3 class="text-bold flex items-center gap-2">{{ assignment.title }}</h3>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,11 @@ import CompetenceAssignmentRow from "@/pages/competence/CompetenceAssignmentRow.
|
|||
import { computed } from "vue";
|
||||
import type { StatusCount } from "@/components/ui/ItProgress.vue";
|
||||
import ItProgress from "@/components/ui/ItProgress.vue";
|
||||
import _ from "lodash";
|
||||
import {
|
||||
assignmentsMaxEvaluationPoints,
|
||||
assignmentsUserPoints,
|
||||
competenceCertificateProgressStatusCount,
|
||||
} from "@/pages/competence/utils";
|
||||
|
||||
log.debug("CompetenceCertificateComponent setup");
|
||||
|
||||
|
|
@ -15,19 +19,11 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const totalPointsEvaluatedAssignments = computed(() => {
|
||||
return _.sum(
|
||||
props.competenceCertificate.assignments
|
||||
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
|
||||
.map((a) => a.max_points)
|
||||
);
|
||||
return assignmentsMaxEvaluationPoints(props.competenceCertificate.assignments);
|
||||
});
|
||||
|
||||
const userPointsEvaluatedAssignments = computed(() => {
|
||||
return _.sum(
|
||||
props.competenceCertificate.assignments
|
||||
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
|
||||
.map((a) => a.completion?.evaluation_points ?? 0)
|
||||
);
|
||||
return assignmentsUserPoints(props.competenceCertificate.assignments);
|
||||
});
|
||||
|
||||
const numAssignmentsEvaluated = computed(() => {
|
||||
|
|
@ -41,11 +37,9 @@ const numAssignmentsTotal = computed(() => {
|
|||
});
|
||||
|
||||
const progressStatusCount = computed(() => {
|
||||
return {
|
||||
SUCCESS: numAssignmentsEvaluated.value,
|
||||
UNKNOWN: numAssignmentsTotal.value - numAssignmentsEvaluated.value,
|
||||
FAIL: 0,
|
||||
} as StatusCount;
|
||||
return competenceCertificateProgressStatusCount(
|
||||
props.competenceCertificate.assignments
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -92,11 +86,14 @@ const progressStatusCount = computed(() => {
|
|||
|
||||
<div v-if="props.detailView">
|
||||
<div
|
||||
v-for="assignment in props.competenceCertificate.assignments"
|
||||
v-for="(assignment, index) in competenceCertificate.assignments"
|
||||
:key="assignment.id"
|
||||
class="bg-white px-8"
|
||||
>
|
||||
<CompetenceAssignmentRow :assignment="assignment"></CompetenceAssignmentRow>
|
||||
<CompetenceAssignmentRow
|
||||
:assignment="assignment"
|
||||
:add-border-bottom="index < competenceCertificate.assignments.length - 1"
|
||||
></CompetenceAssignmentRow>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ const props = defineProps<{
|
|||
certificateSlug: string;
|
||||
}>();
|
||||
|
||||
log.debug("CompetenceCertificateDetailPage created", props);
|
||||
log.debug("CompetenceCertificateDetailPage setup", props);
|
||||
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,16 @@ import { computed, onMounted } from "vue";
|
|||
import type { CompetenceCertificate } from "@/types";
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue";
|
||||
import _ from "lodash";
|
||||
import {
|
||||
assignmentsMaxEvaluationPoints,
|
||||
assignmentsUserPoints,
|
||||
} from "@/pages/competence/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
}>();
|
||||
|
||||
log.debug("CompetenceOverviewPage created", props);
|
||||
log.debug("CompetenceCertificateListPage setup", props);
|
||||
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
|
|
@ -36,19 +39,11 @@ const assignments = computed(() => {
|
|||
});
|
||||
|
||||
const totalPointsEvaluatedAssignments = computed(() => {
|
||||
return _.sum(
|
||||
assignments.value
|
||||
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
|
||||
.map((a) => a.max_points)
|
||||
);
|
||||
return assignmentsMaxEvaluationPoints(assignments.value);
|
||||
});
|
||||
|
||||
const userPointsEvaluatedAssignments = computed(() => {
|
||||
return _.sum(
|
||||
assignments.value
|
||||
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
|
||||
.map((a) => a.completion?.evaluation_points ?? 0)
|
||||
);
|
||||
return assignmentsUserPoints(assignments.value);
|
||||
});
|
||||
|
||||
const numAssignmentsEvaluated = computed(() => {
|
||||
|
|
|
|||
|
|
@ -1,82 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import PerformanceCriteriaRow from "@/pages/competence-old/PerformanceCriteriaRow.vue";
|
||||
import ItProgress from "@/components/ui/ItProgress.vue";
|
||||
import ItToggleArrow from "@/components/ui/ItToggleArrow.vue";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import type { CompetencePage } from "@/types";
|
||||
import log from "loglevel";
|
||||
import { ref } from "vue";
|
||||
|
||||
const competenceStore = useCompetenceStore();
|
||||
|
||||
interface Props {
|
||||
competence: CompetencePage;
|
||||
courseSlug: string;
|
||||
showAssessAgain?: boolean;
|
||||
isInline?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
showAssessAgain: true,
|
||||
isInline: false,
|
||||
});
|
||||
|
||||
log.debug("PerformanceCriteriaRow created", props);
|
||||
|
||||
const isOpen = ref(false);
|
||||
|
||||
const togglePerformanceCriteria = () => {
|
||||
isOpen.value = !isOpen.value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div :class="{ 'mb-4 border-b pb-8': isOpen }" class="-mx-8 px-8">
|
||||
<div
|
||||
class="flex flex-row items-center justify-between"
|
||||
:class="props.isInline ? '' : 'mb-4'"
|
||||
role="button"
|
||||
aria-pressed="false"
|
||||
@click="togglePerformanceCriteria()"
|
||||
>
|
||||
<h2 :class="props.isInline ? ['text-bold', 'w-2/5'] : 'text-large'">
|
||||
{{ competence.competence_id }} {{ competence.title }}
|
||||
</h2>
|
||||
<ItProgress
|
||||
v-if="isInline"
|
||||
class="w-[330px]"
|
||||
:status-count="
|
||||
competenceStore.calcStatusCount(
|
||||
competenceStore.criteriaByCompetence(competence)
|
||||
)
|
||||
"
|
||||
></ItProgress>
|
||||
<ItToggleArrow :is-open="isOpen" :small="isInline"></ItToggleArrow>
|
||||
</div>
|
||||
<ItProgress
|
||||
v-if="!isInline"
|
||||
:status-count="
|
||||
competenceStore.calcStatusCount(
|
||||
competenceStore.criteriaByCompetence(competence)
|
||||
)
|
||||
"
|
||||
></ItProgress>
|
||||
</div>
|
||||
<ul v-if="isOpen">
|
||||
<li
|
||||
v-for="performanceCriteria in competenceStore.criteriaByCompetence(competence)"
|
||||
:key="performanceCriteria.id"
|
||||
class="mb-4 border-b pb-4 last:border-0"
|
||||
>
|
||||
<PerformanceCriteriaRow
|
||||
:criteria="performanceCriteria"
|
||||
:show-state="true"
|
||||
:course-slug="props.courseSlug"
|
||||
:show-assess-again="props.showAssessAgain"
|
||||
></PerformanceCriteriaRow>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
<script setup lang="ts">
|
||||
import log from "loglevel";
|
||||
import { COMPETENCE_NAVI_CERTIFICATE_QUERY } from "@/graphql/queries";
|
||||
import { useQuery } from "@urql/vue";
|
||||
import { computed } from "vue";
|
||||
import type { CompetenceCertificate } from "@/types";
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import {
|
||||
assignmentsMaxEvaluationPoints,
|
||||
assignmentsUserPoints,
|
||||
competenceCertificateProgressStatusCount,
|
||||
} from "@/pages/competence/utils";
|
||||
import ItProgress from "@/components/ui/ItProgress.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
}>();
|
||||
|
||||
log.debug("CompetenceIndexPage setup", props);
|
||||
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
const certificatesQuery = useQuery({
|
||||
query: COMPETENCE_NAVI_CERTIFICATE_QUERY,
|
||||
variables: {
|
||||
courseSlug: props.courseSlug,
|
||||
courseSessionId: courseSession.value.id.toString(),
|
||||
},
|
||||
});
|
||||
|
||||
const competenceCertificates = computed(() => {
|
||||
return (
|
||||
(certificatesQuery.data.value?.competence_certificate_list
|
||||
?.competence_certificates as unknown as CompetenceCertificate[]) ?? []
|
||||
);
|
||||
});
|
||||
|
||||
const allAssignments = computed(() => {
|
||||
return competenceCertificates.value.flatMap((cc) => cc.assignments);
|
||||
});
|
||||
|
||||
const totalPointsEvaluatedAssignments = computed(() => {
|
||||
return assignmentsMaxEvaluationPoints(allAssignments.value);
|
||||
});
|
||||
|
||||
const userPointsEvaluatedAssignments = computed(() => {
|
||||
return assignmentsUserPoints(allAssignments.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container-large lg:mt-4">
|
||||
<h1 class="mb-8">{{ $t("a.KompetenzNavi") }}</h1>
|
||||
|
||||
<div class="mb-4 bg-white p-8">
|
||||
<div class="flex items-center">
|
||||
<h3>{{ $t("a.Kompetenznachweise") }}</h3>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
{{ $t("a.Zwischenstand") }} {{ $t("a.Gesamtpunktzahl") }}:
|
||||
<span class="font-bold">
|
||||
{{ userPointsEvaluatedAssignments }}
|
||||
</span>
|
||||
von {{ totalPointsEvaluatedAssignments }} Punkten
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="mt-4">
|
||||
<div
|
||||
v-for="certificate in competenceCertificates"
|
||||
:key="certificate.id"
|
||||
class="flex items-center justify-between border-b py-4 first:border-t"
|
||||
>
|
||||
<div class="text-bold text-xl">
|
||||
{{ certificate.title }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-bold">
|
||||
{{ assignmentsMaxEvaluationPoints(certificate.assignments) }}
|
||||
</span>
|
||||
von
|
||||
{{ assignmentsUserPoints(certificate.assignments) }}
|
||||
Punkten
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div>
|
||||
{{
|
||||
certificate.assignments.filter(
|
||||
(a) => a.completion?.completion_status === "EVALUATION_SUBMITTED"
|
||||
).length
|
||||
}}
|
||||
von
|
||||
{{ certificate.assignments.length }}
|
||||
Arbeiten abgeschlossen
|
||||
</div>
|
||||
<div class="ml-2 w-40">
|
||||
<ItProgress
|
||||
:status-count="
|
||||
competenceCertificateProgressStatusCount(certificate.assignments)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<router-link
|
||||
:to="`/course/${props.courseSlug}/competence/certificates`"
|
||||
class="btn-text mt-4 inline-flex items-center py-2 pl-0"
|
||||
>
|
||||
<span>{{ $t("a.Details anschauen") }}</span>
|
||||
<it-icon-arrow-right></it-icon-arrow-right>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import * as log from "loglevel";
|
||||
import { onMounted } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
log.debug("CompetenceParentPage created");
|
||||
|
||||
|
|
@ -11,6 +12,16 @@ const props = defineProps<{
|
|||
|
||||
const competenceStore = useCompetenceStore();
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
function routeInOverview() {
|
||||
return route.path.endsWith("/competence");
|
||||
}
|
||||
|
||||
function routeInCompetenceCertificate() {
|
||||
return route.path.includes("/certificate");
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
log.debug("CompetenceParentPage mounted", props.courseSlug);
|
||||
|
||||
|
|
@ -29,7 +40,7 @@ onMounted(async () => {
|
|||
<ul class="scrollbar overflow-auto whitespace-nowrap">
|
||||
<li
|
||||
class="inline-block border-t-2 border-t-transparent py-3"
|
||||
:class="{ 'border-b-2 border-b-blue-900': true }"
|
||||
:class="{ 'border-b-2 border-b-blue-900': routeInOverview() }"
|
||||
>
|
||||
<router-link :to="`/course/${courseSlug}/competence`">
|
||||
{{ $t("mediaLibrary.overview") }}
|
||||
|
|
@ -37,7 +48,7 @@ onMounted(async () => {
|
|||
</li>
|
||||
<li
|
||||
class="ml-6 inline-block border-t-2 border-t-transparent py-3 lg:ml-12"
|
||||
:class="{ 'border-b-2 border-b-blue-900': true }"
|
||||
:class="{ 'border-b-2 border-b-blue-900': routeInCompetenceCertificate() }"
|
||||
>
|
||||
<router-link :to="`/course/${courseSlug}/competence/certificates`">
|
||||
{{ $t("a.Kompetenznachweise") }}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
import type { StatusCount } from "@/components/ui/ItProgress.vue";
|
||||
import type { CompetenceCertificateAssignment } from "@/types";
|
||||
import _ from "lodash";
|
||||
|
||||
export function assignmentsMaxEvaluationPoints(
|
||||
assignments: CompetenceCertificateAssignment[]
|
||||
): number {
|
||||
return _.sum(
|
||||
assignments
|
||||
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
|
||||
.map((a) => a.max_points)
|
||||
);
|
||||
}
|
||||
|
||||
export function assignmentsUserPoints(assignments: CompetenceCertificateAssignment[]) {
|
||||
return _.sum(
|
||||
assignments
|
||||
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
|
||||
.map((a) => a.completion?.evaluation_points ?? 0)
|
||||
);
|
||||
}
|
||||
|
||||
export function competenceCertificateProgressStatusCount(
|
||||
assignments: CompetenceCertificateAssignment[]
|
||||
) {
|
||||
const numAssignmentsEvaluated = assignments.filter(
|
||||
(a) => a.completion?.completion_status === "EVALUATION_SUBMITTED"
|
||||
).length;
|
||||
return {
|
||||
SUCCESS: numAssignmentsEvaluated,
|
||||
UNKNOWN: assignments.length - numAssignmentsEvaluated,
|
||||
FAIL: 0,
|
||||
} as StatusCount;
|
||||
}
|
||||
|
|
@ -66,6 +66,12 @@ const router = createRouter({
|
|||
props: true,
|
||||
component: () => import("@/pages/competence/CompetenceParentPage.vue"),
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
props: true,
|
||||
component: () => import("@/pages/competence/CompetenceIndexPage.vue"),
|
||||
},
|
||||
|
||||
{
|
||||
path: "certificates",
|
||||
props: true,
|
||||
|
|
|
|||
Loading…
Reference in New Issue