Merged in feature/VBV-304-praesenztag-ui (pull request #65)
Feature/VBV-304 praesenztag ui Approved-by: Daniel Egger
This commit is contained in:
commit
96334b4eb6
|
|
@ -0,0 +1,26 @@
|
|||
<template>
|
||||
<div class="mb-12 grid grid-cols-icon-card gap-x-4 grid-areas-icon-card">
|
||||
<it-icon-calendar class="w-[60px] grid-in-icon" />
|
||||
<h2 class="text-large font-bold grid-in-title">Datum</h2>
|
||||
<p class="grid-in-value">{{ attendanceDay.date }}</p>
|
||||
</div>
|
||||
<div class="mb-12 grid grid-cols-icon-card gap-x-4 grid-areas-icon-card">
|
||||
<it-icon-location class="w-[60px] grid-in-icon" />
|
||||
<h2 class="text-large font-bold grid-in-title">Standort</h2>
|
||||
<p class="grid-in-value">{{ attendanceDay.location }}</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-icon-card content-between gap-x-4 grid-areas-icon-card">
|
||||
<it-icon-trainer class="w-[60px] grid-in-icon" />
|
||||
<h2 class="text-large font-bold grid-in-title">Trainer</h2>
|
||||
<p class="grid-in-value">{{ attendanceDay.trainer }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { CourseSessionAttendanceDay } from "@/types";
|
||||
|
||||
export interface Props {
|
||||
attendanceDay: CourseSessionAttendanceDay;
|
||||
}
|
||||
defineProps<Props>();
|
||||
</script>
|
||||
|
|
@ -2,13 +2,15 @@
|
|||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import type { LearningContent } from "@/types";
|
||||
import { computed } from "vue";
|
||||
import AttendanceDay from "../attendanceDay/AttendanceDay.vue";
|
||||
import LearningContentSimpleLayout from "../layouts/LearningContentSimpleLayout.vue";
|
||||
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
|
||||
interface Value {
|
||||
export interface Value {
|
||||
description: string;
|
||||
}
|
||||
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
|
||||
const props = defineProps<{
|
||||
value: Value;
|
||||
content: LearningContent;
|
||||
|
|
@ -20,12 +22,21 @@ const attendanceDay = computed(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container-medium">
|
||||
<div class="lg:mt-8">
|
||||
<div class="text-large my-4">
|
||||
<div v-if="attendanceDay">{{ attendanceDay }}</div>
|
||||
<div v-else>Für diese Durchführung existieren noch keine Details</div>
|
||||
<LearningContentSimpleLayout
|
||||
:title="content.title"
|
||||
learning-content-type="attendance_day"
|
||||
>
|
||||
<div class="container-medium">
|
||||
<div class="lg:mt-8">
|
||||
<div class="text-large my-4">
|
||||
<div v-if="attendanceDay">
|
||||
<AttendanceDay :attendance-day="attendanceDay" />
|
||||
</div>
|
||||
<div v-else>
|
||||
Für diese Durchführung {{ content.id }} existieren noch keine Details
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LearningContentSimpleLayout>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
<template>
|
||||
<LearningContentSimpleLayout
|
||||
:subtitle="learningContentTypeData('resource').title"
|
||||
:learning-content-type="'resource'"
|
||||
>
|
||||
<LearningContentSimpleLayout learning-content-type="resource">
|
||||
<div class="h-screen">
|
||||
<iframe width="100%" height="100%" scrolling="no" :src="value.url" />
|
||||
</div>
|
||||
|
|
@ -12,13 +9,12 @@
|
|||
<script setup lang="ts">
|
||||
import LearningContentSimpleLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentSimpleLayout.vue";
|
||||
import type { LearningContent } from "@/types";
|
||||
import { learningContentTypeData } from "@/utils/typeMaps";
|
||||
|
||||
interface Value {
|
||||
export interface Value {
|
||||
url: string;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
defineProps<{
|
||||
value: Value;
|
||||
content: LearningContent;
|
||||
}>();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
<template>
|
||||
<LearningContentSimpleLayout
|
||||
:title="content.title"
|
||||
:subtitle="learningContentTypeData('media_library').title"
|
||||
:learning-content-type="'media_library'"
|
||||
learning-content-type="media_library"
|
||||
>
|
||||
<div class="container-medium">
|
||||
<p class="text-large my-4 lg:my-8">{{ value.description }}</p>
|
||||
|
|
@ -16,16 +15,15 @@
|
|||
<script setup lang="ts">
|
||||
import LearningContentSimpleLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentSimpleLayout.vue";
|
||||
import type { LearningContent } from "@/types";
|
||||
import { learningContentTypeData } from "@/utils/typeMaps";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
interface Value {
|
||||
export interface Value {
|
||||
description: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
defineProps<{
|
||||
value: Value;
|
||||
content: LearningContent;
|
||||
|
|
|
|||
|
|
@ -1,21 +1,19 @@
|
|||
<template>
|
||||
<LearningContentSimpleLayout
|
||||
:title="content.title"
|
||||
:subtitle="learningContentTypeData('placeholder').title"
|
||||
:learning-content-type="'placeholder'"
|
||||
learning-content-type="placeholder"
|
||||
></LearningContentSimpleLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import LearningContentSimpleLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentSimpleLayout.vue";
|
||||
import type { LearningContent } from "@/types";
|
||||
import { learningContentTypeData } from "@/utils/typeMaps";
|
||||
|
||||
interface Value {
|
||||
export interface Value {
|
||||
description: string;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
defineProps<{
|
||||
value: Value;
|
||||
content: LearningContent;
|
||||
}>();
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
<template>
|
||||
<LearningContentSimpleLayout
|
||||
:title="content.title"
|
||||
:subtitle="learningContentTypeData('video').title"
|
||||
:learning-content-type="'video'"
|
||||
>
|
||||
<LearningContentSimpleLayout :title="content.title" learning-content-type="video">
|
||||
<div class="container-medium">
|
||||
<iframe
|
||||
class="mt-8 aspect-video w-full"
|
||||
|
|
@ -20,13 +16,12 @@
|
|||
<script setup lang="ts">
|
||||
import LearningContentSimpleLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentSimpleLayout.vue";
|
||||
import type { LearningContent } from "@/types";
|
||||
import { learningContentTypeData } from "@/utils/typeMaps";
|
||||
|
||||
interface Value {
|
||||
export interface Value {
|
||||
url: string;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
defineProps<{
|
||||
value: Value;
|
||||
content: LearningContent;
|
||||
}>();
|
||||
|
|
|
|||
|
|
@ -4,15 +4,16 @@ import LearningContentFooter from "@/pages/learningPath/learningContentPage/layo
|
|||
import type { LearningContentType } from "@/types";
|
||||
import { learningContentTypeData } from "@/utils/typeMaps";
|
||||
|
||||
interface Props {
|
||||
title: string | undefined;
|
||||
subtitle: string;
|
||||
export interface Props {
|
||||
title: string;
|
||||
learningContentType: LearningContentType;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
title: undefined,
|
||||
title: "",
|
||||
});
|
||||
|
||||
const type = learningContentTypeData(props.learningContentType);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -21,12 +22,9 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
v-if="props.learningContentType !== 'placeholder'"
|
||||
class="flex h-min w-full items-center gap-2 pb-8"
|
||||
>
|
||||
<component
|
||||
:is="learningContentTypeData(props.learningContentType).icon"
|
||||
class="h-6 w-6 text-gray-900"
|
||||
></component>
|
||||
<component :is="type.icon" class="h-6 w-6 text-gray-900"></component>
|
||||
<p class="whitespace-nowrap text-gray-900">
|
||||
{{ props.subtitle }}
|
||||
{{ type.title }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
import type { LearningContentType } from "@/types";
|
||||
import { assertUnreachable } from "@/utils/utils";
|
||||
|
||||
export function learningContentTypeData(t: LearningContentType): {
|
||||
export interface LearningContentIdentifier {
|
||||
title: string;
|
||||
icon: string;
|
||||
} {
|
||||
}
|
||||
|
||||
export function learningContentTypeData(
|
||||
t: LearningContentType
|
||||
): LearningContentIdentifier {
|
||||
switch (t) {
|
||||
case "assignment":
|
||||
return { title: "Transferauftrag", icon: "it-icon-lc-assignment" };
|
||||
|
|
@ -29,7 +33,7 @@ export function learningContentTypeData(t: LearningContentType): {
|
|||
case "feedback":
|
||||
return { title: "Feedback", icon: "it-icon-lc-feedback" };
|
||||
case "attendance_day":
|
||||
return { title: "Feedback", icon: "it-icon-lc-exercise" };
|
||||
return { title: "Präsenztag", icon: "it-icon-lc-training" };
|
||||
case "placeholder":
|
||||
return { title: "In Umsetzung", icon: "it-icon-lc-document" };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,10 +50,12 @@ module.exports = {
|
|||
"fst fst snd snd trd trd fth fth",
|
||||
],
|
||||
"rating-scale-slim": ["bar bar bar", "fst mid fth"],
|
||||
"icon-card": ["icon title", "icon value"],
|
||||
},
|
||||
gridTemplateColumns: {
|
||||
"horizontal-bar-chart": "50px 1fr 300px 4fr 300px 1fr",
|
||||
"horizontal-bar-chart-slim": "50px 1fr 78px 4fr 78px 1fr",
|
||||
"icon-card": "60px auto",
|
||||
},
|
||||
gridTemplateRows: {
|
||||
"horizontal-bar-chart": "200px 40px",
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ THIRD_PARTY_APPS = [
|
|||
"grapple",
|
||||
"graphene_django",
|
||||
"notifications",
|
||||
"django_jsonform",
|
||||
]
|
||||
|
||||
LOCAL_APPS = [
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with python 3.10
|
||||
# To update, run:
|
||||
# This file is autogenerated by pip-compile with Python 3.10
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements-dev.txt requirements-dev.in
|
||||
#
|
||||
|
|
@ -10,8 +10,6 @@ anyascii==0.3.1
|
|||
# via wagtail
|
||||
anyio==3.5.0
|
||||
# via watchfiles
|
||||
appnope==0.1.3
|
||||
# via ipython
|
||||
argon2-cffi==21.3.0
|
||||
# via -r requirements.in
|
||||
argon2-cffi-bindings==21.2.0
|
||||
|
|
@ -106,6 +104,7 @@ django==3.2.13
|
|||
# django-debug-toolbar
|
||||
# django-extensions
|
||||
# django-filter
|
||||
# django-jsonform
|
||||
# django-model-utils
|
||||
# django-modelcluster
|
||||
# django-notifications-hq
|
||||
|
|
@ -140,6 +139,8 @@ django-filter==21.1
|
|||
# via wagtail
|
||||
django-ipware==4.0.2
|
||||
# via -r requirements.in
|
||||
django-jsonform==2.17.0
|
||||
# via -r requirements.in
|
||||
django-model-utils==4.2.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ django-ipware
|
|||
django-csp
|
||||
django-storages
|
||||
django-notifications-hq
|
||||
django-jsonform
|
||||
|
||||
psycopg2-binary
|
||||
gunicorn
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with python 3.10
|
||||
# To update, run:
|
||||
# This file is autogenerated by pip-compile with Python 3.10
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements.txt requirements.in
|
||||
#
|
||||
|
|
@ -62,6 +62,7 @@ django==3.2.13
|
|||
# django-cors-headers
|
||||
# django-csp
|
||||
# django-filter
|
||||
# django-jsonform
|
||||
# django-model-utils
|
||||
# django-modelcluster
|
||||
# django-notifications-hq
|
||||
|
|
@ -87,6 +88,8 @@ django-filter==21.1
|
|||
# via wagtail
|
||||
django-ipware==4.0.2
|
||||
# via -r requirements.in
|
||||
django-jsonform==2.17.0
|
||||
# via -r requirements.in
|
||||
django-model-utils==4.2.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
|
|
|
|||
|
|
@ -8,9 +8,6 @@ from vbv_lernwelt.learnpath.models import Circle
|
|||
|
||||
@admin.register(CourseSession)
|
||||
class CourseSessionAdmin(admin.ModelAdmin):
|
||||
formfield_overrides = {
|
||||
JSONField: {"widget": PrettyJSONWidget(attrs={"rows": 16, "cols": 80})},
|
||||
}
|
||||
date_hierarchy = "created_at"
|
||||
list_display = [
|
||||
"title",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 3.2.13 on 2023-04-25 12:28
|
||||
|
||||
import django_jsonform.models.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("course", "0004_coursesession_assignment_details_list"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="coursesession",
|
||||
name="attendance_days",
|
||||
field=django_jsonform.models.fields.JSONField(),
|
||||
),
|
||||
]
|
||||
|
|
@ -2,6 +2,7 @@ from django.db import models
|
|||
from django.db.models import UniqueConstraint
|
||||
from django.utils.text import slugify
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_jsonform.models.fields import JSONField
|
||||
from wagtail.models import Page
|
||||
|
||||
from vbv_lernwelt.core.model_utils import find_available_slug
|
||||
|
|
@ -178,6 +179,23 @@ class CourseSession(models.Model):
|
|||
Das anhängen kann via CourseSessionUser oder "Schulklasse (TODO)" geschehen
|
||||
"""
|
||||
|
||||
ATTENDANCE_DAYS_SCHEMA = {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"learningContentId": {
|
||||
"type": "number",
|
||||
"title": "ID des Lerninhalts",
|
||||
"required": True,
|
||||
},
|
||||
"date": {"type": "string"},
|
||||
"location": {"type": "string"},
|
||||
"trainer": {"type": "string"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
|
|
@ -187,7 +205,7 @@ class CourseSession(models.Model):
|
|||
start_date = models.DateField(null=True, blank=True)
|
||||
end_date = models.DateField(null=True, blank=True)
|
||||
|
||||
attendance_days = models.JSONField(default=list, blank=True)
|
||||
attendance_days = JSONField(schema=ATTENDANCE_DAYS_SCHEMA, blank=True, default=list)
|
||||
assignment_details_list = models.JSONField(default=list, blank=True)
|
||||
|
||||
additional_json_data = models.JSONField(default=dict, blank=True)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Outline" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
|
||||
<path d="m25.18,4.86H4.82c-1.24,0-2.25,1.01-2.25,2.25v15.77c0,1.24,1.01,2.25,2.25,2.25h20.36c1.24,0,2.25-1.01,2.25-2.25V7.11c0-1.24-1.01-2.25-2.25-2.25Zm-20.36,1.5h20.36c.41,0,.75.34.75.75v2.21H4.07v-2.21c0-.41.34-.75.75-.75Zm20.36,17.27H4.82c-.41,0-.75-.34-.75-.75v-12.06h21.86v12.06c0,.41-.34.75-.75.75Z"/>
|
||||
<rect x="6.17" y="13.25" width="2.67" height="2.67"/>
|
||||
<rect x="11.18" y="13.25" width="2.67" height="2.67"/>
|
||||
<rect x="16.19" y="13.25" width="2.67" height="2.67"/>
|
||||
<rect x="6.17" y="18.26" width="2.67" height="2.67"/>
|
||||
<rect x="11.18" y="18.26" width="2.67" height="2.67"/>
|
||||
<rect x="16.19" y="18.26" width="2.67" height="2.67"/>
|
||||
<rect x="21.2" y="13.25" width="2.67" height="2.67"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 826 B |
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Outline" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
|
||||
<path d="m26.48.94h-12.15c-1.24,0-2.25,1.01-2.25,2.25v5.54H3.52c-1.24,0-2.25,1.01-2.25,2.25v7.83h1.5v-7.83c0-.41.34-.75.75-.75h8.56v5.11c0,1.24,1.01,2.25,2.25,2.25h12.15c1.24,0,2.25-1.01,2.25-2.25V3.19c0-1.24-1.01-2.25-2.25-2.25Zm.75,14.4c0,.41-.34.75-.75.75h-12.15c-.41,0-.75-.34-.75-.75v-5.11h3.08v-1.5h-3.08V3.19c0-.41.34-.75.75-.75h12.15c.41,0,.75.34.75.75v12.15Z"/>
|
||||
<path d="m6.85,7.21c1.33,0,2.41-1.08,2.41-2.41s-1.08-2.41-2.41-2.41-2.41,1.08-2.41,2.41,1.08,2.41,2.41,2.41Zm0-3.31c.5,0,.91.41.91.91s-.41.91-.91.91-.91-.41-.91-.91.41-.91.91-.91Z"/>
|
||||
<path d="m14.48,19.35c-1.33,0-2.41,1.08-2.41,2.41s1.08,2.41,2.41,2.41,2.41-1.08,2.41-2.41-1.08-2.41-2.41-2.41Zm0,3.31c-.5,0-.91-.41-.91-.91s.41-.91.91-.91.91.41.91.91-.41.91-.91.91Z"/>
|
||||
<path d="m20.41,19.35c-1.33,0-2.41,1.08-2.41,2.41s1.08,2.41,2.41,2.41,2.41-1.08,2.41-2.41-1.08-2.41-2.41-2.41Zm0,3.31c-.5,0-.91-.41-.91-.91s.41-.91.91-.91.91.41.91.91-.41.91-.91.91Z"/>
|
||||
<path d="m26.33,19.35c-1.33,0-2.41,1.08-2.41,2.41s1.08,2.41,2.41,2.41,2.41-1.08,2.41-2.41-1.08-2.41-2.41-2.41Zm0,3.31c-.5,0-.91-.41-.91-.91s.41-.91.91-.91.91.41.91.91-.41.91-.91.91Z"/>
|
||||
<rect x="8.3" y="12.26" width="1.5" height="16.06"/>
|
||||
<rect x="4.44" y="19.28" width="1.5" height="9.03"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Outline" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
|
||||
<path d="m15,28.18c-.17,0-.34-.06-.48-.17-4.95-4.06-8.69-11.2-8.69-16.59S9.78,1.82,15,1.82s9.16,4.12,9.16,9.59-3.74,12.53-8.69,16.59c-.14.11-.31.17-.48.17Zm0-24.85c-4.44,0-7.66,3.4-7.66,8.09s3.33,11.21,7.66,15.02c4.33-3.81,7.66-10.29,7.66-15.02s-3.22-8.09-7.66-8.09Z"/>
|
||||
<path d="m15,14.47c-1.92,0-3.47-1.56-3.47-3.47s1.56-3.47,3.47-3.47,3.47,1.56,3.47,3.47-1.56,3.47-3.47,3.47Zm0-5.45c-1.09,0-1.97.88-1.97,1.97s.88,1.97,1.97,1.97,1.97-.88,1.97-1.97-.88-1.97-1.97-1.97Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 595 B |
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Outline" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
|
||||
<path d="m12.51,6.48c-1.33,0-2.41-1.08-2.41-2.41s1.08-2.41,2.41-2.41,2.41,1.08,2.41,2.41-1.08,2.41-2.41,2.41Zm0-3.31c-.5,0-.91.41-.91.91s.41.91.91.91.91-.41.91-.91-.41-.91-.91-.91Z"/>
|
||||
<path d="m8.43,18.08h-1.5v-7.82c0-1.24,1.01-2.25,2.25-2.25h13.15v1.5h-13.15c-.41,0-.75.34-.75.75v7.82Z"/>
|
||||
<rect x="13.96" y="11.53" width="1.5" height="16.06"/>
|
||||
<rect x="10.1" y="18.55" width="1.5" height="9.03"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 525 B |
Loading…
Reference in New Issue