Merged in feature/VBV-331-inhaltstyp-statischer-text (pull request #86)

Inhaltstyp statischer text

Approved-by: Daniel Egger
This commit is contained in:
Elia Bieri 2023-05-17 17:20:45 +00:00 committed by Daniel Egger
commit 4d7b7f82bd
14 changed files with 241 additions and 31 deletions

View File

@ -219,19 +219,18 @@ graphql schema:
python manage.py graphql_schema
```
On the client side you can (or even have to) run the following command to update the
On the client side you have to run the following command to update the
generated code
```bash
npm run codegen
# or in watch mode
npm run codegen:watch
```
💡 If you run `npm run dev`, the codegen command will be run automatically in watch mode."
### Open Questions
- The `id` field has to be a string?
- Is running `codegen` a prerequisite so that it even works?
- What about the generated types from `codegen`? Hand written types seem to be better.
- The functions is `cacheExchange` should be nearer the concrete implementation
- The functions is `cacheExchange` should be nearer the concrete implementation

123
client/package-lock.json generated
View File

@ -62,6 +62,7 @@
"@vue/test-utils": "^2.0.2",
"@vue/tsconfig": "^0.1.3",
"autoprefixer": "^10.4.8",
"concurrently": "^8.0.1",
"eslint": "8.37",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-cypress": "^2.13.3",
@ -9133,6 +9134,48 @@
"safe-buffer": "~5.1.0"
}
},
"node_modules/concurrently": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.0.1.tgz",
"integrity": "sha512-Sh8bGQMEL0TAmAm2meAXMjcASHZa7V0xXQVDBLknCPa9TPtkY9yYs+0cnGGgfdkW0SV1Mlg+hVGfXcoI8d3MJA==",
"dev": true,
"dependencies": {
"chalk": "^4.1.2",
"date-fns": "^2.29.3",
"lodash": "^4.17.21",
"rxjs": "^7.8.0",
"shell-quote": "^1.8.0",
"spawn-command": "0.0.2-1",
"supports-color": "^8.1.1",
"tree-kill": "^1.2.2",
"yargs": "^17.7.1"
},
"bin": {
"conc": "dist/bin/concurrently.js",
"concurrently": "dist/bin/concurrently.js"
},
"engines": {
"node": "^14.13.0 || >=16.0.0"
},
"funding": {
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
}
},
"node_modules/concurrently/node_modules/supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/config-chain": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
@ -10025,6 +10068,22 @@
"integrity": "sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==",
"dev": true
},
"node_modules/date-fns": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
"integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.21.0"
},
"engines": {
"node": ">=0.11"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/date-fns"
}
},
"node_modules/dayjs": {
"version": "1.11.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
@ -17315,6 +17374,12 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/spawn-command": {
"version": "0.0.2-1",
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
"integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==",
"dev": true
},
"node_modules/spdx-correct": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
@ -18240,6 +18305,15 @@
"node": ">=14"
}
},
"node_modules/tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
"dev": true,
"bin": {
"tree-kill": "cli.js"
}
},
"node_modules/trough": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz",
@ -26587,6 +26661,34 @@
}
}
},
"concurrently": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.0.1.tgz",
"integrity": "sha512-Sh8bGQMEL0TAmAm2meAXMjcASHZa7V0xXQVDBLknCPa9TPtkY9yYs+0cnGGgfdkW0SV1Mlg+hVGfXcoI8d3MJA==",
"dev": true,
"requires": {
"chalk": "^4.1.2",
"date-fns": "^2.29.3",
"lodash": "^4.17.21",
"rxjs": "^7.8.0",
"shell-quote": "^1.8.0",
"spawn-command": "0.0.2-1",
"supports-color": "^8.1.1",
"tree-kill": "^1.2.2",
"yargs": "^17.7.1"
},
"dependencies": {
"supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"config-chain": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
@ -27241,6 +27343,15 @@
"integrity": "sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==",
"dev": true
},
"date-fns": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
"integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
"dev": true,
"requires": {
"@babel/runtime": "^7.21.0"
}
},
"dayjs": {
"version": "1.11.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
@ -32729,6 +32840,12 @@
"integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==",
"dev": true
},
"spawn-command": {
"version": "0.0.2-1",
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
"integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==",
"dev": true
},
"spdx-correct": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
@ -33448,6 +33565,12 @@
"punycode": "^2.3.0"
}
},
"tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
"dev": true
},
"trough": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz",

View File

@ -9,7 +9,7 @@
"codegen:watch": "graphql-codegen --watch",
"coverage": "vitest run --coverage",
"cypress:open": "cypress open",
"dev": "vite",
"dev": "concurrently \"vite\" \"npm run codegen:watch\"",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"prettier": "prettier . --write",
"prettier:check": "prettier . --check",
@ -74,6 +74,7 @@
"@vue/test-utils": "^2.0.2",
"@vue/tsconfig": "^0.1.3",
"autoprefixer": "^10.4.8",
"concurrently": "^8.0.1",
"eslint": "8.37",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-cypress": "^2.13.3",

View File

@ -12,7 +12,7 @@ import FeedbackBlock from "./blocks/FeedbackBlock.vue";
import IframeBlock from "./blocks/IframeBlock.vue";
import MediaLibraryBlock from "./blocks/MediaLibraryBlock.vue";
import PlaceholderBlock from "./blocks/PlaceholderBlock.vue";
import DescriptionBlock from "./blocks/RichTextBlock.vue";
import RichTextBlock from "./blocks/RichTextBlock.vue";
import VideoBlock from "./blocks/VideoBlock.vue";
log.debug("LearningContent.vue setup");
@ -31,11 +31,11 @@ const COMPONENTS: Record<LearningContentType, Component> = {
"learnpath.LearningContentLearningModule": IframeBlock,
"learnpath.LearningContentMediaLibrary": MediaLibraryBlock,
"learnpath.LearningContentPlaceholder": PlaceholderBlock,
"learnpath.LearningContentRichText": DescriptionBlock,
"learnpath.LearningContentRichText": RichTextBlock,
"learnpath.LearningContentTest": IframeBlock,
"learnpath.LearningContentVideo": VideoBlock,
};
const DEFAULT_BLOCK = DescriptionBlock;
const DEFAULT_BLOCK = PlaceholderBlock;
const component = computed(() => {
return COMPONENTS[props.learningContent.content_type] || DEFAULT_BLOCK;

View File

@ -11,5 +11,14 @@ const props = defineProps<{
<LearningContentSimpleLayout
:title="props.content.title"
:learning-content-type="props.content.content_type"
></LearningContentSimpleLayout>
>
<!-- eslint-disable vue/no-v-html -->
<div class="container-medium">
<p
v-if="props.content.description"
class="default-wagtail-rich-text my-4"
v-html="props.content.description"
></p>
</div>
</LearningContentSimpleLayout>
</template>

View File

@ -1,15 +1,30 @@
<template>
<div class="container-medium">
<div class="lg:mt-8">
<p class="text-large my-4">{{ props.content.description }}}</p>
</div>
</div>
</template>
<script setup lang="ts">
import type { LearningContentInterface } from "@/types";
import LearningContentSimpleLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentSimpleLayout.vue";
import type { LearningContentRichText } from "@/types";
const props = defineProps<{
content: LearningContentInterface;
content: LearningContentRichText;
}>();
</script>
<template>
<LearningContentSimpleLayout
:title="props.content.title"
:learning-content-type="props.content.content_type"
>
<!-- eslint-disable vue/no-v-html -->
<div class="container-medium">
<p
v-if="props.content.description"
class="default-wagtail-rich-text my-4"
v-html="props.content.description"
></p>
<div
v-if="props.content.text"
class="default-wagtail-rich-text my-4"
v-html="props.content.text"
></div>
</div>
</LearningContentSimpleLayout>
</template>

View File

@ -72,6 +72,7 @@ export interface LearningContentPlaceholder extends LearningContentInterface {
export interface LearningContentRichText extends LearningContentInterface {
readonly content_type: "learnpath.LearningContentRichText";
text: string;
}
export interface LearningContentTest extends LearningContentInterface {

View File

@ -23,7 +23,7 @@ export function learningContentTypeData(
case "learnpath.LearningContentTest":
return { title: "Test", icon: "it-icon-lc-test" };
case "learnpath.LearningContentRichText":
return { title: "Reflexion", icon: "it-icon-lc-resource" };
return { title: "Text", icon: "it-icon-lc-resource" };
case "learnpath.LearningContentFeedback":
return { title: "Feedback", icon: "it-icon-lc-feedback" };
case "learnpath.LearningContentPlaceholder":

View File

@ -27,11 +27,24 @@ body {
hyphens: auto;
}
.default-wagtail-rich-text h3 {
margin-bottom: 1em;
}
.default-wagtail-rich-text p {
margin-bottom: 0.5em;
}
.default-wagtail-rich-text ul {
list-style-type: disc;
margin-left: 24px;
}
.default-wagtail-rich-text ol {
list-style-type: decimal;
margin-left: 24px;
}
svg {
@apply fill-current;
}

View File

@ -3,6 +3,12 @@ DEFAULT_RICH_TEXT_FEATURES = [
"bold",
"italic",
]
DEFAULT_RICH_TEXT_FEATURES_WITH_HEADER = [
"ul",
"bold",
"italic",
"h3",
]
# ids for cypress test data
ADMIN_USER_ID = -1

View File

@ -4,6 +4,7 @@ import wagtail_factories
from django.conf import settings
from slugify import slugify
from wagtail.models import Site
from wagtail.rich_text import RichText
from vbv_lernwelt.assignment.creators.create_assignments import create_test_assignment
from vbv_lernwelt.assignment.models import Assignment
@ -38,6 +39,7 @@ from vbv_lernwelt.learnpath.tests.learning_path_factories import (
LearningContentLearningModuleFactory,
LearningContentMediaLibraryFactory,
LearningContentPlaceholderFactory,
LearningContentRichTextFactory,
LearningContentVideoFactory,
LearningPathFactory,
LearningSequenceFactory,
@ -198,9 +200,21 @@ damit du erfolgreich mit deinem Lernpfad (durch-)starten kannst.
title="Vorbereitung", parent=circle, icon="it-icon-ls-start"
)
lu = LearningUnitFactory(title="Vorbereitung", parent=circle)
LearningContentPlaceholderFactory(
LearningContentRichTextFactory(
title="Verschaffe dir einen Überblick",
parent=circle,
text=RichText(
"""
<h3>Arbeitsblätter «Vorbereitungsauftrag»</h3>
<p>Handlungskompetenz d2: Informations-und Beratungsgespräch mit Kunden oder Lieferanten führen</p>
<p>Arbeitssituation 4: Kunden beraten und dazugehörige Prozesse abwickeln</p>
<p>Die Kaufleute führen Bedarfserhebungen für Kunden durch und schlagen ihnen angemessene Versicherungslösungen vor. Sie führen Beratungs-und Verkaufsgespräche und erteilen Auskünfte. Sieführen Kundenaufträge aus und behandeln Beschwerden. Sie formulieren Aufträge an relevante Anspruchsgruppen und unterstützen den Aussendient in verkaufsrelevanten Belangen.</p>
<ul>
<li>d2.pv.ük3: Sie erläutern die Leistungen und Produkte im Versicherungsbereich. (K2)</li>
<li>d2pv.ük4: Sie erläutern die Prozesse und Abläufe im privaten Versicherungsbereich. (K2)</li>
</ul>
"""
),
)
LearningContentMediaLibraryFactory(
title=f"Mediathek {title}",

View File

@ -0,0 +1,19 @@
# Generated by Django 3.2.13 on 2023-05-17 09:00
import wagtail.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("learnpath", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="learningcontentrichtext",
name="text",
field=wagtail.fields.RichTextField(blank=True),
),
]

View File

@ -6,6 +6,7 @@ from wagtail.admin.panels import FieldPanel, PageChooserPanel
from wagtail.fields import RichTextField
from wagtail.models import Page
from vbv_lernwelt.core.constants import DEFAULT_RICH_TEXT_FEATURES_WITH_HEADER
from vbv_lernwelt.core.model_utils import find_available_slug
from vbv_lernwelt.course.models import CourseBasePage, CoursePage
@ -306,8 +307,16 @@ class LearningContentTest(LearningContent):
class LearningContentRichText(LearningContent):
text = RichTextField(blank=True, features=DEFAULT_RICH_TEXT_FEATURES_WITH_HEADER)
parent_page_types = ["learnpath.Circle"]
serialize_field_names = LearningContent.serialize_field_names + [
"text",
]
subpage_types = []
content_panels = LearningContent.content_panels + [
FieldPanel("text", classname="Text"),
]
class LearningContentAssignment(LearningContent):

View File

@ -1,4 +1,5 @@
import wagtail_factories
from wagtail.rich_text import RichText
from vbv_lernwelt.learnpath.models import (
Circle,
@ -86,7 +87,7 @@ class LearningUnitFactory(wagtail_factories.PageFactory):
class LearningContentAttendanceDayFactory(wagtail_factories.PageFactory):
title = "Platzhalter Inhalt"
minutes = 15
description = "Platzhalter Beschreibung"
description = RichText("Platzhalter Beschreibung")
content_url = ""
class Meta:
@ -97,7 +98,7 @@ class LearningContentVideoFactory(wagtail_factories.PageFactory):
title = "Platzhalter Video"
minutes = 15
content_url = "https://www.youtube.com/embed/qhPIfxS2hvI"
description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
description = RichText("Lorem ipsum dolor sit amet, consectetur adipiscing elit.")
class Meta:
model = LearningContentVideo
@ -107,7 +108,7 @@ class LearningContentPlaceholderFactory(wagtail_factories.PageFactory):
title = "Platzhalter Video"
minutes = 15
content_url = ""
description = "Platzhalter"
description = RichText("Platzhalter")
class Meta:
model = LearningContentPlaceholder
@ -117,7 +118,7 @@ class LearningContentFeedbackFactory(wagtail_factories.PageFactory):
title = "Feedback"
minutes = 15
content_url = ""
description = ""
description = RichText("")
class Meta:
model = LearningContentFeedback
@ -127,7 +128,7 @@ class LearningContentLearningModuleFactory(wagtail_factories.PageFactory):
title = "Beispiel Lernmodul"
minutes = 15
content_url = ""
description = ""
description = RichText("")
class Meta:
model = LearningContentLearningModule
@ -137,7 +138,7 @@ class LearningContentMediaLibraryFactory(wagtail_factories.PageFactory):
title = "Mediathek"
minutes = 15
content_url = ""
description = ""
description = RichText("")
class Meta:
model = LearningContentMediaLibrary
@ -147,7 +148,7 @@ class LearningContentTestFactory(wagtail_factories.PageFactory):
title = "Fachcheck"
minutes = 15
content_url = ""
description = ""
description = RichText("")
class Meta:
model = LearningContentTest
@ -157,7 +158,7 @@ class LearningContentRichTextFactory(wagtail_factories.PageFactory):
title = "Rich Text"
minutes = 15
content_url = ""
description = ""
description = RichText("")
class Meta:
model = LearningContentRichText
@ -167,7 +168,7 @@ class LearningContentAssignmentFactory(wagtail_factories.PageFactory):
title = "Geleitete Fallarbeit"
minutes = 15
content_url = ""
description = ""
description = RichText("")
class Meta:
model = LearningContentAssignment