Add competence detail page
This commit is contained in:
parent
7597311220
commit
93bec05abc
|
|
@ -43,7 +43,7 @@ const getIconName = () => {
|
|||
>
|
||||
<it-icon-check class="h-4/5 w-4/5"></it-icon-check>
|
||||
</div>
|
||||
<div class="ml-2">Bewertung freigegeben</div>
|
||||
<div class="ml-2">{{ $t("a.Bewertung freigegeben") }}</div>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="
|
||||
|
|
@ -58,7 +58,7 @@ const getIconName = () => {
|
|||
>
|
||||
<it-icon-check class="h-6 w-6"></it-icon-check>
|
||||
</div>
|
||||
<div class="ml-2">Ergebnisse abgegeben</div>
|
||||
<div class="ml-2">{{ $t("a.Ergebnisse abgegeben") }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -74,8 +74,8 @@ const getIconName = () => {
|
|||
</div>
|
||||
</div>
|
||||
<div v-else class="flex flex-col items-center">
|
||||
<div>Höchstpunktzahl</div>
|
||||
<div>{{ assignment.max_points }} Punkte</div>
|
||||
<div>{{ $t("a.Höchstpunktzahl") }}</div>
|
||||
<div>{{ assignment.max_points }} {{ $t("a.Punkte") }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,29 +5,29 @@ 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";
|
||||
|
||||
log.debug("CompetenceCertificateComponent setup");
|
||||
|
||||
const props = defineProps<{
|
||||
competenceCertificate: CompetenceCertificate;
|
||||
detailView: boolean;
|
||||
}>();
|
||||
|
||||
const totalPointsEvaluatedAssignments = computed(() => {
|
||||
return props.competenceCertificate.assignments.reduce((acc, assignment) => {
|
||||
if (assignment.completion?.completion_status === "EVALUATION_SUBMITTED") {
|
||||
return acc + assignment.max_points;
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
return _.sum(
|
||||
props.competenceCertificate.assignments
|
||||
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
|
||||
.map((a) => a.max_points)
|
||||
);
|
||||
});
|
||||
|
||||
const userPointsEvaluatedAssignments = computed(() => {
|
||||
return props.competenceCertificate.assignments.reduce((acc, assignment) => {
|
||||
if (assignment.completion?.completion_status === "EVALUATION_SUBMITTED") {
|
||||
return acc + (assignment.completion?.evaluation_points ?? 0);
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
return _.sum(
|
||||
props.competenceCertificate.assignments
|
||||
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
|
||||
.map((a) => a.completion?.evaluation_points ?? 0)
|
||||
);
|
||||
});
|
||||
|
||||
const numAssignmentsEvaluated = computed(() => {
|
||||
|
|
@ -52,12 +52,23 @@ const progressStatusCount = computed(() => {
|
|||
<template>
|
||||
<div>
|
||||
<div class="mb-4 bg-white p-8">
|
||||
<h2>
|
||||
<div class="flex items-center">
|
||||
<h3 :class="{ 'heading-2': props.detailView }">
|
||||
{{ props.competenceCertificate.title }}
|
||||
</h2>
|
||||
</h3>
|
||||
<div
|
||||
v-if="numAssignmentsEvaluated < numAssignmentsTotal"
|
||||
class="ml-2 rounded-full bg-gray-200 px-2.5 py-0.5 text-sm"
|
||||
>
|
||||
{{ $t("a.Zwischenstand") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="heading-1 py-4">
|
||||
<div
|
||||
class="py-4"
|
||||
:class="{ 'heading-1': props.detailView, 'heading-2': !props.detailView }"
|
||||
>
|
||||
{{ userPointsEvaluatedAssignments }}
|
||||
</div>
|
||||
<div class="pl-2">von {{ totalPointsEvaluatedAssignments }} Punkten</div>
|
||||
|
|
@ -67,8 +78,19 @@ const progressStatusCount = computed(() => {
|
|||
{{ numAssignmentsEvaluated }} von {{ numAssignmentsTotal }} Arbeiten
|
||||
abgeschlossen
|
||||
</div>
|
||||
|
||||
<div v-if="!props.detailView">
|
||||
<router-link
|
||||
:to="competenceCertificate.frontend_url"
|
||||
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 v-if="props.detailView">
|
||||
<div
|
||||
v-for="assignment in props.competenceCertificate.assignments"
|
||||
:key="assignment.id"
|
||||
|
|
@ -77,6 +99,7 @@ const progressStatusCount = computed(() => {
|
|||
<CompetenceAssignmentRow :assignment="assignment"></CompetenceAssignmentRow>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@ import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertifi
|
|||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
certificateSlug: string;
|
||||
}>();
|
||||
|
||||
log.debug("CompetenceOverviewPage created", props);
|
||||
log.debug("CompetenceCertificateDetailPage created", props);
|
||||
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
const queryResult = useQuery({
|
||||
const certificatesQuery = useQuery({
|
||||
query: COMPETENCE_NAVI_CERTIFICATE_QUERY,
|
||||
variables: {
|
||||
courseSlug: props.courseSlug,
|
||||
|
|
@ -23,11 +24,11 @@ const queryResult = useQuery({
|
|||
},
|
||||
});
|
||||
|
||||
const competenceCertificates = computed(() => {
|
||||
const certificate = computed(() => {
|
||||
return (
|
||||
(queryResult.data.value?.competence_certificate_list
|
||||
(certificatesQuery.data.value?.competence_certificate_list
|
||||
?.competence_certificates as unknown as CompetenceCertificate[]) ?? []
|
||||
);
|
||||
).find((cc) => cc.slug.endsWith(props.certificateSlug));
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
|
|
@ -37,14 +38,19 @@ onMounted(async () => {
|
|||
|
||||
<template>
|
||||
<div class="container-large lg:mt-4">
|
||||
<h1>{{ $t("competences.title") }}</h1>
|
||||
|
||||
<div
|
||||
v-for="competenceCertificate in competenceCertificates"
|
||||
:key="competenceCertificate.id"
|
||||
<nav class="lg:pb-4">
|
||||
<router-link
|
||||
class="btn-text inline-flex items-center pl-0"
|
||||
:to="`/course/${props.courseSlug}/competence/certificates`"
|
||||
>
|
||||
<it-icon-arrow-left />
|
||||
<span>{{ $t("general.back") }}</span>
|
||||
</router-link>
|
||||
</nav>
|
||||
<div v-if="certificate">
|
||||
<CompetenceCertificateComponent
|
||||
:competence-certificate="competenceCertificate"
|
||||
:competence-certificate="certificate"
|
||||
:detail-view="true"
|
||||
></CompetenceCertificateComponent>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
<script setup lang="ts">
|
||||
import log from "loglevel";
|
||||
import { COMPETENCE_NAVI_CERTIFICATE_QUERY } from "@/graphql/queries";
|
||||
import { useQuery } from "@urql/vue";
|
||||
import { computed, onMounted } from "vue";
|
||||
import type { CompetenceCertificate } from "@/types";
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue";
|
||||
import _ from "lodash";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
}>();
|
||||
|
||||
log.debug("CompetenceOverviewPage created", 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 assignments = computed(() => {
|
||||
return competenceCertificates.value.flatMap((cc) => cc.assignments);
|
||||
});
|
||||
|
||||
const totalPointsEvaluatedAssignments = computed(() => {
|
||||
return _.sum(
|
||||
assignments.value
|
||||
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
|
||||
.map((a) => a.max_points)
|
||||
);
|
||||
});
|
||||
|
||||
const userPointsEvaluatedAssignments = computed(() => {
|
||||
return _.sum(
|
||||
assignments.value
|
||||
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
|
||||
.map((a) => a.completion?.evaluation_points ?? 0)
|
||||
);
|
||||
});
|
||||
|
||||
const numAssignmentsEvaluated = computed(() => {
|
||||
return assignments.value.filter((a) => {
|
||||
return a.completion?.completion_status === "EVALUATION_SUBMITTED";
|
||||
}).length;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
// log.debug("AssignmentView mounted", props.assignmentId, props.userId);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container-large lg:mt-4">
|
||||
<h2 class="mb-4">{{ $t("a.Kompetenznachweise") }}</h2>
|
||||
|
||||
<div class="mb-4 bg-white p-8">
|
||||
<div class="flex items-center">
|
||||
<h3>{{ $t("a.Gesamtpunktzahl") }}</h3>
|
||||
<div
|
||||
v-if="numAssignmentsEvaluated < assignments.length"
|
||||
class="ml-2 rounded-full bg-gray-200 px-2.5 py-0.5 text-sm"
|
||||
>
|
||||
{{ $t("a.Zwischenstand") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="heading-1 py-4">
|
||||
{{ userPointsEvaluatedAssignments }}
|
||||
</div>
|
||||
<div class="pl-2">von {{ totalPointsEvaluatedAssignments }} Punkten</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="competenceCertificate in competenceCertificates"
|
||||
:key="competenceCertificate.id"
|
||||
>
|
||||
<CompetenceCertificateComponent
|
||||
:competence-certificate="competenceCertificate"
|
||||
:detail-view="false"
|
||||
></CompetenceCertificateComponent>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import CompetenceDetail from "@/pages/competence-old/CompetenceDetail.vue";
|
||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import * as log from "loglevel";
|
||||
|
||||
log.debug("CompetenceListPage created");
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
}>();
|
||||
|
||||
const competenceStore = useCompetenceStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container-large">
|
||||
<nav class="py-4 lg:pb-8">
|
||||
<router-link
|
||||
v-if="competenceStore.competenceProfilePage()"
|
||||
class="btn-text inline-flex items-center pl-0"
|
||||
:to="`${competenceStore.competenceProfilePage()?.frontend_url}-old` as string"
|
||||
>
|
||||
<it-icon-arrow-left />
|
||||
<span>{{ $t("general.back") }}</span>
|
||||
</router-link>
|
||||
</nav>
|
||||
<div class="mb-10 flex flex-col items-center justify-between lg:flex-row">
|
||||
<h1>Kompetenzen</h1>
|
||||
<ItDropdownSelect
|
||||
v-model="competenceStore.selectedCircle"
|
||||
class="mt-4 w-full lg:mt-0 lg:w-96"
|
||||
:items="competenceStore.availableCircles"
|
||||
></ItDropdownSelect>
|
||||
</div>
|
||||
<ul v-if="competenceStore.competenceProfilePage()">
|
||||
<li
|
||||
v-for="competence in competenceStore.competences()"
|
||||
:key="competence.id"
|
||||
class="mb-8 bg-white p-8"
|
||||
>
|
||||
<CompetenceDetail
|
||||
:competence="competence"
|
||||
:course-slug="props.courseSlug"
|
||||
></CompetenceDetail>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -31,10 +31,19 @@ onMounted(async () => {
|
|||
class="inline-block border-t-2 border-t-transparent py-3"
|
||||
:class="{ 'border-b-2 border-b-blue-900': true }"
|
||||
>
|
||||
<router-link :to="`/`">
|
||||
<router-link :to="`/course/${courseSlug}/competence`">
|
||||
{{ $t("mediaLibrary.overview") }}
|
||||
</router-link>
|
||||
</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 }"
|
||||
>
|
||||
<router-link :to="`/course/${courseSlug}/competence/certificates`">
|
||||
{{ $t("a.Kompetenznachweise") }}
|
||||
</router-link>
|
||||
</li>
|
||||
|
||||
<!-- Add similar logic for other `li` items as you expand the list -->
|
||||
<li class="ml-6 inline-block lg:ml-12"></li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -67,9 +67,16 @@ const router = createRouter({
|
|||
component: () => import("@/pages/competence/CompetenceParentPage.vue"),
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
path: "certificates",
|
||||
props: true,
|
||||
component: () => import("@/pages/competence/CompetenceOverviewPage.vue"),
|
||||
component: () =>
|
||||
import("@/pages/competence/CompetenceCertificateListPage.vue"),
|
||||
},
|
||||
{
|
||||
path: "certificates/:certificateSlug",
|
||||
props: true,
|
||||
component: () =>
|
||||
import("@/pages/competence/CompetenceCertificateDetailPage.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load wagtailadmin_tags %}
|
||||
<table class="listing {% if full_width %}full-width{% endif %} {% block table_classname %}{% endblock %}">
|
||||
{% if show_ordering_column or show_bulk_actions %}
|
||||
<col width="10px"/>
|
||||
{% endif %}
|
||||
<col/>
|
||||
{% if show_parent %}
|
||||
<col/>
|
||||
{% endif %}
|
||||
<col width="12%"/>
|
||||
<col width="12%"/>
|
||||
<col width="12%"/>
|
||||
<col width="10%"/>
|
||||
<thead>
|
||||
{% block pre_parent_page_headers %}
|
||||
{% endblock %}
|
||||
|
||||
{% if parent_page %}
|
||||
{% page_permissions parent_page as parent_page_perms %}
|
||||
<tr class="index {% if not parent_page.live %} unpublished{% endif %}
|
||||
{% block parent_page_row_classname %}{% endblock %}">
|
||||
<td class="title"{% if show_ordering_column or show_bulk_actions %} colspan="2"{% endif %}>
|
||||
{% block parent_page_title %}
|
||||
{% endblock %}
|
||||
</td>
|
||||
<td class="updated" valign="bottom">{% if parent_page.latest_revision_created_at %}
|
||||
<div class="human-readable-date" title="{{ parent_page.latest_revision_created_at|date:"DATETIME_FORMAT" }}">
|
||||
{% blocktrans trimmed with time_period=parent_page.latest_revision_created_at|timesince %}{{ time_period }}
|
||||
ago{% endblocktrans %}</div>{% endif %}</td>
|
||||
<td class="type" valign="bottom">
|
||||
{% if not parent_page.is_root %}
|
||||
{{ parent_page.content_type.model_class.get_verbose_name }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="status" valign="bottom">
|
||||
{% if not parent_page.is_root %}
|
||||
{% include "wagtailadmin/shared/page_status_tag.html" with page=parent_page %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% block post_parent_page_headers %}
|
||||
{% endblock %}
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if pages %}
|
||||
{% trans "Select page" as checkbox_aria_label %}
|
||||
{% for page in pages %}
|
||||
{% page_permissions page as page_perms %}
|
||||
<tr {% if ordering == "ord" %}id="page_{{ page.id|unlocalize }}"
|
||||
data-page-title="{{ page.get_admin_display_title }}"{% endif %}
|
||||
class="{% if not page.live %}unpublished{% endif %} {% block page_row_classname %}{% endblock %}">
|
||||
{% if show_ordering_column %}
|
||||
<td class="ord">{% if orderable and ordering == "ord" %}
|
||||
<div class="handle icon icon-grip text-replace">{% trans 'Drag' %}</div>{% endif %}</td>
|
||||
{% elif show_bulk_actions %}
|
||||
{% include "wagtailadmin/bulk_actions/listing_checkbox_cell.html" with obj_type="page" obj=page aria_labelledby_prefix="page_" aria_labelledby=page.pk|unlocalize aria_labelledby_suffix="_title" %}
|
||||
{% endif %}
|
||||
<td id="page_{{ page.pk|unlocalize }}_title" class="title title_type_{{ page.content_type.model }}" valign="top" data-listing-page-title>
|
||||
{{ page.type }}
|
||||
{% block page_title %}
|
||||
{% endblock %}
|
||||
</td>
|
||||
{% if show_parent %}
|
||||
<td class="parent" valign="top">
|
||||
{% block page_parent_page_title %}
|
||||
{% with page.get_parent as parent %}
|
||||
{% if parent %}
|
||||
<a
|
||||
href="{% url 'wagtailadmin_explore' parent.id %}">{{ parent.specific_deferred.get_admin_display_title }}</a>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="updated" valign="top">{% if page.latest_revision_created_at %}
|
||||
<div class="human-readable-date" title="{{ page.latest_revision_created_at|date:"DATETIME_FORMAT" }}">
|
||||
{% blocktrans trimmed with time_period=page.latest_revision_created_at|timesince %}{{ time_period }}
|
||||
ago{% endblocktrans %}</div>{% endif %}</td>
|
||||
<td class="type" valign="top">{{ page.content_type.model_class.get_verbose_name }}</td>
|
||||
<td class="status" valign="top">
|
||||
{% include "wagtailadmin/shared/page_status_tag.html" with page=page %}
|
||||
</td>
|
||||
{% block page_navigation %}
|
||||
{% endblock %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% block no_results %}{% endblock %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
{% extends "wagtailadmin/pages/listing/_list_explore.html" %}
|
||||
|
||||
{% load i18n wagtailadmin_tags %}
|
||||
|
||||
|
||||
{% block page_title %}
|
||||
{% include "wagtailadmin/pages/listing/_page_title_explore.html" %}
|
||||
{% endblock %}
|
||||
Loading…
Reference in New Issue